Java'da nasıl doğru bir mikro kıyaslama yazabilirim?


870

Java'da doğru bir mikro karşılaştırmayı nasıl yazıyorsunuz (ve çalıştırıyorsunuz)?

Bazı kod örnekleri ve düşünmek için çeşitli şeyler gösteren yorumlar arıyorum.

Örnek: Kıyaslama zamanı / yinelemeyi veya yinelemeleri / zamanı ölçmeli ve neden?

İlgili: Kronometre kıyaslaması kabul edilebilir mi?


İlgili bazı bilgiler için birkaç dakika önce [bu soru] [1] 'e bakın. edit: üzgünüm, bu bir cevap olması gerekiyordu. Yorum olarak göndermeliydim. [1]: stackoverflow.com/questions/503877/…
Tiago

Bu sorunun posterini böyle bir soruya göndermeyi planladıktan sonra, bu sorunun mevcut olmadığını belirttim. İşte burada, umarım zaman içinde bazı iyi ipuçları toplar.
John Nilsson

5
Java 9 mikro kıyaslama için bazı özellikler sağlayabilir: openjdk.java.net/jeps/230
Raedwald

1
@Raedwald JEP'in JDK koduna bazı mikro ölçütler eklemeyi amaçladığını düşünüyorum, ancak
jmh'nin JDK'ya

1
@Raedwald Gelecekten merhaba. Kesimi yapmadı .
Michael

Yanıtlar:


787

Java HotSpot'un içerik oluşturucularından mikro ölçütler yazmayla ilgili ipuçları :

Kural 0: JVM'ler ve mikro kıyaslama hakkında saygın bir makale okuyun. İyi olan Brian Goetz, 2005 . Mikro kriterlerden çok fazla beklemeyin; yalnızca sınırlı bir JVM performans özellikleri aralığı ölçer.

Kural 1: Her zaman zamanlama aşama (lar) ından önce tüm başlatma ve derlemeleri tetikleyecek kadar, test çekirdeğinizi tam olarak çalıştıran bir ısınma aşaması ekleyin. (Isınma aşamasında daha az yineleme tamamdır. Temel kural onbinlerce iç döngü yinelemesidir.)

Kural 2: Her zaman çalıştırmak -XX:+PrintCompilation, -verbose:gcderleyici ve JVM diğer bölgelerinde zamanlama aşamasında beklenmedik işi olmadığını doğrulamak böylece, vb.

Kural 2.1: Zamanlama ve ısınma aşamalarının başında ve sonunda mesajları yazdırın, böylece zamanlama aşamasında Kural 2'den çıktı olmadığını doğrulayabilirsiniz.

Kural 3:-client ve -serverve OSR ile düzenli derlemeler arasındaki farkın farkında olun . -XX:+PrintCompilationBayrak, örneğin, non-ilk giriş noktası belirten bir de işareti ile OSR derlemeler rapor eder: Trouble$1::run @ 2 (41 bytes). En iyi performansın peşindeyseniz sunucuyu istemciye ve OSR'ye düzenli olarak tercih edin.

Kural 4: Başlatma etkilerinin farkında olun. Zamanlama aşaması sırasında ilk kez yazdırmayın, çünkü baskı yüklenir ve sınıfları başlatır. Sınıf yüklemesini özel olarak test etmediğiniz sürece (ve bu durumda yalnızca test sınıflarını yükleyin), ısınma aşamasının (veya son raporlama aşamasının) dışında yeni sınıflar yüklemeyin. Kural 2, bu tür etkilere karşı ilk savunma hattınızdır.

Kural 5: Deoptimizasyon ve yeniden derleme etkilerinin farkında olun. Zamanlayıcı aşamasında ilk kez herhangi bir kod yolu almayın, çünkü derleyici, yolun hiç kullanılmayacağı yönündeki önceki iyimser bir varsayım temelinde kodu önemsiz hale getirebilir ve yeniden derleyebilir. Kural 2, bu tür etkilere karşı ilk savunma hattınızdır.

Kural 6: Derleyicinin zihnini okumak için uygun araçları kullanın ve ürettiği koddan şaşıracağınızı düşünün. Bir şeyi daha hızlı veya daha yavaş yapan şeylerle ilgili teoriler oluşturmadan önce kodu kendiniz inceleyin.

