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?
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?
Yanıtlar:
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: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 çıktı olmadığını doğrulayabilirsiniz.
Kural 3:-client
ve -server
ve OSR ile 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 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. -Xbatch
Derleyiciyi uygulama ile serileştirmek için kullanın ve derleyicinin -XX:CICompilerCount=1
kendisine 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 Xms
ve UseEpsilonGC
varsa 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 .
System.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()
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.
Bu sorunun cevap olarak işaretlendiğini biliyorum ancak mikro ölçütler yazmamıza yardımcı olan iki kütüphaneden bahsetmek istedim
Başlangıç eğiticileri
Başlangıç eğiticileri
Java karşılaştırmaları için önemli şeyler:
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 .)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.
gc
her zaman kullanılmayan belleği serbest bıraktığı izlenimini verir .
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.
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:
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.
İ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.
Kıyaslanmış kodda hesaplanan sonuçları bir şekilde kullandığınızdan emin olun. Aksi takdirde kodunuz optimize edilebilir.
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).
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 A
daha yüksek bir yayılım da olabilir, bu nedenle ölçülen performans avantajı A
ile 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.
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");
println
Ayrı 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.
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.