JMH ile Doğru Ölçüm — Sayıya Güvenmeden Önce
Önce şunu oku: Sınıf Yükleme ve JIT — Uygulama Neden Isınır?
30 saniyede özet
Elle yazılmış bir zamanlama döngüsü çoğu zaman ölçmek istediğin şeyi ölçmez: ısınmayı, silinmiş bir hesabı ya da bir sabiti ölçer. JMH bu tuzakları düzenler; sonucu okurken de hata payına bakılır.
Bir koşucunun hızını ölçeceksin. İlk turunu sayarsan ısınmasını ölçersin; tek yarışa bakarsan o günkü şansını.
Kodun hızını ölçmek de böyle. Üstelik Java’da koşucu bazen yolu hiç koşmadan bitiş çizgisinde belirir.
Adım adım oku
- Kronometre hazır, koşucu başlıyor.
- İlk turlar yavaş: koşucu daha ısınıyor.
- Birkaç turdan sonra hız oturuyor.
- Hakem ısınma turlarını saymıyor, yarışı birkaç kez tekrarlatıyor ve sapmayı yazıyor.
-
Bayt: Faiz hesabını long ile yeniden yazdım ve döngüyle ölçtüm. Neredeyse sıfır sürüyor!
-
Sen: Sıfır mı? Bu biraz fazla iyi değil mi?
-
Bayt: Haklısın. Belki ölçtüğüm şey hesap değildi.
-
Bayt: O zaman önce sayının neyi ölçtüğüne bakalım.
Basit bir döngü neden yanıltır?
İlki ısınma. JVM kodu önce yorumlar, sık çalışanı sonra derler; ilk çağrılar bu yüzden yavaştır. Bunu Sınıf yükleme ve JIT dersinde gördün.
Faiz hesabını bir döngüde çok kez çağırdın ama sonucu hiçbir yerde kullanmadın. Ölçülen süre neredeyse sıfır çıktı. Kod gerçekten bu kadar hızlı mı? Cevabı göster
Büyük ihtimalle hayır. Sonucu kullanılmayan bir hesap, JIT için gereksiz iştir ve silinebilir. Ölçtüğün şey boş bir döngü olabilir.
İkincisi bu: dead code eliminationDerleyicinin ya da JIT'in, sonucu hiçbir yerde kullanılmayan hesabı tamamen silmesi. Ölçümde boş bir döngünün süresini görmene yol açar.JMH'de ölçülen metot sonucunu döndürür ya da Blackhole.consume ile verir; böylece sonuç kullanılmış sayılır.Sözlükte gör →. Üçüncüsü de constant foldingGirdisi hep aynı sabit olan bir hesabın derleme anında bir kez yapılıp sonucun koda gömülmesi. Ölçülen şey hesap değil, hazır bir sabit olur.Önlemek için girdi final olmayan bir @State alanından okunur.Sözlükte gör →: girdi hep aynı sabitse, JIT hesabı önceden yapıp sonucu koda gömebilir.
Kafam karıştı, daha basit anlat
Elle ölçerken JVM’in akıllılığı sana karşı çalışır: kullanmadığın işi siler, hep aynı olan hesabı bir kez yapar.
Bir metodu System.nanoTime ile tek bir kez çağırıp ölçtün. Bu sayı neyi ölçer?
Her ölçüm hatasını, sonucu hangi tuzağın bozduğuna göre ayır.
JMH ile ölçmek
JMH, OpenJDK’nın mikro ölçümKüçük bir kod parçasının, örneğin tek bir metodun, süresini ölçmek. JVM'de ısınma ve JIT yüzünden elle yapıldığında kolayca yanıltır.Java'da standart araç JMH'dir (Java Microbenchmark Harness). Sistemin geneli için profiler ve yük testi kullanılır.Sözlükte gör → aracıdır. Isınmayı, ayrı JVM’leri ve tekrarları senin yerine düzenler.
@State(Scope.Benchmark)public class InterestBenchmark { long balanceKurus = 1_250_000; // not final: cannot be folded
@Benchmark public long dailyInterest() { return balanceKurus * 35 / 36_500; // returned: JMH consumes it }}İki kural yeter. Metot sonucunu döndürür (ya da Blackhole.consume ile verir) ve girdisini final olmayan bir @State alanından alır.
Kafam karıştı, daha basit anlat
Sonucu geri ver ki silinmesin. Girdiyi bir alandan al ki önceden hesaplanmasın.
JMH'de ölçülen metot neden sonucunu döndürmeli?
Ölçülecek girdi neden final olmayan bir @State alanından okunur?
Sonucu okumak
JMH her ölçüm için bir skor ve yanında bir hata payı yazar. İki sonucun hata payları örtüşüyorsa aralarında bir fark gösterilmiş değildir.
Mikro ölçüm küçük bir parçayı ölçer. Bir sistemin nerede yavaşladığını ise async-profiler ya da JFR gibi bir profiler ve gerçekçi bir yük testi söyler.
JMH iki yöntem için skor ve hata payı yazdı; aralıklar büyük ölçüde örtüşüyor. Ne söylersin?
Kendin gör
Aynı soru dört yolla ölçülüyor: faiz hesabı ne kadar sürüyor? Simülatör süre göstermiyor; her yöntemin neyi ölçtüğünü gösteriyor.
Faiz hesabı ne kadar sürüyor? Önce neyi ölçtüğüne bak
Tohum 1Oynat ya da adımla.
Şu an ne oldu?
Bir sayı çıkacak
Yöntem: Elle döngü, sonuç kullanılmıyor. Her yöntem bir sayı yazdırır. Asıl soru, o sayının neyi ölçtüğü.
Görevler0/3
JIT'in işi silebildiği ölçümü bulaçık
İpucu
Sonucu hiçbir yere gitmeyen bir hesap ara.
Doğru araçla bile yanlış ölçülen metodu bulaçık
İpucu
JMH her şeyi kurtarmaz: metodun ne döndürdüğüne bak.
Bütün kontrolleri geçen ölçümü kuraçık
İpucu
Isınma, ayrı JVM, kullanılan sonuç, değişken girdi ve hata payı.
Olay günlüğü (0)
Henüz olay yok. Oynat veya adımla.
- Varsayılanı oynat. Elle döngü: sonuç kullanılmıyor, iş silinmiş olabilir.
- Tek çağrıyı dene. Ölçülen şey sınıf yükleme ve yorumlayıcı.
- “JMH, ama metot void” seç. Doğru araç, yanlış metot.
- Son seçeneği oynat. Bütün kontroller geçiyor.
Simülatörde JMH kullanılan bir ölçüm yine de güvenilmez çıktı. Neden?
Tuzaklar
@Benchmark içinde kendi döngün. JIT döngüyü açıp birleştirebilir; tekrarı JMH’ye bırak.
Gürültülü makinede ölçmek. Tarayıcı, IDE ve derleme açıkken alınan sonuçlar birbirini tutmaz. Ölçümü sakin bir makinede, aynı koşullarda tekrarla.
Önemsiz parçayı hızlandırmak. Profiler önce nereye bakılacağını söyler; mikro ölçüm ancak ondan sonra gelir.
Bu JMH ölçümü şüpheli derecede hızlı sonuç veriyor. Sorunlu satırları işaretle.
Derinleş · Faiz hesabı: BigDecimal ile long kuruşu adil karşılaştırmak 3 dosya · ~44 satır · ilk okumada atlayabilirsin
Kendini sına
Önce hızlı bir ısınma: puan yok, kayıt yok. Sonra asıl sorular.
Sonucu kullanılmayan bir hesap JIT tarafından silinebilir.
Bir servis yavaş ve ekip hemen JMH ile bir metodu hızlandırmak istiyor. İlk adım ne olmalı?
Aklında kalacak üç şey
- 1 Elle yazılmış bir zamanlama döngüsü ısınmayı, JIT'in sildiği bir hesabı ya da önceden hesaplanmış bir sabiti ölçebilir. Yanlış ölçüm hata vermez, yanlış bir sayı verir.
- 2 JMH ısınma turlarını ayırır, her denemeyi ayrı bir JVM'de koşturur ve hata payı verir. Ölçülen metot sonucunu döndürür, girdisini de @State'ten alır.
- 3 Hata payları örtüşen iki sonuç arasında fark yoktur. Mikro ölçüm bir parçayı ölçer; sistemin nerede yavaşladığını profiler ve yük testi söyler.
4 kart sonraki derste seni bekliyor