Kural 7: Ölçümlerinizdeki gürültüyü azaltın. Karşılaştırmanızı sessiz bir makinede çalıştırın ve aykırı değerleri atarak birkaç kez çalıştırın. -XbatchDerleyiciyi uygulama ile serileştirmek için kullanın ve derleyicinin -XX:CICompilerCount=1kendisine paralel çalışmasını önlemek için ayar yapmayı düşünün . GC yükünü azaltmak, Xmx(yeterince büyük) eşitlerini ayarlamak Xmsve UseEpsilonGCvarsa kullanın .

Kural 8: Karşılaştırmanız için bir kütüphane kullanın, çünkü muhtemelen daha verimlidir ve bu tek amaç için zaten hata ayıklanmıştır. Gibi JMH , Kalınlık veya Bill ve Java için Paul Mükemmel UCSD Deneyler .


5
Bu da ilginç bir makaleydi
John Nilsson

142
Ayrıca, çoğu OS + JVM kombinasyonunda tipik olan + veya - 15 ms doğrulukla Tamam değilseniz, asla System.currentTimeMillis () öğesini kullanmayın. Bunun yerine System.nanoTime () öğesini kullanın.
Scott Carey


93
Daha doğru olduğu garantiSystem.nanoTime() edilmediğine dikkat edilmelidir . Sadece en azından doğru olduğu garanti edilir. Bununla birlikte, genellikle önemli ölçüde daha doğrudur. System.currentTimeMillis()
Yerçekimi

41
Birinin System.nanoTime()yerine kullanması gereken temel neden System.currentTimeMillis(), birincisinin monoton olarak artmasının garanti edilmesidir. Döndürülen değerlerin çıkarılması currentTimeMillis, muhtemelen sistem zamanı bazı NTP arka plan programı tarafından ayarlandığından, olumsuz sonuçlar verebilir.
Waldheinz

239

Bu sorunun cevap olarak işaretlendiğini biliyorum ancak mikro ölçütler yazmamıza yardımcı olan iki kütüphaneden bahsetmek istedim

Google'dan Kaliper

Başlangıç ​​eğiticileri

  1. http://codingjunkie.net/micro-benchmarking-with-caliper/
  2. http://vertexlabs.co.uk/blog/caliper

JMH yönüne OpenJDK

Başlangıç ​​eğiticileri

  1. JVM'de Karşılaştırma Tuzaklarından Kaçınma
  2. http://nitschinger.at/Using-JMH-for-Java-Microbenchmarking
  3. http://java-performance.info/jmh/

37
+1, kabul edilen yanıtın Kural 8'i olarak eklenmiş olabilir: Kural 8: çünkü pek çok şey ters gidebilir, muhtemelen kendiniz yapmaya çalışmak yerine mevcut bir kütüphaneyi kullanmalısınız!
assylias

8
@Pangea jmh bugünlerde muhtemelen Kaliper'dan daha üstündür, Ayrıca bakınız: groups.google.com/forum/#!msg/mechanical-sympathy/m4opvy4xq3U/…
assylias

86

Java karşılaştırmaları için önemli şeyler:

  • Kod birkaç kez çalıştırarak ilk JIT Isınma uğramadan önce onu
  • Sonuçları saniye veya (daha iyi) on saniye olarak ölçebilecek kadar uzun süre çalıştırdığınızdan emin olun
  • System.gc()Yinelemeler arasında arama yapamasanız da, testler arasında çalıştırmak iyi bir fikirdir, böylece her testin çalışmak için "temiz" bir bellek alanı elde etmesi umulur. (Evet, gc()bir garantiden daha fazla bir ipucu, ancak benim deneyimimde gerçekten çöp toplaması muhtemel .)
  • Yinelemeleri ve zamanı ve "en iyi" algoritmanın 1,0 puan alacağı ve diğerlerinin göreceli olarak puanlanacağı şekilde ölçeklendirilebilen bir zaman / yineleme puanı görüntülemeyi severim. Bu, tüm algoritmaları uzun bir süre çalıştırabileceğiniz , hem yineleme sayısını hem de zamanı değiştirebileceğiniz, ancak yine de karşılaştırılabilir sonuçlar elde edebileceğiniz anlamına gelir .

Sadece .NET'te bir kıyaslama çerçevesinin tasarımı hakkında bloglama sürecindeyim. Bir var bir çift arasında önceki mesajların size bazı fikirler verebilir mümkün olabilir - her şey tabii ki uygun olacaktır, ama bir kısmı olabilir.


