Java performansını önemli ölçüde nasıl geliştiririm?


23

LMAX'taki ekip, 1 ms'den daha az gecikmeyle nasıl 100k TPS yapabildiklerine dair bir sunum yaptı . Bu sunumu bir blog , teknik makale (PDF) ve kaynak kodun kendisi ile yedeklediler .

Son zamanlarda, Martin Fowler , LMAX mimarisi üzerine mükemmel bir makale yayınladı ve şu anda saniyede altı milyon siparişi işleme koyabildiklerini ve ekibin performansta bir başka büyüklük sırası elde etmek için attığı adımların birkaçını vurguladı.

Şimdiye kadar, Business Logic Processor'ın hızının anahtarının hafızada her şeyi sıralı bir şekilde yaptığını açıkladım. Sadece bunu yapmak (ve aptalca hiçbir şey), geliştiricilerin 10K TPS'yi işleyebilecek bir kod yazmasını sağlar.

Daha sonra iyi kodun basit unsurlarına yoğunlaşmanın bunu 100K TPS aralığına getirebileceğini buldular. Bu sadece iyi faktörlü kod ve küçük yöntemlere ihtiyaç duyar - esasen bu, Hotspot'un daha iyi bir optimizasyon işi yapmasını ve işlemcilerin kodu çalışırken önbelleğe almasında daha verimli olmasını sağlar.

Başka bir büyüklük sırası daha yükseltmek biraz daha akıllıca geçti. LMAX ekibinin oraya ulaşmak için yararlı buldukları bazı şeyler var. Bunlardan bir tanesi, önbellek dostu ve çöp konusunda dikkatli olacak şekilde tasarlanmış Java koleksiyonlarının özel uygulamalarını yazmaktı.

Bu üst düzey performansa ulaşmak için bir başka teknik de performans testine dikkat çekmek. İnsanların performansı iyileştirmeye yönelik teknikler hakkında çok fazla konuştuğunu fark ettim, ancak gerçekten fark yaratan tek şey denemek.

Fowler bulunan birkaç şey olduğunu söyledi, ancak yalnızca bir çiftten bahsetti.

Bu performans seviyelerine ulaşmada yardımcı olabilecek başka mimariler, kütüphaneler, teknikler veya “şeyler” var mı?


11
“Hangi mimariler, kütüphaneler, teknikler veya“ şeyler ”bu tür performans seviyelerine ulaşmada yardımcı olur?” Neden sordun? Yani alıntı kesin listesi. Bunlardan hiçbiri bu listedeki öğelerin türünü etkilemeyen pek çok başka şey var. Başkasının adlandırabileceği herhangi bir şey bu liste kadar yardımcı olmaz. Şimdiye kadar üretilmiş en iyi optimizasyon listelerinden birini alıntı yaptığınızda neden kötü fikirleri soruyorsunuz?
S.Lott

Oluşturulan kodun sistemde nasıl çalıştığını görmek için hangi araçları kullandıklarını öğrenmek güzel olurdu.

1
İnsanların her türlü teknikle yemin ettiğini duydum. En etkili bulduğum şey, sistem düzeyinde profil oluşturma. Programınızın ve iş yükünüzün sistemi nasıl kullandığı gibi size darboğazlar gösterebilir. Performansla ilgili iyi bilinen kurallara uymanızı ve daha sonra kolayca ayarlayabilmeniz için modüler kod yazmanızı öneririm ... Sistem profillemede yanlış gideceğinizi sanmıyorum.
Ritler

Yanıtlar:


21

Yüksek performanslı işlemlerin işlenmesi için her türlü teknik vardır ve Fowler'in makalesinde yer alan konu, kanama alanındaki birçok kişiden sadece biridir. Herhangi birinin durumuna uygulanabilir veya uygulanamayacak bir dizi teknik listelemek yerine, temel prensipleri ve LMAX'in çok sayıda kişiye nasıl hitap ettiğini tartışmanın daha iyi olacağını düşünüyorum.

Yüksek ölçekli bir işlem işleme sistemi için aşağıdakilerin tümünü mümkün olduğunca yapmak istiyorsunuz:

  1. En yavaş depolama katmanlarında harcanan zamanı en aza indirin. Modern bir sunucuda en hızlıdan en yavaşına kadar: CPU / L1 -> L2 -> L3 -> RAM -> Disk / LAN -> WAN. En hızlı modern manyetik diskten en yavaş RAM'e bile atlama, sıralı erişim için 1000x'in üzerindedir ; rastgele erişim daha da kötüdür.

  2. Bekleyen harcanan zamanı en aza indirin veya ortadan kaldırın . Devlet, bu araçlar, mümkün olduğunca az devlet olarak paylaşımı ve gereken paylaşılacak Mümkün açık kilitleri kaçınarak.

  3. İş yükünü yay. İşlemciler son birkaç yılda çok daha hızlı bir şekilde artmadı, ancak daha da küçüldü ve bir sunucuda 8 çekirdek oldukça yaygın. Bunun ötesinde, çalışmayı Google'ın yaklaşımı olan birden fazla makineye bile yayabilirsiniz; Bunun en güzel yanı I / O dahil her şeyi ölçeklendirmesidir .

