Java'da nasıl doğru bir mikro ölçüt yazarım?
Java'da doğru bir mikro karşılaştırmayı nasıl yazarsınız (ve çalıştırırsınız)?
Düşünülmesi gereken çeşitli şeyleri gösteren bazı kod örnekleri ve yorumlar arıyorum.
Örnek: Kıyaslama zamanı / yinelemeyi veya yinelemeleri / zamanı ölçmeli mi ve neden?
İlgili: Kronometre karşılaştırması kabul edilebilir mi?
Yanıtlar
Java HotSpot'un yaratıcılarından mikro karşılaştırmalar yazma hakkında ipuçları :
Kural 0: JVM'ler ve mikro kıyaslama hakkında saygın bir makale okuyun. İyi olanı Brian Goetz, 2005 . Mikro ölçütlerden çok fazla şey beklemeyin; sadece sınırlı bir JVM performans özelliklerini ölçerler.
Kural 1: Her zaman, zamanlama aşamalarından önce tüm başlatmaları ve derlemeleri tetiklemeye yetecek kadar, test çekirdeğinizi baştan sona çalıştıran bir ısınma aşaması ekleyin. (Isınma aşamasında daha az yineleme uygundur. Temel kural, birkaç on binlerce iç döngü yinelemesidir.)
Kural 2: Her zaman çalıştırmak -XX:+PrintCompilation
, -verbose:gc
derleyici 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 hiçbir çıktı olmadığını doğrulayabilirsiniz.
Kural 3:-client
ve -server
, ve OSR ve düzenli derlemeler arasındaki farkın farkında olun . -XX:+PrintCompilation
Bayrak, ö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 sunucudan istemciye ve düzenli olarak OSR'ye tercih edin.
Kural 4: Başlatma etkilerinin farkında olun. Zamanlama aşamanız sırasında ilk kez yazdırmayın, çünkü yazdırma işlemi yükler ve sınıfları başlatır. Özel olarak sınıf yüklemesini test etmediğiniz sürece (ve bu durumda yalnızca test sınıflarını yüklemediğiniz), ı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: İyileştirme ve yeniden derleme etkilerinin farkında olun. Zamanlama aşamasında ilk kez herhangi bir kod yolunu takip etmeyin, çünkü derleyici, yolun hiç kullanılmayacağına dair daha önceki iyimser bir varsayıma dayanarak kodu gereksiz 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şırmayı bekleyin. Bir şeyi neyin daha hızlı veya daha yavaş kıldığına dair teoriler oluşturmadan önce kodu kendiniz inceleyin.
Kural 7: Ölçümlerinizdeki gürültüyü azaltın. Kıyaslama ölçünüzü sessiz bir makinede çalıştırın ve aykırı değerleri atarak birkaç kez çalıştırın. -Xbatch
Derleyiciyi uygulamayla serileştirmek için kullanın ve derleyicinin -XX:CICompilerCount=1
kendisiyle paralel çalışmasını engellemek için ayarlamayı düşünün . GC ek yükünü azaltmak için elinizden gelenin en iyisini yapın, Xmx
eşittir (yeterince büyük) ayarlayın Xms
ve UseEpsilonGCvarsa kullanın .
Kural 8: Muhtemelen daha verimli olduğundan ve yalnızca bu amaç için zaten hata ayıklaması yapıldığından, kıyaslamanız için bir kitaplık kullanın. Gibi JMH , Kalınlık veya Bill ve Java için Paul Mükemmel UCSD Deneyler .
Bu sorunun yanıtlanmış olarak işaretlendiğini biliyorum ancak mikro ölçütler yazmamıza yardımcı olan iki kitaplıktan bahsetmek istedim.
Google'dan kumpas
Başlangıç eğitimleri
- http://codingjunkie.net/micro-benchmarking-with-caliper/
- http://vertexlabs.co.uk/blog/caliper
OpenJDK'dan JMH
Başlangıç eğitimleri
- JVM'de Kıyaslama Tuzaklarından Kaçınma
- Java Microbenchmarking için JMH'yi kullanma
- JMH'ye Giriş
Java karşılaştırmaları için önemli şeyler şunlardır:
- Kod birkaç kez çalıştırarak ilk JIT Isınma uğramadan önce onu
- Sonuçları saniyeler veya (daha iyi) onlarca saniye içinde ölçebilmek için yeterince uzun süre çalıştırdığınızdan emin olun.
System.gc()
Yinelemeler arasında arama yapamasanız da, onu testler arasında çalıştırmak iyi bir fikirdir, böylece her bir testin çalışmak için "temiz" bir bellek alanı elde etmesini umuyoruz. (Evet,gc()
garantiden çok bir ipucudur, ancak tecrübelerime göre gerçekten çöp toplaması muhtemeldir .)- Yinelemeleri ve zamanı ve "en iyi" algoritma 1.0 puan alacak ve diğerleri göreceli bir şekilde puanlanacak şekilde ölçeklenebilen bir zaman / yineleme puanı göstermeyi seviyorum. Bu, tüm algoritmaları uzun süre çalıştırabileceğiniz , hem yineleme sayısını hem de zamanı değiştirebileceğiniz, ancak yine de benzer sonuçlar alabileceğiniz anlamına gelir .
NET'te bir kıyaslama çerçevesinin tasarımı hakkında blog yazma 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.
jmh , OpenJDK'ya yakın zamanda eklenen bir eklentidir ve Oracle'dan 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 karşılaştırmaları oluşturmak, çalıştırmak ve analiz etmek için bir Java koşumudur.
Örnek test yorumlarında çok ilginç bilgiler gömülü .
Ayrıca bakınız:
- JVM'de Kıyaslama Tuzaklarından Kaçınma
- Jmh'nin temel güçlü yönleri üzerine tartışma .
Kıyaslama zamanı / yinelemeyi veya yinelemeleri / zamanı ölçmeli mi ve neden?
Neyi test etmeye çalıştığınıza bağlıdır .
Gecikmeyle ilgileniyorsanız , zamanı / yinelemeyi kullanın ve iş hacmiyle ilgileniyorsanız yinelemeleri / zamanı kullanın.
İki algoritmayı karşılaştırmaya çalışıyorsanız, sırayı değiştirerek her biri için en az iki kıyaslama yapın. yani:
for(i=1..n)
alg1();
for(i=1..n)
alg2();
for(i=1..n)
alg2();
for(i=1..n)
alg1();
Farklı geçişlerde aynı algoritmanın çalışma zamanında bazı göze çarpan farklılıklar (bazen% 5-10) buldum.
Ayrıca, her döngünün çalışma süresinin en az 10 saniye veya daha fazla olması için n'nin çok büyük olduğundan emin olun . Ne kadar çok yineleme olursa, karşılaştırma sürenizdeki rakamlar o kadar önemli ve veriler o kadar güvenilirdir.
Bir şekilde karşılaştırmalı kodda hesaplanan sonuçları kullandığınızdan emin olun. Aksi takdirde kodunuz optimize edilebilir.
Java'da mikro ölçütler yazmak için birçok olası tuzak vardır.
Birincisi: Rastgele zaman alan her türlü olayı hesaplamanız gerekir: Çöp toplama, önbelleğe alma efektleri (dosyalar için OS ve bellek için CPU), IO vb.
İkincisi: Çok kısa aralıklarla ölçülen sürelerin doğruluğuna güvenemezsiniz.
Üçüncüsü: JVM, yürütme sırasında kodunuzu optimize eder. Böylece aynı JVM örneğindeki farklı çalıştırmalar daha hızlı ve daha hızlı hale gelecektir.
Önerilerim: Kıyaslamanızın birkaç saniye çalışmasını sağlayın, bu, milisaniyeler üzerinde bir çalışma süresinden daha güvenilirdir. JVM'yi ısıtın (JVM'nin optimizasyonları çalıştırabilmesi için kıyaslamayı ölçüm yapmadan en az bir kez çalıştırmak anlamına gelir). Ve kıyaslamanızı birden çok kez (belki 5 kez) çalıştırın ve medyan değeri alın. Her mikro karşılaştırmayı yeni bir JVM örneğinde çalıştırın (her yeni Java karşılaştırması için çağrı yapın) aksi takdirde JVM'nin optimizasyon etkileri daha sonraki çalışan testleri etkileyebilir. Isınma aşamasında yürütülmeyen şeyleri yürütmeyin (çünkü bu, sınıf yüklemesini ve yeniden derlemeyi tetikleyebilir).
Farklı uygulamaları karşılaştırırken mikro kıyaslama sonuçlarının analiz edilmesinin de önemli olabileceği unutulmamalıdır. Bu nedenle bir anlamlılık testi yapılmalıdır.
Bunun nedeni A
, kıyaslama çalıştırmalarının çoğu sırasında uygulamanın uygulamaya göre daha hızlı olabilmesidir B
. Ancak A
daha yüksek bir yayılmaya da sahip olabilir, bu nedenle ölçülen performans avantajı, A
ile karşılaştırıldığında hiçbir önemi olmayacaktır B
.
Bu nedenle, bir mikro ölçütü doğru yazmak ve çalıştırmak, aynı zamanda doğru şekilde analiz etmek de önemlidir.
Diğer mükemmel tavsiyeye ek olarak, aşağıdakilere de dikkat etmeliyim:
Bazı CPU'lar için (örn. TurboBoost'lu Intel Core i5 serisi), sıcaklık (ve şu anda kullanılan çekirdek sayısı ve bunların kullanım yüzdesi) saat hızını etkiler. CPU'lar dinamik olarak saat hızına sahip olduğundan, bu 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 karşılaştırmalarına müdahale edebilir. Sıcaklığın ve dalgalanmaların da Turbo frekansının ne kadar süreyle korunacağını etkilediğini unutmayın.
Belki de üzerinde doğrudan kontrole sahip olduğunuz daha temelde önemli bir husus: doğru şeyi ölçtüğünüzden emin olun! Örneğin, System.nanoTime()
belirli bir kod parçasını karşılaştırmak için kullanıyorsanız , ilgilenmediğiniz şeyleri ölçmekten kaçınmak için atama çağrılarını anlamlı olan yerlere yerleştirin. Örneğin, şunları yapmayın:
long startTime = System.nanoTime();
//code here...
System.out.println("Code took "+(System.nanoTime()-startTime)+"nano seconds");
Sorun, kod bittiğinde bitiş zamanını hemen alamamanızdır. 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");
http://opt.sourceforge.net/Java Micro Benchmark - farklı platformlarda bilgisayar sisteminin karşılaştırmalı performans özelliklerini belirlemek için gereken kontrol görevleri. Optimizasyon kararlarına rehberlik etmek ve farklı Java uygulamalarını karşılaştırmak için kullanılabilir.