Peter Lawrey este un profesionist Java care a apărut pe poziția 3 în topul StackOverflow. Avem plăcerea de a-l avea ca invitat special la Cluj IT Days 2014, unde va prezenta un subiect tehnic Hot topics in Java Performance Tuning precum și o prezentare despre experiența sa de consultant independent What I have learnt by becoming an independent consultant. De asemenea, Peter va susține un training de o zi despre Java Performance, mai multe detalii puteți găsi pe www.itdays.ro. Am avut așadar plăcerea de a îl provoca pe Peter la câteva întrebări tehnice înainte de eveniment.
[Ovidiu Mățan]. Cum vezi Java 8 din perspectiva performanței?
[Peter Lawrey] Poate că cea mai importantă îmbunătățire a performanței a fost extinderea Compressed Oops la o memorie de 64 GB. Dacă ai un heap între 32 GB și 64 GB, poți obține o îmbunătățire a eficienței memoriei. Poți folosi compressed oops pentru 128 GB heaps, dar este puțin probabil să ajute în această fază.
Adesea, mai importantă decât performanța CPU este performanța dezvoltatorului. În această privință, adăugarea de lambda și lanțul API pot face o mare diferență dacă le poți folosi efectiv. Un dezvoltator costă de obicei mai mult într-un an decât un server scump, așa că economisirea timpului de dezvoltare este importantă, adesea mai importantă decât salvarea CPU.
Putem lua în considerare orice clase care se comportă mai bine din punctul de vedere al utilizării memoriei decât în Java 7 sau 6?
JSR 310 are o bibliotecă DateTime îmbunătățită; este mult mai bună decât Calendarul din mai multe motive, iar performanța este unul dintre ele.
Optimizarea aplicațiilor necesită un mod de testare adecvat și repetat. Folosiți de obicei un framework sau alt mecanism pentru a obține acest lucru?
[Peter] Clienții noștri utilizează Chronicle Queue pentru a înregistra fiecare intrare într-un sistem timp de o zi /zile. Derularea acestui șir care persistă le permite să recreeze bug-uri obscure și să investigheze probleme de performanță dificil de găsit. Încercarea de a recrea probleme de performanță cu un test sintetic este un început bun, dar este dificil să găsești mai mult de jumătate din problemele de performanță în acest mod. În schimb, este mai bine să folosești un volum de lucru real pentru o zi sau o săptămână.
În agenda workshop-ului, avem subiectul Low level Java programming, how to make using Unsafe safer? (Programarea Java low level,cum să faci utilizarea Unsafe să fie mai sigură?), ceea ce sună foarte interesant. Poți să oferi un indiciu cititorilor noștri?
[Peter] Pe scurt, vrei o bibliotecă care te încurajează să utilizezi Unsafe într-un mod sigur. Noi folosim o bibliotecă pe care o numim Java-Lang, care are unthread (fir) sigur, versiuni de 64 biți ale ByteBuffer, care vă permite accesul la fișierele de memorie partajate, într-o manieră relativ sigură. Ceea ce ar trebui să fie și mai interesant este că puteți utiliza structuri de date pe care noi le-am construit pe această bibliotecă pentru a susține șirurile, Maps și Sets, care au interfețe mai simple cu care să lucrezi. Ambele Queue și Map pot persista și distribui date între procese pe același aparat într-un ritm de 30+ M operații per secundă. Ceea ce ar fi fost imposibil de făcut în Java pură, în oricare alt mod.
De exemplu, poți scrie:
Map map = ChronicleMapBuilder.of(String.class, String.class).createPersistedTo(file);
// you can now use the map as normal.
map.put(“hello”, “world”);
String s = map.get(“hello”);
[Peter] Ceea ce e magic în legătură cu aceasta este că intrarea este vizibilă în mai puțin de o micro-secundă la toate JVM-urile de pe computerul tău și persistă. Este o metodă simplă și rapidă de stocare de date. Notă: deoarece harta este păstrată pe disk, ea este limitată numai de mărimea spațiului liber de pe discul tău și nu de mărimea heap. Cum este off heap, nu contribuie la timpul de pauză al GC-ului tău, indiferent cât este de mare. De asemenea, necesită un timp scurt de încărcare la restart, căci nu este nevoie să fie încărcat în heap. O hartă de 1000M poate dura 10ms pentru a fi gata de utilizare.
În ultimul articol al tău de pe blog, Chronicle Map și Yahoo Cloud Service Benchmark, propunerea ta era de a utiliza valori de 100 biți pentru perechi valori cheie pentru a obține rezultate mai bune. Totuși, menționezi efectul de colectare de deșeuri în teste. Cum putem de asemenea minimiza intervenția cumulărilor de deșeu în codul de performanță înaltă?_
[Peter] În Yahoo Cloud Service Benchmark, deșeurile produse devin în final o constrângere. La aproximativ 3 milioane de citiri/scrieri pe secundă, benchmark-ul poate utiliza până la 90% din CPU.
Cel mai bun loc pentru a începe să reduci reziduul este să rulezi un test realist într-un profiler de memorie. Eu utilizez YourKit, dar mai există și alte _profiler-_e comerciale bune. Odată ce vezi unde se generează cea mai mare parte a reziduului, le poți înlocui cu alternative care creează mai puține reziduuri sau deloc. De exemplu, utilizați primitive, obiecte simple în loc de Maps, obiecte mutabile reciclate, sau structuri de date off heap.
Cel mai mare beneficiu al reducerii reziduurilor este pe lângă durata de pauză GC, și viteza de rulare a codului tău între pauze. Reducerea ratei alocate poate îmbunătăți rezultatul de 2- 5x excluzând timpul de pauză GC. Rate de alocare mai mici înseamnă că nu îți umplicache-urile din CPU-ul tău cu reziduuri în fiecare fracțiune de milisecundă și că thread-urile (firele) tale lucrează mai eficient. L1 cache nu este numai de 10-20x mai rapid decât L3 cache-ul tău, dar L3 cache este o resursă partajată, așa că thread-urile tale încep să se încurce unele pe altele cu cât utilizezi mai mult L3 cache, iar programul tău nu mai scalează la fel de bine atunci când folosești mai multe nuclee.