Fowler’a göre, LMAX bunların her birine aşağıdaki yaklaşımı uygular:

  1. Tutun tüm bellekte durumunu bütün zamanlar. Çoğu veritabanı motorları aslında bu zaten yapacak olursa tüm veritabanı bellekte sığabilecek, ancak bir gerçek zamanlı ticaret platformunda anlaşılabilir şans, bir şey bırakıyorum istemiyoruz. Bir ton risk eklemeden bunu ortadan kaldırmak için bir grup hafif yedekleme ve yük devretme altyapısı inşa etmek zorunda kaldılar.

  2. Girdi olaylarının akışı için kilitsiz bir sıra ("bozucu") kullanın. Kesin olarak kilitlenmeyen ve aslında genellikle acı verici şekilde yavaş dağıtılmış işlemleri içeren geleneksel dayanıklı mesaj sıralarının aksine .

  3. Fazla değil. LMAX bunu iş yükünün birbirine bağımlı olması esasına dayanarak veri yolunun altına atar; birinin sonucu diğerlerinin parametrelerini değiştirir. Bu kritik bir uyarıdır ve Fowler'ın açıkça söylediği bir uyarıdır. Onlar yaparım bazı yük devretme yetenekleri sağlamak amacıyla eşzamanlılık kullanımını, ancak tüm iş mantığını bir üzerine işlenir tek iplik .

LMAX, yüksek ölçekli OLTP'ye tek yaklaşım değildir . O kendi başına Oldukça zekice olsa da, senin yok değil bu performans seviyesine çekip amacıyla kanama-kenar teknikleri kullanmak gerekir.

Yukarıdaki prensiplerin hepsinde, muhtemelen en önemli ve en etkili olan 3 numaralı cihazdır, çünkü açıkçası donanım ucuzdur. İş yükünü yarım düzine çekirdeğe ve birkaç düzine makineye uygun şekilde bölebiliyorsanız, gökyüzü geleneksel Paralel Hesaplama tekniklerinin sınırıdır . Bir sürü mesaj kuyruğu ve bir yuvarlak dağıtımcı dağıtıcısı dışında hiçbir şeyle ne kadar verim elde edebileceğinizi görünce şaşıracaksınız. Belli ki LMAX kadar verimli değil - aslında yakın bile değil - verim, gecikme ve maliyet etkinliği ayrı ayrı endişeler ve burada özellikle verim hakkında konuşuyoruz.

Eğer LMAX’ın, özellikle de aceleci bir tasarım seçimine karşılık bir iş gerçekliğine karşılık gelen ortak bir durumla aynı tür özel ihtiyaçlarınız varsa, o zaman bileşenlerini denemeyi öneririm, çünkü çok fazla görmedim. bu şartlara uygun başka bir şey. Fakat eğer sadece yüksek ölçeklenebilirlikten bahsediyorsak, o zaman sizleri dağıtık sistemler hakkında daha fazla araştırma yapmaya davet ediyorum, çünkü bugün çoğu kurum tarafından kullanılan kanonik yaklaşımlar (Hadoop ve ilgili projeler, ESB ve ilgili mimariler, ayrıca Fowler bahseder, vb.).

SSD'ler aynı zamanda bir oyun değiştirici olacak; tartışmasız, onlar zaten. Artık RAM'e benzer erişim sürelerine sahip kalıcı bir depolama alanınız olabilir ve sunucu sınıfı SSD'ler hala korkunç bir şekilde pahalı olsalar da, benimseme oranları arttıkça nihayetinde fiyatlar düşecektir. Kapsamlı bir şekilde araştırıldı ve sonuçlar oldukça kafa karıştırıcı ve yalnızca zamanla daha iyi olacak, bu nedenle "her şeyi bellekte tut" konsepti eskiden olduğundan çok daha az önemli. Bu yüzden bir kez daha, mümkün olduğunda eşzamanlılığa odaklanmaya çalışıyorum.


Fowler'ın kağıt önbellek habersiz algoritmalar için bir ayak notta başvuru vardı yoktu sürece prensiplerinin tartışılması ilkeleri temelinde yatan bir ... harika ve yorumunuz mükemmel ve en.wikipedia.org/wiki/Cache-oblivious_algorithm sığar ( kategori 1'de yukarıda var) Onlara asla tökezlemezdim. Yani ... yukarıda sahip olduğunuz her kategori için, bir kişinin bilmesi gereken ilk 3 şeyi biliyor musunuz?
Dakotah Kuzey,

