JNI çağrılarını yavaşlatan nedir?


194

Java'da JNI çağrısı yaparken 'sınırları aşmanın' yavaş olduğunu biliyorum.

Ancak yavaşlatan ne olduğunu bilmek istiyorum. Altta yatan jvm uygulaması, onu yavaşlatan bir JNI çağrısı yaparken ne yapar?


2
(+1) Güzel soru. Konuya girerken, gerçek ölçütler yapan herkesi bulgularını yayınlamaya teşvik etmek istiyorum.
NPE

2
Bir JNI çağrısının, aktarılan Java nesnelerini C (örneğin) anlayabileceği bir şeye dönüştürmesi gerekir; dönüş değeri ile aynı. Tür dönüştürme ve çağrı yığını marshalling iyi bir yığın.
Dave Newton

Dave, bunu daha önce anladım ve duymuştum. Peki dönüşüm tam olarak nasıl? bu 'bir şey' nedir? Detaylar arıyorum.
pdeva

Java ve C arasında veri iletmek için doğrudan ByteBuffers kullanılması nispeten düşük ek yüke neden olabilir.
Peter Lawrey

6
çağrı uygun bir C yığın çerçevesine ihtiyaç duyar, tüm kullanışlı CPU kayıtlarını iter (ve onları geri patlatır), çağrı eskrim gerektirir ve ayrıca satır içi gibi birçok optimizasyonu önler. Ayrıca iş parçacıkları yürütme yığını kilidini terk etmelidir (örneğin, yerel koddayken önyargı kilitlerinin çalışmasına izin vermek için) ve sonra geri almalıdır.
bestsss

Yanıtlar:


174

İlk olarak, "yavaş" ile onlarca nanosaniye alabilecek bir şeyden bahsettiğimizi belirtmek gerekir. Önemsiz yerel yöntemler için, 2010 yılında Windows masaüstümde ortalama 40 ns ve Mac masaüstümde 11 ns çağrıları ölçtüm. Çok fazla çağrı yapmadığınız sürece fark etmeyeceksiniz.

Bununla birlikte, yerel bir yöntemi çağırmak normal bir Java yöntemi çağrısı yapmaktan daha yavaş olabilir . Nedenleri şunları içerir:

  • Yerel yöntemler JVM tarafından satır içine alınmaz. Bu makine için tam zamanında derlenmeyecekler - zaten derlenmişler.
  • Bir Java dizisi yerel kodda erişim için kopyalanabilir ve daha sonra geri kopyalanabilir. Maliyet, dizinin boyutunda doğrusal olabilir. 100.000 dizinin JNI kopyalamasını Windows masaüstümde ortalama 75 mikrosaniye ve Mac'te 82 mikrosaniye olarak ölçtüm. Neyse ki, doğrudan erişim GetPrimitiveArrayCritical veya NewDirectByteBuffer aracılığıyla elde edilebilir .
  • Yöntem bir nesneden geçtiyse veya geri arama yapması gerekiyorsa, yerel yöntem büyük olasılıkla JVM'ye kendi çağrılarını yapacaktır. Java alanlarına, yöntemlerine ve türlerine yerel koddan erişmek yansımaya benzer bir şey gerektirir. İmzalar dizelerde belirtilir ve JVM'den sorgulanır. Bu yavaş hem ve hataya yatkın.
  • Java Dizeleri nesnelerdir, uzunlukları vardır ve kodlanmıştır. Bir dizeye erişmek veya dizeyi oluşturmak için O (n) kopyası gerekebilir.

Muhtemelen tarihli bazı ek tartışmalar "9.2: JNI maliyetlerinin incelenmesi" bölümünde Steve Wilson ve Jeff Kesselman tarafından "Java® Platform Performansı: Stratejiler ve Taktikler", 2000'de bulunabilir. Aşağıdaki @Philip tarafından yapılan yorumda bu sayfanın aşağıya üçte biri kadar .

2009 IBM developerWorks makalesi "Java Native Interface'i kullanmak için en iyi uygulamalar" , JNI ile performans tuzaklarından kaçınmak için bazı öneriler sunar.


1
Bu yanıt , bazı yerel kodların JVM tarafından satır içine alınabileceğini iddia ediyor .
AH

5
Bu yanıt, bazı standart yerel kodların JNI kullanmak yerine JVM'de satır içine alındığını not eder. Yukarıda, "doğal yöntemler", JNI yoluyla uygulanan genel kullanıcı tanımlı yerel yöntemler örneğidir. Sun.misc.Unsafe işaretçisi için teşekkürler.
Andy Thomas

Bu yaklaşımın her JNI çağrısı için kullanılabileceğini iddia etmek istemedim. Ama orada olduğunu bilmek zarar vermez ise saf bytecode ve saf JNI'yı kodu arasında bazı orta yol. Belki de bu bazı tasarım kararlarını etkileyecektir. Belki de bu mekanizma gelecekte genelleştirilir.
AH

3
@AH, JNI ile içsel hata yapıyorsun. Bunlar oldukça farklı. sun.misc.Unsafeve diğer pek çok şey System.currentTimeMillis/nanoTimeJVM tarafından 'sihir' ile ele alınır. Onlar JNI değildir ve JVM impl, kendisini barikat, uygun .c / .h dosyaları hiç yok. JVM'yi yazma / hacklemediğiniz sürece yaklaşım takip edilemez.
bestsss

1
" Bu java.sun.com belgesi " şu anda bozuk - işte çalışan bir bağlantı.
Philip Guin

25

İşaretli tüm Java yöntemlerinin native"yavaş" olmadığını belirtmek gerekir . Bunlardan bazıları şunlardır intrinsics son derece hızlı onları yapar. İçsel ve hangilerinin ise kötü, arayıp bulabileceğiniz hangilerinin kontrol etmek do_intrinsicde vmSymbols.hpp .


23

Temel olarak JVM, her JNI çağrısı için C parametrelerini yorumlamalı olarak oluşturur ve kod optimize edilmez.

Bu makalede ana hatlarıyla belirtilen daha fazla ayrıntı var

Kıyaslama JNI vs yerel kod ile ilgileniyorsanız, bu proje karşılaştırmalı değerlendirme kodu vardır.


2
bağlandığınız kağıt, JNI'nın dahili olarak nasıl çalıştığını açıklayandan daha fazla bir performans ölçütü kağıdına benziyor.
pdeva

@pdeva Maalesef bulduğum diğer kaynaklar java.sun.com ile bağlantılıydı ve bağlantılar Oracle satın alımından bu yana güncellenmedi. JNI dahili hakkında daha fazla bilgi arıyorum.
dmck

13
Makale Java 1.3 hakkında - çok uzun zaman önce. O zamanki sorunlar Java 7 için hala geçerli mi?
AH
Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.