3
Küçük nitpick: IMO "böylece her testin" olması "gerekir, böylece her testin elde edilmesi gerekir, çünkü birincisi çağrının gc her zaman kullanılmayan belleği serbest bıraktığı izlenimini verir .
Sanjay T. Sharma

@ SanjayT.Sharma: Niyet , aslında öyle. Kesinlikle garanti edilmese de, aslında oldukça güçlü bir ipucu. Daha net olması için düzenleyecek.
Jon Skeet

1
System.gc () öğesini çağırmayı kabul etmiyorum. Bu bir ipucu, hepsi bu. "Umarım bir şeyler yapar" bile. Asla dememelisin. Bu programlama, sanat değil.
gyorgyabraham

13
@gyabraham: Evet, bu bir ipucu - ama genellikle alındığını gözlemlediğim bir ipucu. Kullanmayı System.gc()sevmiyorsanız, önceki testlerde oluşturulan nesneler nedeniyle bir testte çöp toplamayı en aza indirmeyi nasıl öneriyorsunuz? Ben pragmatikim, dogmatik değil.
Jon Skeet

9
@gyabraham: "Büyük düşüş" ile ne demek istediğini bilmiyorum. Daha ayrıntılı bir şekilde açıklayabilir misiniz - daha iyi sonuçlar vermek için bir teklifiniz var mı? Açıkça bunun bir garanti olmadığını söyledim ...
Jon Skeet

48

jmh , OpenJDK'ya yeni bir ektir ve Oracle'ın bazı performans mühendisleri tarafından yazılmıştır. Kesinlikle bir göz atmaya değer.

Jmh, Java ve JVM'yi hedefleyen diğer dillerde yazılmış nano / mikro / makro ölçütlerini oluşturmak, çalıştırmak ve analiz etmek için kullanılan bir Java koşum takımıdır.

Çok ilginç bilgiler örnek testleri yorumlarına gömüldü .

Ayrıca bakınız:


1
Ayrıca bu blog yayınına bakın: JMH'yi kullanmaya başlama ile ilgili ayrıntılar için psy-lob-saw.blogspot.com/2013/04/… .
Nitsan Wakart


23

Kıyaslama zamanı / yinelemeyi veya yinelemeleri / zamanı ölçmeli ve neden?

Neyi test etmeye çalıştığınıza bağlıdır .

Gecikme ile ilgileniyorsanız , zaman / yineleme kullanın ve verim ile ilgileniyorsanız , yineleme / saat kullanın.


16

İki algoritmayı karşılaştırmaya çalışıyorsanız, her biri için sırayı değiştirerek en az iki karşılaştırma ölçütü yapın. yani:

for(i=1..n)
  alg1();
for(i=1..n)
  alg2();
for(i=1..n)
  alg2();
for(i=1..n)
  alg1();

Farklı algoritmalarda aynı algoritmanın çalışma zamanında bazı farklar (bazen% 5-10) buldum.

Ayrıca, n'nin çok büyük olduğundan emin olun , böylece her döngünün çalışma süresi en az 10 saniye olur. Yineleme sayısı arttıkça, karşılaştırma sürenizdeki rakamlar da o kadar artar ve veriler o kadar güvenilirdir.


5
Doğal olarak düzeni değiştirmek çalışma zamanını etkiler. JVM optimizasyonları ve önbellek efektleri burada çalışacaktır. Daha iyisi JVM optimizasyonunu 'ısıtmak', birden fazla çalışma yapmak ve her testi farklı bir JVM'de karşılaştırmaktır.
Mnementh

15

Kıyaslanmış kodda hesaplanan sonuçları bir şekilde kullandığınızdan emin olun. Aksi takdirde kodunuz optimize edilebilir.


13

Java'da mikro ölçütler yazmak için birçok olası tuzak vardır.

İlk olarak, daha fazla veya daha az zaman alan rastgele her türlü olayla hesaplamak zorundasınız: Çöp toplama, önbellek efektleri (dosyalar için işletim sistemi ve bellek için CPU), IO vb.

İkincisi: Çok kısa aralıklarla ölçülen sürelerin doğruluğuna güvenemezsiniz.

Üçüncü olarak: JVM yürütürken kodunuzu optimize eder. Böylece aynı JVM örneğindeki farklı çalışmalar daha hızlı ve daha hızlı hale gelecektir.