@Dakotah: Çoğu zaman uygulamaların büyük çoğunluğunda beklemekle harcanan disk G / Ç'yi tamamen elimine almadığım sürece ve önbellek yerelliği konusunda endişelenmeye bile başlamam . Bunun dışında "bir kişinin bilmesi gereken ilk 3 şey" ile ne demek istiyorsun? Top 3 ne, ne hakkında bilmek?
Aarona temmuz

RAM erişim gecikmesinden (~ 10 ^ -9s) manyetik disk gecikmesine (~ 10 ^ -3s-ortalama durum) atlamak, 1000x'ten daha büyük bir başka büyüklük sırasıdır. SSD'ler bile yüzlerce mikrosaniyede ölçülen erişim zamanlarına sahiptir.
Sedat Alien,

@Sedate: Gecikme evet, fakat bu ham gecikmeden çok bir iş çıkışı sorunudur ve erişim zamanlarını geçmiş ve toplam transfer hızına ulaştığınızda, diskler o kadar da kötü değildir. Bu yüzden rasgele ve sıralı erişim arasındaki ayrımı yaptım; rasgele erişim senaryoları için , öncelikle gecikme konusu haline gelir.
Aaron,

@Aaronaught: Yeniden okuduktan sonra haklı olduğunu düşünüyorum. Belki de, tüm veri erişiminin mümkün olduğunca sıralı olması gerektiği bir noktaya değinilmelidir; RAM'den gelen verilere erişirken de önemli avantajlar olabilir.
Sedat Alien

10

Bundan öğrenmenin en büyük dersi, temel bilgilerle başlamanız gerektiğidir:

  • İyi algoritmalar, uygun veri yapıları ve "gerçekten aptal" bir şey yapmamak
  • İyi faktörlü kod
  • Performans testi

Performans testi sırasında kodunuzu belirler, darboğazları bulur ve bunları birer birer giderirsiniz.

Çok fazla insan "bunları birer birer düzelt" bölümüne atlıyor. "Java koleksiyonlarının özel uygulamalarını" yazmak için çok zaman harcıyorlar, çünkü sistemlerinin yavaş olmasının tümünün önbellek eksikliğinden kaynaklandığını biliyorlar. Bu katkıda bulunan bir faktör olabilir, ancak böyle düşük düzeyli bir kodu ince ayarlamaya doğru atlarsanız, LinkedList'i kullanmanız gerektiğinde ArrayList'i kullanmanın daha büyük sorununu özme olasılığınız veya sisteminizin gerçek nedeni bu olabilir. ORM'niz bir işletmenin tembel yüklü çocukları olduğu ve bu nedenle her istek için veri tabanına 400 ayrı yolculuk yaptığı için yavaş olması.


7

Özellikle LMAX kodu hakkında yorum yapmayacağım, çünkü bunun büyük ölçüde intihar olduğunu düşünüyorum, ancak burada önemli ölçüde ölçülebilir performans iyileştirmeleriyle sonuçlanan bazı örnekler var.