Önerilerim: Karşılaştırmanızı birkaç saniye çalıştırın, bu milisaniye boyunca bir çalışma süresinden daha güvenilirdir. JVM'yi ısıtın (JVM'nin optimizasyonları çalıştırabilmesi için ölçütleri en az bir kez ölçmeden çalıştırmak anlamına gelir). Ve karşılaştırmalı değerlendirmenizi birkaç kez (belki 5 kez) çalıştırın ve medyan değerini alın. Her mikro ölçütü yeni bir JVM örneğinde çalıştırın (her yeni ölçüt yeni Java'yı arayın) aksi takdirde JVM'nin optimizasyon etkileri daha sonra çalışan testleri etkileyebilir. Isınma aşamasında yürütülmeyen şeyleri çalıştırmayın (çünkü bu, sınıf yükünü ve yeniden derlemeyi tetikleyebilir).


8

Ayrıca, farklı uygulamaları karşılaştırırken mikro karşılaştırmalı değerlendirmenin sonuçlarını analiz etmenin de önemli olabileceği unutulmamalıdır. Bu nedenle bir önem testi yapılmalıdır.

Bunun nedeni A, kıyaslamanın çoğu çalışması sırasında uygulamanın uygulamadan daha hızlı olabilmesidir B. Ancak Adaha yüksek bir yayılım da olabilir, bu nedenle ölçülen performans avantajı Aile karşılaştırıldığında önemli olmayacaktır B.

Bu nedenle mikro ölçütleri doğru bir şekilde yazmak ve çalıştırmak, aynı zamanda doğru bir şekilde analiz etmek de önemlidir.


8

Diğer mükemmel tavsiyelere eklemek için aşağıdakilere de dikkat etmeliyim:

Bazı CPU'lar için (örn. TurboBoost ile Intel Core i5 serisi), sıcaklık (ve kullanılmakta olan çekirdek sayısı ve kullanım yüzdesi) saat hızını etkiler. CPU'lar dinamik olarak saatli olduğundan, sonuçlarınızı etkileyebilir. Örneğin, tek iş parçacıklı bir uygulamanız varsa, maksimum saat hızı (TurboBoost ile) tüm çekirdekleri kullanan bir uygulamadan daha yüksektir. Bu nedenle bu, bazı sistemlerde tek ve çok iş parçacıklı performansın karşılaştırılmasına müdahale edebilir. Sıcaklığın ve volatajların Turbo frekansının ne kadar süreyle korunduğunu da unutmayın.

Belki de üzerinde doğrudan kontrol sahibi olmanızın daha önemli bir yönü var: doğru olanı ölçtüğünüzden emin olun! Örneğin, System.nanoTime()belirli bir kod parçasını karşılaştırmak için, ilgilenmediğiniz şeyleri ölçmekten kaçınmak için anlamlı olan yerlere atamaya çağrı yapın. Örneğin, şunları yapma:

long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");

Sorun, kodun bittiği anı hemen bitirmemenizdir. Bunun yerine aşağıdakileri deneyin:

final long endTime, startTime = System.nanoTime();
//code here...
endTime = System.nanoTime();
System.out.println("Code took "+(endTime-startTime)+"nano seconds");

Evet, zamanlanmış bölgede ilgisiz işler yapmamanız önemlidir, ancak ilk örneğiniz hala iyidir. printlnAyrı bir başlık satırı veya başka bir şey değil, yalnızca bir çağrı System.nanoTime()vardır ve bu çağrı için dize bağımsız değişkenini oluşturmanın ilk adımı olarak değerlendirilmelidir . Bir derleyicinin ikincisiyle yapamayacakları ilk şeyle yapamayacağı hiçbir şey yoktur ve hiçbiri bir durma zamanını kaydetmeden önce ekstra iş yapmaya teşvik etmez.
Peter Cordes

7

http://opt.sourceforge.net/ Java Micro Benchmark - bilgisayar sisteminin farklı platformlarda karşılaştırmalı performans özelliklerini belirlemek için gereken kontrol görevleri. Optimizasyon kararlarını yönlendirmek ve farklı Java uygulamalarını karşılaştırmak için kullanılabilir.


2
Keyfi bir Java kodu değil, sadece JVM + donanımını karşılaştırıyor gibi görünüyor.
Stefan L
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.