Her zaman olduğu gibi, bunlar bir sorununuz olduğunu ve performansı iyileştirmeniz gerektiğini bildiğiniz zaman uygulanması gereken tekniklerdir - aksi takdirde sadece erken optimizasyon yapıyor olabilirsiniz.

  • Doğru veri yapısını kullanın ve gerekirse özel bir tane oluşturun - doğru veri yapısı tasarımı, mikro optimizasyonlardan elde edeceğiniz gelişmeyi ciddiye alır, bu yüzden önce bunu yapın. Algoritmanız, çok sayıda hızlı O (1) rasgele erişim okumasının performansına bağlıysa, bunu destekleyen bir veri yapısına sahip olduğunuzdan emin olun! Bunu düzeltmek için bazı çemberlerin içinden atlamakta fayda var, örneğin verilerinizi çok hızlı bir şekilde kullanmak için bir dizide temsil edebileceğiniz bir yol bulmak O (1) indeksli okumalar.
  • CPU, bellek erişiminden daha hızlıdır - bellek L1 / L2 önbelleğinde değilse, bir rastgele belleği okumak için gereken sürede oldukça fazla hesaplama yapabilirsiniz. Size bir hafıza okumasını kaydederse, genellikle hesaplama yapmaya değer.
  • JIT derleyicisine son oluşturma alanları, yöntemleri ve sınıfları finalinde yardım edin, JIT derleyicisine gerçekten yardımcı olan belirli optimizasyonları sağlar. Özel örnekler:

    • Derleyici, bir final sınıfının alt sınıfları olmadığını varsayabilir, böylece sanal yöntem çağrılarını statik yöntem çağrılarına dönüştürebilir
    • Derleyici, statik bir final alanını güzel bir performans iyileştirmesi için sabit olarak değerlendirebilir, özellikle sabit, derleme zamanında hesaplanabilecek hesaplamalarda kullanılırsa.
    • Bir Java nesnesi içeren bir alan nihai olarak başlatılmışsa, optimizasyon cihazı hem boş denetlemeyi hem de sanal yöntem gönderimini ortadan kaldırabilir. Güzel.
  • Koleksiyon sınıflarını dizilerle değiştirin - bu daha az okunabilir kodla sonuçlanır ve sürdürülmesi daha zordur ancak bir dolaylı katmanı kaldırdığı ve pek çok hoş dizi erişim optimizasyonundan yararlandığı için neredeyse her zaman daha hızlıdır. Genelde iç döngüde / performansa duyarlı kodda iyi bir fikir, onu bir darboğaz olarak tanımladıktan sonra, aksi halde okunabilirlik adına sakınınız!

  • İlkelleri mümkün olan her yerde kullanın - ilkel nesneler temelde nesneye dayalı eşdeğerlerinden daha hızlıdır. Özellikle, boks, büyük miktarda ek yük ekler ve kötü GC duraklamalarına neden olabilir. Performansa / gecikmeye önem veriyorsanız, ilkellerin kutulanmasına izin vermeyin.

  • Düşük seviyeli kilitlemeyi en aza indirin - kilitler düşük seviyede çok pahalıdır. Tamamen kilitlemekten kaçınmanın yollarını bulun ya da kaba taneli bir seviyede kilitlemenizi sağlayın; böylece yalnızca nadiren büyük veri blokları üzerinde kilitlemeniz gerekir ve düşük düzeyli kod, kilitleme veya eşzamanlılık sorunları hakkında endişelenmenize gerek kalmadan devam edebilir.

  • Bellek ayırmaktan kaçının; bu, genel olarak sizi yavaşlatabilir; çünkü JVM çöp toplama işlemi inanılmaz derecede verimlidir, ancak çok düşük gecikme süresi almaya çalışıyorsanız ve GC duraklamalarını en aza indirmeniz gerekiyorsa çok yararlıdır. Tahsislerden kaçınmak için kullanabileceğiniz özel veri yapıları vardır - özellikle http://javolution.org/ kütüphanesi bunlar için mükemmel ve dikkate değerdir.

Son yöntemlerin yapılmasına katılmıyorum . JIT, bir yöntemin asla geçersiz kılınmayacağını çözebilir. Ayrıca, bir alt sınıfın daha sonra yüklenmesi durumunda optimizasyonu geri alabilirsiniz. Ayrıca “bellek ayırmaktan kaçının” de GC'nin işini zorlaştırabilir ve böylece sizi yavaşlatabilir - bu nedenle dikkatli kullanın.
maaartinus

@maaartinus: finalBazı JİT'lerle ilgili olarak bunu çözebilir, bazılarında olmayabilir. Uygulamaya bağlıdır (birçok performans ayarı ipucu gibi). Tahsisler hakkında anlaşın - bunu kıyaslamalısınız. Genellikle tahsisatların kaldırılmasının daha iyi olduğunu öğrendim, fakat YMMV.
mikera

4

Aaronaught tarafından verilen mükemmel bir cevapta belirtilenin dışında , bunun gibi bir kodun geliştirilmesi, anlaşılması ve hata ayıklanması oldukça zor olabilir. LMAX blogunda bahsi geçen adamlarından biri olarak "çok verimli olmasına rağmen ... batırılması çok kolay ."

  • Geleneksel sorguları ve kilitleri kullanan bir geliştirici için, yeni bir yaklaşıma kodlama vahşi ata binmek gibi gelebilir. En azından bu, LMAX teknik makalesinde hangi kavramdan bahsettiğimi Phaser ile deney yaparken kendi deneyimimdi . Bu anlamda, bu yaklaşımın geliştirici beyin çekişmesi için kilit çekişme esnaf ettiğini söyleyebilirim .

Yukarıda verilenlere göre, Yıkıcı ve benzer yaklaşımları seçenlerin , çözümlerini sürdürmeye yetecek geliştirme kaynaklarına sahip olduklarından daha iyi olduklarını düşünüyorum .

Genel olarak, Yıkıcı yaklaşım bana oldukça umut verici görünüyor. Şirketiniz, örneğin yukarıda belirtilen nedenlerden dolayı bunu kullanmaya yetmiyor olsa bile, yönetiminizi, onu incelemeye (ve genel olarak SEDA'ya ) bir miktar çaba harcayacağına "yatırmaya" ikna etmeyi düşünün. müşterileri onları 4x, 8x vb. daha az sunucu gerektiren daha rekabetçi bir çözüm lehine bırakacaklar.

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.