{"id":35851,"date":"2019-10-31T22:07:03","date_gmt":"2019-10-31T19:07:03","guid":{"rendered":"https:\/\/prohoster.info\/blog\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java\/"},"modified":"2019-10-31T22:07:03","modified_gmt":"2019-10-31T19:07:03","slug":"bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java","status":"publish","type":"post","link":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java","title":{"rendered":"Una lunga intervista con Cliff Click, il padre della compilazione JIT in Java","gt_translate_keys":[{"key":"rendered","format":"text"}]},"content":{"rendered":"<p><img decoding=\"async\" alt=\"Una lunga intervista con Cliff Click, il padre della compilazione JIT in Java\" src=\"\/wp-content\/uploads\/2019\/07\/9ea9740ef74c0ae14d334482af115222.png\" style=\"display:block;margin: 0 auto;\" \/><strong>Cliff Click<\/strong> \u2014 CTO dell'azienda Cratus (sensori IoT per migliorare i processi), fondatore e co-fondatore di diverse startup (inclusi Rocket Realtime School, Neurensic e H2O.ai), con vari exit di successo. Cliff ha scritto il suo primo compilatore a 15 anni (Pascal per TRS Z-80)! \u00c8 noto soprattutto per il suo lavoro su C2 in Java (il Sea of Nodes IR). Questo compilatore ha dimostrato al mondo che JIT pu\u00f2 produrre codice di alta qualit\u00e0, un fattore chiave per l'emergere di Java come una delle principali piattaforme software moderne. In seguito, Cliff ha aiutato l'azienda Azul Systems a costruire un mainframe a 864 core con software in puro Java, che supportava pause GC su un heap da 500 gigabyte in 10 millisecondi. In generale, Cliff ha lavorato su tutti gli aspetti della JVM.<br clear=\"all\"><br \/>\n\u00a0<br \/>\nQuesto hubrapost \u00e8 una lunga intervista con Cliff. Parleremo dei seguenti argomenti:<\/p>\n<p><\/p>\n<ul>\n<li>Passaggio alle ottimizzazioni di basso livello<\/li>\n<li>Come effettuare un grande refactoring<\/li>\n<li>Modello di costo<\/li>\n<li>Apprendimento delle ottimizzazioni di basso livello<\/li>\n<li>Esempi pratici di miglioramento delle prestazioni<\/li>\n<li>Perch\u00e9 creare un proprio linguaggio di programmazione<\/li>\n<li>Carriera di ingegnere delle prestazioni<\/li>\n<li>Sfide tecniche<\/li>\n<li>Un po' su allocazione dei registri e multithreading<\/li>\n<li>La sfida pi\u00f9 grande della vita<\/li>\n<\/ul>\n<p><\/p>\n<p>Intervista condotta da:<\/p>\n<p><\/p>\n<ul>\n<li><strong>Andrey Satari<\/strong> di Amazon Web Services. Nella sua carriera ha lavorato a progetti molto diversi: ha testato un database distribuito NewSQL in Yandex, un sistema di rilevamento cloud presso Kaspersky Lab, un videogioco multiplayer in Mail.ru e un servizio di calcolo dei tassi di cambio in Deutsche Bank. \u00c8 interessato al testing di sistemi backend e distribuiti su larga scala.<\/li>\n<li><strong>Vladimir Sitnikov<\/strong> di Netcracker. Lavora da dieci anni sulle prestazioni e la scalabilit\u00e0 di NetCracker OS \u2014 il software utilizzato dagli operatori di telecomunicazioni per automatizzare i processi di gestione della rete e delle apparecchiature di rete. \u00c8 appassionato di prestazioni in Java e Oracle Database. Autore di oltre una dozzina di miglioramenti delle prestazioni nel driver JDBC ufficiale di PostgreSQL.<noindex><a rel=\"nofollow\" name=\"habracut\"><\/a><\/noindex><\/li>\n<\/ul>\n<p><\/p>\n<h1 id=\"perehod-k-nizkourovnevym-optimizaciyam\">Passaggio alle ottimizzazioni di basso livello<\/h1>\n<p><\/p>\n<p><strong>Andrey<\/strong>: Sei una persona conosciuta nel mondo della compilazione JIT, in Java e nel lavoro sulle prestazioni in generale, giusto?\u00a0<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Esatto!<\/p>\n<p><\/p>\n<p><strong>Andrey<\/strong>: Iniziamo con alcune domande generali sul lavoro sulle prestazioni. Cosa pensi della scelta tra ottimizzazioni ad alto e basso livello, come il lavoro a livello di CPU?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Qui tutto \u00e8 semplice. Il codice pi\u00f9 veloce \u00e8 quello che non viene mai eseguito. Pertanto, \u00e8 sempre necessario partire da un alto livello, lavorando sugli algoritmi. Una notazione O migliore batter\u00e0 una notazione O peggiore, a meno che non entrino in gioco costanti abbastanza grandi. Le cose a basso livello arrivano per ultime. Di solito, se hai ottimizzato adeguatamente il resto dello stack e c'\u00e8 ancora qualcosa di interessante, allora si tratta di basso livello. Ma come si inizia da un alto livello? Come si fa a sapere se hai fatto abbastanza lavoro a un livello elevato? Bene... non c'\u00e8 modo. Non ci sono ricette pronte. Devi capire il problema, decidere cosa intendi fare (per non fare passi non necessari in seguito) e solo allora puoi tirare fuori un profiler che possa dirti qualcosa di utile. A un certo punto, capisci da solo che ti sei liberato delle cose superflue ed \u00e8 arrivato il momento di affinare a un basso livello. Questo \u00e8 senza dubbio una forma d'arte. Molti fanno cose inutili, ma si muovono cos\u00ec velocemente che non hanno tempo per occuparsi delle prestazioni. Ma finch\u00e9 la questione non diventa urgente. Di solito, il 99% del tempo a nessuno interessa quello che faccio, fino al momento in cui non emerge un problema cruciale di cui a qualcuno importa. E l\u00ec tutti iniziano a chiederti: \u00abperch\u00e9 non ha funzionato perfettamente fin dall'inizio\u00bb. Insomma, c'\u00e8 sempre qualcosa da migliorare nelle performance. Ma il 99% del tempo non hai indizi! Stai solo cercando di far funzionare qualcosa e nel processo capisci cosa \u00e8 importante. Non si pu\u00f2 mai sapere in anticipo che quella parte debba essere perfetta, quindi, in sostanza, devi essere perfetto in tutto. E questo \u00e8 impossibile e non lo fai. Ci sono sempre un sacco di cose da riparare \u2013 ed \u00e8 assolutamente normale.<\/p>\n<p><\/p>\n<h1 id=\"kak-delat-bolshoy-refaktoring\">Come effettuare un grande refactoring<\/h1>\n<p><\/p>\n<p><strong>Andrey<\/strong>: Come lavorate sulle prestazioni? Si tratta di un problema trasversale. Ad esempio, vi \u00e8 mai capitato di affrontare questioni che sorgono a causa dell'intersezione di molte funzionalit\u00e0 gi\u00e0 esistenti?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Cerco di evitarlo. Se so che le prestazioni diventeranno un problema, ci rifletto prima di iniziare a codificare, soprattutto sulle strutture dati. Ma spesso scopri tutto questo molto pi\u00f9 tardi. E allora \u00e8 necessario ricorrere a misure estreme e fare ci\u00f2 che chiamo \u00abriscrivi e domina\u00bb: bisogna afferrare un pezzo abbastanza grande. Parte del codice dovr\u00e0 comunque essere riscritta a causa di problemi di prestazioni o per qualche altro motivo. Qualunque sia la ragione per cui si riscrive il codice, \u00e8 quasi sempre meglio riscrivere un pezzo pi\u00f9 grande piuttosto che uno pi\u00f9 piccolo. In quel momento tutti iniziano a tremare per la paura: \u00abOh no, non si pu\u00f2 toccare cos\u00ec tanto codice!\u00bb. Ma, di fatto, questo approccio funziona quasi sempre molto meglio. Bisogna affrontare subito un problema grande, tracciarne un grande cerchio attorno e dire: tutto ci\u00f2 che \u00e8 all'interno del cerchio lo riscriver\u00f2. Il confine \u00e8 molto pi\u00f9 piccolo rispetto al contenuto all'interno di esso che deve essere sostituito. E se una tale delimitazione dei confini permette di lavorare all'interno in modo perfetto \u2013 sei libero, fai quello che vuoi. Non appena hai compreso il problema, il processo di riscrittura diventa molto pi\u00f9 semplice, quindi afferra un grande pezzo!<br \/>\nAllo stesso tempo, quando fai un grande rifacimento e ti rendi conto che le prestazioni diventeranno un problema, puoi subito iniziare a preoccupartene. Di solito si traduce in cose semplici come \"non copiare i dati, gestisci i dati il pi\u00f9 semplicemente possibile, rendili pi\u00f9 piccoli\". Nei grandi rifacimenti, ci sono modi standard per migliorare le prestazioni. E questi girano quasi sempre attorno ai dati.<\/p>\n<p><\/p>\n<h1 id=\"model-stoimosti\">Modello di costo<\/h1>\n<p><\/p>\n<p><strong>Andrey<\/strong>: In uno dei podcast hai parlato dei modelli di costo in relazione alle prestazioni. Puoi spiegare cosa intendevi con questo?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Certo. Sono nato in un'epoca in cui le prestazioni del processore erano estremamente importanti. E quest'era sta tornando \u2013 il destino non \u00e8 privo di ironia. Ho iniziato a vivere ai tempi delle macchine a otto bit, il mio primo computer operava con 256 byte. Proprio byte. Tutto era molto piccolo. Dovevamo contare le istruzioni e, man mano che iniziavamo a progredire nelle pile dei linguaggi di programmazione, i linguaggi si occupavano di sempre di pi\u00f9. C'era l'Assembly, poi il Basic, poi il C, e il C si occupava di molteplici dettagli, come la distribuzione dei registri e la selezione delle istruzioni. Ma tutto era piuttosto chiaro e se creavo un puntatore a un'istanza di una variabile, avrei ottenuto un caricamento, e il costo di quell'istruzione era noto. L'hardware fornisce un numero noto di cicli macchina, quindi la velocit\u00e0 di esecuzione di diverse operazioni pu\u00f2 essere facilmente calcolata sommando tutte le istruzioni che intendi eseguire. Ogni compare\/test\/branch\/call\/load\/store poteva essere sommato e si poteva dire: ecco il tuo tempo di esecuzione. Quando lavori sull'ottimizzazione delle prestazioni, noterai sicuramente quali numeri corrispondono a piccoli cicli caldi.\u00a0<br \/>\nMa non appena inizi a utilizzare Java, Python e simili, ti allontani rapidamente dall'hardware di basso livello. Qual \u00e8 il costo della chiamata a un getter in Java? Se il JIT in HotSpot funziona correttamente, <noindex><a rel=\"nofollow\" href=\"https:\/\/wiki.openjdk.java.net\/display\/HotSpot\/Inlining\">inlaynato<\/a><\/noindex>, sar\u00e0 un caricamento, ma se non lo fa, sar\u00e0 una chiamata a funzione. Poich\u00e9 la chiamata \u00e8 all'interno di un ciclo caldo, annuller\u00e0 tutte le altre ottimizzazioni in quel ciclo. Pertanto, il costo reale sar\u00e0 molto maggiore. E cos\u00ec perdi immediatamente la capacit\u00e0 di guardare un pezzo di codice e comprendere perch\u00e9 dovremmo eseguirlo in termini di cicli di clock della CPU, memoria utilizzata e cache. Tutto questo diventa interessante solo se ci si immerge davvero nelle prestazioni.<br \/>\nCi troviamo ora in una situazione in cui la velocit\u00e0 dei processori non cresce quasi pi\u00f9 da un decennio. I vecchi tempi stanno tornando! Non puoi pi\u00f9 contare su buone prestazioni a thread singolo. Ma se improvvisamente ti dedichi al calcolo parallelo, \u00e8 incredibilmente complicato e tutti ti guardano come se fossi James Bond. Qui il decuplicare \u00e8 qualcosa che si verifica generalmente nei casi in cui qualcuno ha perso di vista qualcosa. La parallelit\u00e0 richiede molto lavoro. Per ottenere quel decuplimento, devi capire il modello di costo. Cosa e quanto costa. E per fare ci\u00f2 devi capire come il linguaggio interagisce con l'hardware sottostante.<br \/>\nMartin Thompson ha scelto una parola fantastica per il suo blog <noindex><a rel=\"nofollow\" href=\"https:\/\/mechanical-sympathy.blogspot.com\/\">Simpatia Meccanica<\/a><\/noindex>! \u00c8 fondamentale capire cosa deve fare l'hardware, come lo far\u00e0 esattamente e perch\u00e9 lo fa. Utilizzando queste informazioni, \u00e8 piuttosto semplice iniziare a contare le istruzioni e scoprire dove sta andando a finire il tempo di esecuzione. Se non hai una preparazione adeguata, stai semplicemente cercando un gatto nero in una stanza buia. Vedo continuamente persone che ottimizzano le prestazioni senza avere la minima idea di cosa stiano realmente facendo. Si sforzano molto e non riescono a fare progressi significativi. Quando prendo lo stesso pezzo di codice, realizzo un paio di piccole modifiche e ottengo un miglioramento di cinque o dieci volte, loro dicono: beh, non \u00e8 giusto, sapevamo gi\u00e0 che eri migliore. Sorprendente. Di cosa stavo parlando... il modello dei costi riguarda ci\u00f2 che scrivi in codice e quanto velocemente funziona in media nel contesto generale.<\/p>\n<p><\/p>\n<p><strong>Andrey<\/strong>: E come si pu\u00f2 mantenere in mente un tale volume? Si ottiene attraverso molta esperienza, giusto? Dove si acquisisce tale esperienza?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Beh, ho acquisito la mia esperienza in modo piuttosto complicato. Ho programmato in Assembler nei tempi in cui era possibile comprendere ogni singola istruzione. Pu\u00f2 sembrare sciocco, ma da allora ho nella mia testa, nella mia memoria, rimasto per sempre un insieme di istruzioni Z80. Non ricordo i nomi delle persone gi\u00e0 un minuto dopo aver parlato, ma ricordo il codice scritto 40 anni fa. \u00c8 divertente, sembra un sindrome di \"<noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/%D0%A1%D0%B8%D0%BD%D0%B4%D1%80%D0%BE%D0%BC_%D1%81%D0%B0%D0%B2%D0%B0%D0%BD%D1%82%D0%B0\">scienziato pazzo<\/a><\/noindex>\u00bb.<\/p>\n<p><\/p>\n<h1 id=\"obuchenie-nizkourovnevym-optimizaciyam\">Apprendimento delle ottimizzazioni di basso livello<\/h1>\n<p><\/p>\n<p><strong>Andrey<\/strong>: C'\u00e8 un modo pi\u00f9 semplice per entrare nel settore?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: S\u00ec e no. L'hardware che usiamo tutti non \u00e8 cambiato molto nel tempo. Tutti usano x86, ad eccezione degli smartphone basati su Arm. Se non ti occupi di qualche embed hardcore, hai tutto lo stesso. Bene, procediamo. Anche le istruzioni non sono cambiate da secoli. Bisogna andare e scrivere qualcosa in Assembly. Poco, ma abbastanza per iniziare a capire. Tu stai sorridendo, ma io lo dico seriamente. Devi capire la corrispondenza tra linguaggio e hardware. Dopo di che, devi scrivere un po' e realizzare un piccolo compilatore giocattolo per un piccolo linguaggio giocattolo. 'Giocattolo' significa che deve essere fatto in un tempo ragionevole. Pu\u00f2 essere super semplice, ma deve generare istruzioni. L'atto di generazione delle istruzioni permetter\u00e0 di comprendere il modello di costo per il ponte tra il codice di alto livello, che tutti scrivono, e il codice macchina, che viene eseguito sull'hardware. Questa corrispondenza si fisser\u00e0 nella mente nel momento in cui scrivi il compilatore. Anche il compilatore pi\u00f9 semplice. Dopo di ci\u00f2, puoi iniziare a guardare a Java e scoprire che ha un divario semantico molto pi\u00f9 profondo, e costruire ponti sopra di esso \u00e8 molto pi\u00f9 complicato. In Java \u00e8 molto pi\u00f9 difficile capire se il nostro ponte \u00e8 buono o cattivo, cosa lo far\u00e0 crollare e cosa no. Ma hai bisogno di un punto di partenza, quando guardi il codice e comprendi: 'Ecco, questo getter dovrebbe essere inlined ogni volta'. E poi scopri che a volte \u00e8 cos\u00ec, tranne nei casi in cui il metodo diventa troppo grande e JIT inizia a inlineare tutto. La performance di questi punti pu\u00f2 essere prevista istantaneamente. Di solito i getter funzionano bene, ma poi guardi ai grandi cicli caldi e realizzi che ci sono chiamate di funzione che non si capisce cosa facciano. \u00c8 l\u00ec che risiede il problema con l'uso generalizzato dei getter, il motivo per cui non vengono inlinati: non \u00e8 chiaro se sia un getter. Se hai una base di codice super piccola, puoi semplicemente memorizzarla e poi dire: ecco un getter, ecco un setter. In una grande base di codice, ogni funzione vive la propria storia, che sostanzialmente nessuno conosce. Il profiler dice che abbiamo perso il 24% del tempo su un ciclo e per capire cosa fa quel ciclo bisogna analizzare ogni funzione all'interno. Impossibile comprenderlo senza studiare la funzione, e questo rallenta notevolmente il processo di comprensione. Ecco perch\u00e9 non utilizzo getter e setter, ho raggiunto un nuovo livello!<br \/>\nDove posso trovare un modello di costo? Beh, certo, si pu\u00f2 leggere qualcosa... Ma penso che il modo migliore sia agire. Creare un piccolo compilatore sar\u00e0 il miglior modo per comprendere il modello di costo e assimilarlo. Un piccolo compilatore che sia utile per programmare un microonde \u00e8 un compito da principianti. Beh, voglio dire, se hai gi\u00e0 abilit\u00e0 di programmazione, dovrebbero essere sufficienti. Tutte queste cose come analizzare una stringa che sar\u00e0 una qualche espressione algebraica, estrarre da essa le istruzioni per le operazioni matematiche nell'ordine corretto, prendere i valori corretti dai registri \u2014 tutto questo si fa facilmente. E mentre lo fai, si imprime nella tua mente. Penso che tutti sappiano cosa fa un compilatore. E questo dar\u00e0 familiarit\u00e0 con il modello di costo.<\/p>\n<p><\/p>\n<h1 id=\"prakticheskie-primery-uluchsheniya-proizvoditelnosti\">Esempi pratici di miglioramento delle prestazioni<\/h1>\n<p><\/p>\n<p><strong>Andrey<\/strong>: A cosa altro bisogna prestare attenzione quando si lavora sulle prestazioni?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Strutture dati. A proposito, non ho tenuto queste lezioni da un po'... <noindex>Rocket School<\/noindex>. \u00c8 stato divertente, ma richiedeva cos\u00ec tanto impegno, e ho una vita anche io! Comunque, durante una delle lezioni pi\u00f9 grandi e interessanti, \"Dove va il vostro performance\", ho dato agli studenti un esempio: due e mezzo gigabyte di dati fintech letti da un file CSV, e poi bisognava calcolare il numero di prodotti venduti. Dati di mercato standard. Pacchetti UDP trasformati in formato testuale, risalenti agli anni '70. Chicago Mercantile Exchange \u2013 varie cose come petrolio, mais, fagioli di soia e simili. Bisognava contare questi prodotti, il numero di operazioni, il volume medio di movimento di denaro e beni, ecc. Questa \u00e8 una matematica commerciale abbastanza semplice: trovare il codice prodotto (che sono 1-2 caratteri in una tabella hash), ottenere il totale, aggiungerlo a uno dei set di operazioni, aggiungere il volume, aggiungere il costo e un paio di altre cose. Matematica molto semplice. L'implementazione giocattolo era molto diretta: tutto era nel file, leggevo il file e lo scorrevo, separando le singole registrazioni in righe Java, cercando le cose necessarie e sommando secondo la matematica descritta sopra. E questo funziona con una certa velocit\u00e0. <\/p>\n<p><\/p>\n<p>Con questo approccio \u00e8 evidente cosa sta succedendo e i calcoli paralleli non aiuteranno, giusto? Si scopre che un aumento delle prestazioni di cinque volte pu\u00f2 essere ottenuto semplicemente scegliendo le strutture dati giuste. E questo sorprende anche i programmatori esperti! Nel mio caso specifico, il focus era sul fatto che non si dovrebbe effettuare allocazioni di memoria in un ciclo caldo. Beh, non \u00e8 tutta la verit\u00e0, ma in generale \u2013 non si dovrebbe allocare \"una volta ogni X\", quando X \u00e8 sufficientemente grande. Quando X \u00e8 di due e mezzo gigabyte, non bisognerebbe allocare nulla \"una volta per lettera\", o \"una volta per riga\", o \"una volta per campo\", niente di simile. \u00c8 proprio questo a richiedere tempo. Come funziona tutto ci\u00f2? Immagina che io faccia una chiamata <code>String.split()<\/code> o <code>BufferedReader.readLine()<\/code>. <code>Readline<\/code> crea una stringa da un insieme di byte ricevuti dalla rete, una volta per ogni stringa, per ciascuna delle centinaia di milioni di stringhe. Prendo questa stringa, la analizzo e la scarto. Perch\u00e9 la scarto? Beh, l'ho gi\u00e0 elaborata, quindi basta. Perci\u00f2, per ogni byte letto da questi 2.7G, verranno scritti due caratteri nella stringa, il che significa gi\u00e0 5.4G, e non mi servono pi\u00f9, quindi vengono scartati. Se guardiamo alla larghezza di banda della memoria, carichiamo 2.7G, che attraversano la memoria e il bus di memoria nel processore, e poi il doppio viene spedito nella stringa in memoria, e tutto questo viene elaborato mentre si crea ogni nuova stringa. Ma devo leggerla, l'hardware la legge, anche se poi tutto viene ri-elaborato. E devo scriverla, perch\u00e9 ho creato una stringa e le cache sono sovraccariche \u2013 la cache non pu\u00f2 contenere 2.7G. Dunque, per ogni byte letto, leggo altri due byte aggiuntivi e scrivo due byte aggiuntivi, e alla fine hanno un rapporto 4:1 \u2013 in questo rapporto sprechiamo irresponsabilmente la larghezza di banda della memoria. Ma poi si scopre che se faccio <code>String.split()<\/code> \u2013 non \u00e8 la prima volta che lo faccio, l\u00ec dentro potrebbero esserci altre 6-7 aree. Pertanto, il codice di lettura CSV classico con successivo parsing delle righe porta a una perdita di banda di memoria di circa 14:1 rispetto a quanto vorreste realmente avere. Se eliminate queste allocazioni, potete ottenere un incremento delle prestazioni di cinque volte. <\/p>\n<p><\/p>\n<p>E non \u00e8 che sia molto difficile. Se guardate il codice nella giusta prospettiva, tutto diventa piuttosto semplice, non appena comprendete il problema. Non dovete smettere di allocare memoria: il problema \u00e8 solo che allocando qualcosa subito muore e nel processo brucia una risorsa importante, che in questo caso \u00e8 la banda di memoria. E tutto questo si traduce in un calo delle prestazioni. Su x86 di solito bisogna bruciare attivamente i cicli del processore, mentre qui avete esaurito tutta la memoria molto prima. La soluzione \u00e8 ridurre il numero di allocazioni.\u00a0<br \/>\nUn'altra parte del problema \u00e8 che, se esegui il profiler quando la banda di memoria \u00e8 esaurita, proprio nel momento in cui ci\u00f2 accade, di solito aspetti il ritorno della cache, poich\u00e9 \u00e8 piena di spazzatura che hai appena prodotto, con tutte queste righe. Pertanto, ogni operazione di load o store diventa lenta, poich\u00e9 portano a miss nel cache: l'intera cache diventa lenta, in attesa che la spazzatura venga rimossa. Cos\u00ec, il profiler mostrer\u00e0 solo un rumore casuale caldo, sfumato lungo l'intero ciclo - non ci sar\u00e0 alcuna singola istruzione calda o luogo nel codice. Solo rumore. E se guardi i cicli di GC, saranno tutti nella Young Generation e super veloci - microsecondi o millisecondi al massimo. Infatti, tutta questa memoria muore all'istante. Allocando miliardi di gigabyte, li taglia e li taglia e di nuovo li taglia. Tutto ci\u00f2 avviene molto rapidamente. Risultato: ci sono cicli di GC economici, un rumore caldo lungo tutto il ciclo, ma noi vogliamo ottenere un'accelerazione di 5 volte. In quel momento, qualcosa deve scattare nella tua mente e farti chiedere: \u00abPerch\u00e9?!\u00bb. Un overflow della banda di memoria non viene visualizzato nel classic debugger, \u00e8 necessario eseguire il debugger dei contatori hardware delle prestazioni e vederlo direttamente. Non direttamente, \u00e8 possibile sospettarlo da questi tre sintomi. Il terzo sintomo \u00e8 quando guardi cosa hai allocato, chiedi al profiler e lui risponde: \u00abHai creato un miliardo di righe, ma il GC ha lavorato gratuitamente\u00bb. Non appena questo accade, capisci che hai prodotto troppi oggetti e hai bruciato tutta la banda di memoria. C'\u00e8 un modo per capirlo, ma non \u00e8 ovvio.\u00a0<\/p>\n<p><\/p>\n<p>Problema nella struttura dei dati: la struttura nuda che sta dietro a tutto ci\u00f2 \u00e8 troppo grande, occupa 2.7G di spazio su disco, quindi creare una copia di questa cosa \u00e8 molto indesiderabile \u2013 vorresti caricarla direttamente dal buffer di rete nei registri, per non dover leggere e scrivere nel buffer ripetutamente. Sfortunatamente, Java non ti fornisce per impostazione predefinita una tale libreria con il JDK. Ma \u00e8 in effetti banale, giusto? Fondamentalmente, si tratta di 5-10 righe di codice per implementare il proprio caricatore di righe bufferizzato, che replica il comportamento della classe String, fungendo da involucro attorno al buffer di byte sottostante. Il risultato \u00e8 che lavori quasi come con le stringhe, ma in realt\u00e0 stai muovendo puntatori sul buffer, e i byte grezzi non vengono copiati da nessuna parte, riutilizzando quindi gli stessi buffer pi\u00f9 e pi\u00f9 volte, mentre il sistema operativo \u00e8 lieto di gestire compiti per cui \u00e8 stato progettato, come la doppia bufferizzazione nascosta di questi buffer. A proposito, capite che quando si lavora con la GC si garantisce che ogni allocazione di memoria non sar\u00e0 visibile alla CPU dopo l'ultimo ciclo di GC? Quindi, tutto questo non pu\u00f2 essere nella cache, e si verifica un colpo sicuro del 100%. Quando si utilizza un puntatore, su x86 sottrarre un registro dalla memoria richiede 1-2 cicli, e non appena ci\u00f2 accade, paghi, paghi, paghi, perch\u00e9 la memoria \u00e8 tutta <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Cache_inclusion_policy\">NOVE cache<\/a><\/noindex> \u2013 e questo rappresenta il costo di allocazione della memoria. Il vero costo.<\/p>\n<p><\/p>\n<p>In altre parole, le strutture dati sono tra le pi\u00f9 difficili da modificare. Una volta che ci si rende conto di aver scelto una struttura dati sbagliata che comprometter\u00e0 le performance future, di solito \u00e8 necessario fare un lavoro notevole per apportare modifiche, e se non lo si fa, le cose andranno solo peggio. \u00c8 fondamentale pensare alle strutture dati. Il vero costo risiede in solide strutture dati, scelte in modo superficiale con la mentalit\u00e0 \"ho copiato la struttura dati X nella struttura dati Y perch\u00e9 Y mi piace esteticamente di pi\u00f9\". Tuttavia, l'operazione di copia (che appare economica) consuma effettivamente larghezza di banda in memoria, ed \u00e8 qui che si nasconde tutto il tempo di esecuzione perso. Se ho una gigantesca stringa JSON e voglio trasformarla in un albero DOM strutturato di POJO o simili, l'operazione di parsing di quella stringa e la creazione dei POJO, seguita da un nuovo accesso ai POJO, comporter\u00e0 un costo aggiuntivo \u2013 \u00e8 una questione costosa. A meno che non si acceda ai POJO molto pi\u00f9 frequentemente che alla stringa. In alternativa, si potrebbe semplicemente decodificare la stringa ed estrarre solo ci\u00f2 che serve, senza convertirla in alcun POJO. Se tutto ci\u00f2 accade in un contesto in cui \u00e8 richiesta la massima performance, niente POJO \u2013 bisogna in qualche modo lavorare direttamente sulla stringa.<\/p>\n<p><\/p>\n<h1 id=\"zachem-sozdavat-svoy-yazyk-programmirovaniya\">Perch\u00e9 creare un proprio linguaggio di programmazione<\/h1>\n<p><\/p>\n<p><strong>Andrey<\/strong>: Hai detto che per comprendere il modello di costo, bisogna scrivere un piccolo linguaggio\u2026<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Non un linguaggio, ma un compilatore. Un linguaggio e un compilatore sono due cose diverse. La differenza principale \u00e8 nella propria testa.\u00a0<\/p>\n<p><\/p>\n<p><strong>Andrey<\/strong>: A proposito, per quanto ne so, stai sperimentando nella creazione di linguaggi propri. Perch\u00e9?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Perch\u00e9 posso! Sono in parte in pensione, quindi questo \u00e8 il mio hobby. Ho realizzato i linguaggi di qualcuno per tutta la vita. Ho anche lavorato molto sullo stile di codifica. Inoltre, vedo problemi in altri linguaggi. Vedo che ci sono modi migliori per fare cose familiari. E ne farei uso. Sono stanco di vedere problemi in me stesso, in Java, in Python, in qualsiasi altro linguaggio. Attualmente scrivo in React Native, JavaScript ed Elm come hobby, che non riguarda la pensione, ma un lavoro attivo. Scrivo anche in Python e probabilmente continuer\u00f2 a lavorare sull'apprendimento automatico per i backend Java. Ci sono molti linguaggi popolari e tutti hanno caratteristiche interessanti. Ognuno \u00e8 valido per qualcosa di unico e si possono provare a raccogliere tutte queste caratteristiche insieme. Quindi, sto studiando cose interessanti per me, il comportamento del linguaggio, cercando di inventare una semantica ragionevole. E finora ci riesco! Attualmente, sto affrontando la semantica della memoria, perch\u00e9 voglio averla come in C e Java, ottenendo un modello di memoria forte e una semantica della memoria per i carichi e gli archivi. Allo stesso tempo, voglio avere un'inferenza dei tipi automatica come in Haskell. Ecco, sto cercando di mescolare l'inferenza dei tipi simile a Haskell con la memoria che funge come in C e Java. Su questo ho lavorato negli ultimi 2-3 mesi, ad esempio.<\/p>\n<p><\/p>\n<p><strong>Andrey<\/strong>: Se stai creando un linguaggio che prende i migliori aspetti da altri linguaggi, hai mai pensato che qualcuno possa fare il contrario: prendere le tue idee e usarle?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: \u00c8 proprio cos\u00ec che nascono nuovi linguaggi! Perch\u00e9 Java \u00e8 simile a C? Perch\u00e9 C aveva una sintassi valida che tutti comprendevano e Java si \u00e8 ispirato a quella sintassi, aggiungendo sicurezza dei tipi, controlli sui limiti degli array, GC, e hanno migliorato alcune cose di C. Hanno aggiunto le proprie. Ma si sono ispirati piuttosto fortemente, giusto? Tutti si poggiano sulle spalle dei giganti che sono venuti prima di loro, ed \u00e8 proprio cos\u00ec che si fa progresso.<\/p>\n<p><\/p>\n<p><strong>Andrey<\/strong>: Da quello che ho capito, il tuo linguaggio sar\u00e0 sicuro per quanto riguarda l'uso della memoria. Hai pensato di implementare qualcosa di simile al borrow checker di Rust? L'hai esaminato, che ne pensi?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Beh, scrivo in C da un'eternit\u00e0, con tutte queste malloc e free, e gestisco manualmente il ciclo di vita. Sapete, il 90-95% del ciclo di vita gestito manualmente ha una struttura simile. Ed \u00e8 davvero, davvero doloroso occuparsi di tutto questo manualmente. Vorrei che il compilatore dicesse semplicemente cosa succede e quali risultati ottieni con le tue azioni. Per alcune cose, il borrow checker fa giusto questo di default. Inoltre, dovrebbe automaticamente fornire informazioni, capire tutto e non gravarmi nemmeno del dover esporre tale comprensione. Dovrebbe almeno eseguire un'analisi di escape locale, e solo se non riesce a farlo, allora dovrei aggiungere annotazioni di tipo che descrivano il ciclo di vita \u2013 e uno schema del genere \u00e8 molto pi\u00f9 complesso del borrow checker, o di qualsiasi altro verificatore di memoria esistente. La scelta tra \u00abtutto va bene\u00bb e \u00abnon ho capito nulla\u00bb \u2014 no, dovrebbe esserci qualcosa di meglio.\u00a0<br \/>\nQuindi, come persona che ha scritto molto codice in C, ritengo che avere supporto per la gestione automatica del ciclo di vita sia fondamentale. Inoltre, sono stanco di quanto Java utilizzi memoria e il problema principale \u00e8 il garbage collector. Quando si richiede memoria in Java, non si riottiene la memoria che era locale nell'ultimo ciclo di garbage collection. Nei linguaggi con una gestione della memoria pi\u00f9 precisa non \u00e8 cos\u00ec. Se chiami malloc, ottieni immediatamente la memoria che \u00e8 stata appena utilizzata. Di solito, si fanno delle operazioni temporanee con la memoria e poi si restituisce immediatamente. E viene rapidamente reintegrata nel pool di malloc, e il successivo ciclo di malloc la riutilizza. Pertanto, l'uso reale della memoria \u00e8 ridotto al set di oggetti vivi in un dato momento, pi\u00f9 le perdite. E se non ci sono perdite del tutto irricevibili, gran parte della memoria rimane nei cache e nel processore, e questo funziona rapidamente. Ma richiede molta gestione manuale della memoria tramite malloc e free, chiamati nell'ordine corretto, nel posto giusto. Rust pu\u00f2 gestire correttamente tutto questo da solo e in molti casi offre anche prestazioni superiori, poich\u00e9 l'uso della memoria si restringe solo ai calcoli attuali, a differenza dell'attesa del prossimo ciclo di gc che liberer\u00e0 la memoria. In fin dei conti, abbiamo ottenuto un modo molto interessante per migliorare le prestazioni. E piuttosto potente \u2013 nel senso che mi sono occupato di simili questioni nell'elaborazione dei dati per il fintech, e ci\u00f2 ha permesso di ottenere un'accelerazione fino a cinque volte. Questo \u00e8 un notevole incremento, soprattutto in un mondo in cui i processori non diventano pi\u00f9 veloci, mentre continuiamo a aspettarci miglioramenti.<\/p>\n<p><\/p>\n<h1 id=\"karera-performans-inzhenera\">Carriera di ingegnere delle prestazioni<\/h1>\n<p><\/p>\n<p><strong>Andrey<\/strong>: Vorrei anche chiederti della carriera in generale. Sei diventato famoso grazie al tuo lavoro su JIT in HotSpot, per poi passare ad Azul \u2013 che \u00e8 anche un\u2019azienda JVM. Ma l\u00ec ti sei occupato pi\u00f9 di hardware che di software. Poi, all'improvviso, ti sei spostato su Big Data e Machine Learning, e successivamente su rilevazione delle frodi. Com'\u00e8 successo? Sono ambiti di sviluppo molto diversi.<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Sono un programmatore da molto tempo e ho avuto esperienze in molti ambiti diversi. Quando le persone dicono: \u00abOh, sei quello che ha fatto JIT per Java!\u00bb, trovo sempre che sia divertente. Prima di questo, mi sono occupato di un clone di PostScript, il linguaggio che Apple utilizzava una volta per le sue stampanti laser. E prima ancora, ho realizzato un'implementazione del linguaggio Forth. Penso che il tema centrale per me sia lo sviluppo di strumenti. Ho passato la mia vita a creare strumenti che aiutano altre persone a scrivere i loro fantastici programmi. Ma mi sono anche dedicato allo sviluppo di sistemi operativi, driver, debugger a livello di kernel, e linguaggi per l'implementazione di sistemi operativi, che iniziavano in modo semplice ma diventavano sempre pi\u00f9 complessi. Ma il focus principale rimane, comunque, lo sviluppo di strumenti. Un ampio periodo della mia vita \u00e8 trascorso tra Azul e Sun, ed era tutto incentrato su Java. Ma quando mi sono dedicato a Big Data e Machine Learning, ho di nuovo indossato il mio cappello da festa e ho detto: \u00abOh, adesso abbiamo un problema non banale, ed \u00e8 in corso un sacco di cose interessanti e persone che stanno facendo qualcosa\u00bb. \u00c8 un ottimo percorso di sviluppo da seguire. <\/p>\n<p><\/p>\n<p>S\u00ec, amo molto il calcolo distribuito. Il mio primo lavoro \u00e8 stato durante gli studi, in C, su un progetto pubblicitario. Era un calcolo distribuito su chip Zilog Z80, che raccoglievano dati per il riconoscimento ottico analogo di testi, realizzato da un vero analizzatore analogico. Era un argomento interessante e decisamente originale. Ma c'erano dei problemi, alcune parti non venivano riconosciute correttamente, quindi bisognava estrarre l'immagine e mostrarla a qualcuno che gi\u00e0 sapeva leggere, il quale comunicava cosa c'era scritto. Cos\u00ec c'erano lavori con dati, e questi lavori avevano il loro linguaggio. C'era un backend che gestiva tutto questo \u2013 Z80 funzionanti in parallelo con terminali vt100 in uso \u2013 uno per persona, e c'era un modello di programmazione parallela su Z80. Una certa porzione di memoria comune che veniva condivisa da tutti gli Z80 all'interno di una configurazione a stella; sia il backplane che met\u00e0 della RAM erano condivisi all'interno della rete, mentre l'altra met\u00e0 era privata o utilizzata per altro. Una sistema distribuito complesso e significante con memoria condivisa\u2026 semi-condivisa. Quando \u00e8 stato\u2026 Non riesco pi\u00f9 a ricordare, circa a met\u00e0 degli anni '80. \u00c8 passato un bel po' di tempo.\u00a0<br \/>\nS\u00ec, consideriamo che 30 anni siano abbastanza tempo. Le sfide legate al calcolo distribuito esistono da molto, e le persone hanno combattuto con esse da tempo, <noindex><a rel=\"nofollow\" href=\"https:\/\/ru.wikipedia.org\/wiki\/Beowulf_(%D0%BA%D0%BB%D0%B0%D1%81%D1%82%D0%B5%D1%80)\">Beowulf<\/a><\/noindex>-cluster. Questi cluster sembrano\u2026 Ad esempio: c'\u00e8 Ethernet e il tuo veloce x86 \u00e8 collegato a questo Ethernet, e ora desideri ottenere una memoria condivisa fittizia, perch\u00e9 allora nessuno poteva dedicarsi alla programmazione del calcolo distribuito, era troppo complicato e quindi esisteva una memoria condivisa fittizia con protezione delle pagine di memoria su x86. Se scrivevi in questa pagina, informavamo gli altri processori che, se accedevano alla stessa memoria condivisa, avrebbero dovuto ricaricarla da te, ed \u00e8 cos\u00ec che \u00e8 nato un protocollo per supportare la coerenza della cache e il software per questo. Un concetto interessante. Il vero problema, ovviamente, era un altro. Tutto questo funzionava, ma si evidenziavano rapidamente problemi di performance, poich\u00e9 nessuno comprendeva il modello di performance a un livello sufficientemente buono: quali fossero i modelli di accesso alla memoria, come evitare che i nodi pingassero all'infinito tra di loro, e cos\u00ec via. <\/p>\n<p><\/p>\n<p>In H2O ho concepito quanto segue: gli stessi sviluppatori sono responsabili di identificare dove si nasconde il parallelismo e dove non c'\u00e8. Ho ideato un modello di codifica che rende la scrittura di codice ad alte prestazioni facile e semplice. Scrivere codice poco performante, invece, \u00e8 difficile e risulta poco gradevole. \u00c8 necessario fare uno sforzo significativo per scrivere codice lento, dovendo ricorrere a metodi non convenzionali. Il codice lento \u00e8 evidente a colpo d'occhio. Di conseguenza, di solito si scrive codice che funziona rapidamente, ma bisogna capire come gestire la memoria condivisa. Tutto \u00e8 legato a grandi array e il comportamento \u00e8 simile a quello di grandi array non volatili in Java parallelo. In altre parole, immaginate che due thread scrivano in un array parallelo: uno di essi ha successo, mentre l'altro, ovviamente, no, e non sapete chi \u00e8 chi. Se non sono volatili, l'ordine pu\u00f2 essere qualsiasi \u2013 e questo funziona davvero bene. Le persone si prendono cura dell'ordine delle operazioni, posizionano correttamente volatile e, nei posti giusti, si aspettano problemi di prestazioni legati alla memoria. Altrimenti, scriverebbero semplicemente codice in cicli da 1 a N, dove N \u00e8 qualche trilione, sperando che tutti i casi complessi diventino automaticamente paralleli \u2013 e l\u00ec non funziona. Ma in H2O non \u00e8 n\u00e9 Java n\u00e9 Scala, si pu\u00f2 considerare come \u00abJava meno meno\u00bb, se si desidera. \u00c8 uno stile di programmazione molto chiaro e assomiglia a scrivere codice semplice in C o Java con cicli e array. Ma nel frattempo, la memoria pu\u00f2 essere gestita in terabyte. Uso ancora H2O. Di tanto in tanto lo utilizzo in diversi progetti \u2013 e resta ancora la cosa pi\u00f9 veloce, di decine di volte superiore ai concorrenti. Se state lavorando con Big Data e dati a colonne, \u00e8 molto difficile superare H2O.<\/p>\n<p><\/p>\n<h1 id=\"tehnicheskie-chellenzhi\">Sfide tecniche<\/h1>\n<p><\/p>\n<p><strong>Andrey<\/strong>: Qual \u00e8 stata la sfida pi\u00f9 grande della tua carriera?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Stiamo discutendo la parte tecnica o quella non tecnica della questione? Direi che le sfide pi\u00f9 grandi sono quelle non tecniche.\u00a0<br \/>\nPer quanto riguarda le sfide tecniche, le ho semplicemente vinte. Non so nemmeno quale sia stata la pi\u00f9 grande, ma ci sono stati diversi problemi abbastanza interessanti che hanno richiesto molto tempo e sforzo mentale. Quando sono andato a Sun, ero sicuro di poter realizzare un compilatore veloce, ma molti senior mi dicevano che non ce l'avrei mai fatta. Eppure ho intrapreso questa strada, scrivendo un compilatore fino all'allocatore di registri, e sorprendentemente veloce. Era veloce quanto un moderno C1, ma all'epoca l'allocatore era molto pi\u00f9 lento; ripensandoci, era un problema di una grande struttura dati. Ne avevo bisogno per scrivere un allocatore grafico di registri e non capivo la diatriba tra l'espressivit\u00e0 del codice e la velocit\u00e0 che esisteva all'epoca e che era molto importante. Si \u00e8 rivelato che la struttura dati superava generalmente la dimensione della cache sui sistemi x86 di quel tempo e quindi, se inizialmente avevo ipotizzato che l'allocatore di registri avrebbe assorbito il 5-10% dell'intero tempo di JIT, in realt\u00e0 si \u00e8 rivelato essere il 50%. <\/p>\n<p><\/p>\n<p>Il tempo passava, il compilatore diventava sempre pi\u00f9 chiaro e performante, smetteva di generare codice inguardabile in un numero sempre maggiore di casi, e le prestazioni cominciavano ad assomigliare a quelle del compilatore C. A meno che, ovviamente, tu non stia scrivendo qualche schifezza che neanche C riesce ad accelerare. Se scrivi codice come se fossi su C, ottieni anche performance simili a C in un numero maggiore di casi. E pi\u00f9 andavo avanti, pi\u00f9 frequentemente il codice risultava asintoticamente in linea con il livello C; l'allocatore di registri iniziava a sembrare qualcosa di compiuto... indipendentemente dal fatto che il tuo codice fosse veloce o lento. Continuavo a lavorare sull'allocatore per ottenere allocazioni migliori. Diventava sempre pi\u00f9 lento, ma offriva prestazioni sempre pi\u00f9 eccellenti nei casi in cui nessun altro riusciva. Potevo immergermi nell'allocatore di registri, dedicare un mese di lavoro, e improvvisamente tutto il codice iniziava a girare il 5% pi\u00f9 velocemente. Questo accadeva ripetutamente e l'allocatore di registri diventava una sorta di opera d'arte: tutti lo amavano o lo odiavano, e le persone dell'accademia si ponevano domande su \"perch\u00e9 tutto venisse fatto in questo modo\", perch\u00e9 non... <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Register_allocation#Linear_Scan\">scansione lineare<\/a><\/noindex>, e qual \u00e8 la differenza. La risposta \u00e8 sempre la stessa: un allocatore basato su grafi colorati pi\u00f9 un'attenta gestione del codice buffer \u00e8 uguale a uno strumento vincente, la migliore combinazione che nessuno pu\u00f2 battere. Ed \u00e8 piuttosto un aspetto non ovvio. Tutto il resto che fa il compilatore \u00e8 piuttosto studiato, anche se portato a un livello di arte. Ho sempre realizzato cose che dovevano trasformare il compilatore in un'opera d'arte. Ma nulla di tutto ci\u00f2 era eccezionale \u2014 tranne per l'allocatore di registri. Il trucco \u00e8 che bisogna fare attenzione <noindex><a rel=\"nofollow\" href=\"https:\/\/en.wikipedia.org\/wiki\/Register_allocation\">a tagliare<\/a><\/noindex> sotto carico e, se ci\u00f2 accade (posso spiegare meglio, se ti interessa), significa che si pu\u00f2 inlinare in modo pi\u00f9 aggressivo, senza il rischio di superare il punto di rottura nella curva delle prestazioni. A quei tempi c'erano molte compilazioni complete, ricche di accessori e suoni, che avevano allocatori di registri, ma nessuno \u00e8 riuscito pi\u00f9 a fare cos\u00ec. <\/p>\n<p><\/p>\n<p>Il problema \u00e8 che quando aggiungi metodi suscettibili di inlining, aumentando sempre di pi\u00f9 l'area di inlining, l'insieme dei valori utilizzati supera istantaneamente il numero di registri disponibili, e si deve eliminare. Il livello critico di solito si raggiunge quando l'allocatore cede, e uno dei buoni candidati per lo spillage vale pi\u00f9 di un altro, ed \u00e8 l\u00ec che si sprecano valori completamente assurdi. Il valore dell'inlining sta nel fatto che perdi parte dell'overhead, l'overhead della chiamata e del salvataggio; puoi vedere i valori all'interno e puoi ottimizzarli ulteriormente. Il costo dell'inlining \u00e8 che si genera un gran numero di valori vivi, e se il tuo allocatore di registri spills pi\u00f9 di quanto necessario, perdi subito. Pertanto, la maggior parte degli allocatori ha un problema: quando l'inlining supera una certa soglia, inizia a spillarsi tutto e la performance pu\u00f2 andare in malora. Coloro che sviluppano compilatori aggiungono alcune euristiche: per esempio, per fermare l'inlining una volta raggiunta una certa dimensione sufficiente, poich\u00e9 le allocazioni rovinerebbero tutto. Cos\u00ec si forma una rottura nel grafico delle performance: in inline, inline, le performance crescono lentamente \u2014 e poi boom! \u2014 crollano a picco, perch\u00e9 hai inline troppa roba. Cos\u00ec \u00e8 sempre andata fino all'arrivo di Java. Java richiede molto pi\u00f9 inlining, quindi ho dovuto rendere il mio allocatore molto pi\u00f9 aggressivo, affinch\u00e9 si mantenesse su un buon livello e non scendesse; e se hai inline troppo, inizia a spillare, ma c'\u00e8 comunque un momento in cui non ci sono pi\u00f9 spillaggi. Questa \u00e8 un'osservazione interessante ed \u00e8 arrivata a me da un'illuminazione, non ovvia, ma ben ripagata. Ho iniziato a fare inlining aggressivo e questo mi ha portato in posizioni in cui le performance di Java e C sono affiancate. Sono davvero vicine: posso scrivere codice in Java che risulta significativamente pi\u00f9 veloce del codice C e simili, ma in media, nella grande visione delle cose, sono grossolanamente comparabili. Credo che parte di questo merito vada all'allocatore di registri, che mi consente di fare inlining in modo praticamente a tappeto. In effetti, faccio inlining per tutto ci\u00f2 che vedo. La questione \u00e8 se l'allocatore funzioni bene, e se il codice risultante sia ragionevolmente funzionante. Questa \u00e8 stata una grande sfida: capire tutto questo e farlo funzionare.<\/p>\n<p><\/p>\n<h1 id=\"nemnogo-pro-allokaciyu-registrov-i-mnogoyadernost\">Un po' su allocazione dei registri e multithreading<\/h1>\n<p><\/p>\n<p><strong>Vladimir<\/strong>: Problemi come l'allocazione dei registri sembrano un tema infinito. \u00c8 interessante sapere se qualche idea sembrava promettente, ma poi \u00e8 fallita nella pratica?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Certo! L'allocazione dei registri \u00e8 un'area in cui, per risolvere un problema NP-completo, cerchi di trovare alcune euristiche. E non potrai mai raggiungere la soluzione ideale, giusto? \u00c8 semplicemente impossibile. Guarda, la compilazione Ahead of Time - funziona male anche essa. Qui si parla di casi medi. Di prestazioni tipiche, quindi puoi andare e misurare qualcosa che consideri una buona prestazione tipica - dopo tutto, stai lavorando per migliorarla! L'allocazione dei registri \u00e8 un tema interamente dedicato alle prestazioni. Una volta che hai il primo prototipo, che funziona e rende ci\u00f2 che serve, inizia il lavoro sulle performance. Devi imparare a misurare bene. Perch\u00e9 \u00e8 importante? Se ci sono dati chiari, puoi guardare a diverse aree e vedere: ah, questo ha aiutato qui, ma l\u00ec \u00e8 andato tutto male! Emergeranno alcune buone idee, aggiungi una nuova euristica e improvvisamente tutto inizia a funzionare mediamente un po' meglio. O non inizia. Ho avuto molti casi in cui abbiamo lottato per cinque punti percentuali di prestazioni che distingevano il nostro sviluppo dagli allocatori precedenti. E ogni volta appare cos\u00ec: hai vinto da qualche parte, hai perso da qualche altra parte. Se hai buoni strumenti di analisi delle prestazioni, puoi trovare idee perdenti e capire perch\u00e9 stanno perdendo. Forse vale la pena lasciare tutto com'\u00e8, o forse \u00e8 il caso di fare una seria messa a punto, o andare a sistemare qualcos'altro. Questo \u00e8 un intero insieme di cose! Ho fatto questo fantastico hack, ma ho bisogno anche di questo, di questo e di quest'altro \u2013 e la loro combinazione complessiva porta a dei miglioramenti. Ma gli hack singoli possono fallire. Questa \u00e8 la natura del lavoro sulle prestazioni dei problemi NP-completi.<\/p>\n<p><\/p>\n<p><strong>Vladimir<\/strong>: Sembra che cose come la verniciatura negli allocatori siano gi\u00e0 una questione risolta. Beh, per voi sembra risolta, a giudicare da quello che raccontate, quindi vale la pena\u2026<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Non \u00e8 risolta in quanto tale. Devi trasformarla in \"risolta\". Ci sono compiti difficili e vanno affrontati. Quando ci\u00f2 \u00e8 fatto, arriva il momento di lavorare sulle prestazioni. Questa fase deve essere presa sul serio: eseguire benchmark, raccogliere metriche, spiegare situazioni in cui, tornando a una versione precedente, il tuo vecchio hack funziona di nuovo (o viceversa, smette di funzionare). E non fermarti finch\u00e9 non ottieni risultati. Come ho gi\u00e0 detto, ci sono idee interessanti che non hanno avuto successo, ma nel campo dell'allocazione dei registri le idee sono praticamente infinite. Puoi, ad esempio, leggere pubblicazioni scientifiche. Sebbene ora questo campo si muova pi\u00f9 lentamente e sia diventato pi\u00f9 chiaro rispetto ai tempi della sua giovinezza. Tuttavia, in questo settore lavorano un'infinit\u00e0 di persone e tutte le loro idee meritano di essere provate, tutte aspettano il loro momento. E non puoi sapere quanto siano buone se non le provi. Quanto bene si integrano con tutto il resto nel tuo allocatore, perch\u00e9 l'allocatore fa molte cose, e alcune idee nel tuo specifico allocatore potrebbero non funzionare, mentre in un altro allocatore funzionerebbero senza problemi. Il principale modo per vincere per un allocatore \u00e8 estrarre le cose lente dal percorso principale e forzare la suddivisione lungo i confini dei percorsi lenti. Quindi, se vuoi avviare la GC, seguire il percorso lento, deottimizzare, generare un'eccezione, e cos\u00ec via \u2013 sai che queste situazioni sono relativamente rare. E sono davvero rare, l'ho verificato. Effettui un lavoro aggiuntivo e grazie a ci\u00f2 molti vincoli su questi percorsi lenti scompaiono, ma questo non \u00e8 molto importante, poich\u00e9 sono lenti e ci si passa raramente. Ad esempio, un puntatore nullo \u2013 non accade mai, giusto? Devi avere pi\u00f9 percorsi per diverse cose, ma non devono intralciarsi nel principale.\u00a0<\/p>\n<p><\/p>\n<p><strong>Vladimir<\/strong>: Cosa pensi della multi-core quando ci sono migliaia di core? \u00c8 una cosa utile?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Il successo della GPU dimostra che \u00e8 abbastanza utile!<\/p>\n<p><\/p>\n<p><strong>Vladimir<\/strong>: Sono piuttosto specializzati. E per quanto riguarda i processori generali?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: Beh, questo era il modello di business di Azul. La risposta arriv\u00f2 ancora nell'era in cui la gente amava molto le performance prevedibili. All'epoca era difficile scrivere codice parallelo. Il modello di codifica H2O \u00e8 molto scalabile, ma non \u00e8 un modello generale. A meno che non sia leggermente pi\u00f9 generale rispetto all'uso di GPU. Stiamo parlando della complessit\u00e0 di sviluppo di una cosa del genere o della complessit\u00e0 del suo utilizzo? Ad esempio, Azul mi ha insegnato una lezione interessante, piuttosto inaspettata: piccoli cache vanno bene.\u00a0<\/p>\n<p><\/p>\n<h1 id=\"samyy-bolshoy-chellenzh-v-zhizni\">La sfida pi\u00f9 grande della vita<\/h1>\n<p><\/p>\n<p><strong>Vladimir<\/strong>: E riguardo alle sfide non tecniche?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: La sfida pi\u00f9 grande \u00e8 stata non essere... gentile e buono con le persone. E di conseguenza, mi sono sempre ritrovato in situazioni estremamente conflittuali. In queste situazioni sapevo che tutto stava andando male, ma non sapevo come procedere nella risoluzione di quei problemi, e non riuscivo a gestirli. Molti problemi di lunga durata, che durano decenni, sono emersi proprio in questo modo. Il fatto che in Java ci siano i compilatori C1 e C2 \u00e8 una diretta conseguenza di ci\u00f2. Il fatto che in Java non ci sia stata per un decennio la compilazione multilivello \u00e8 anch'essa una diretta conseguenza. \u00c8 ovvio che avevamo bisogno di un sistema del genere, ma non \u00e8 chiaro perch\u00e9 non ci fosse. Ho avuto problemi con un ingegnere... o un gruppo di ingegneri. Tanto tempo fa, quando ho iniziato a lavorare in Sun, ero... Va bene, non solo allora, ho sempre avuto un'opinione su tutto. E credevo fosse la verit\u00e0 che si potesse semplicemente prendere questa verit\u00e0 e dirla senza mezzi termini. Soprattutto perch\u00e9 avevo ragione in modo sconcertante gran parte del tempo. E se questo approccio non ti piace... soprattutto se sei ovviamente in errore e stai facendo sciocchezze... Insomma, poche persone riuscivano a tollerare un tale stile comunicativo. Anche se alcune s\u00ec, come me. Ho costruito tutta la mia vita su principi meritocratici. Se mi mostri qualcosa di sbagliato, mi volto immediatamente e dico: hai detto una sciocchezza. Ovviamente mi scuso e tutto il resto, riconosco i meriti, se ci sono, e compio gli altri passi corretti. D'altra parte, ho ragione in modo sconcertante in un'impressionante percentuale di tempo. E questo non funziona molto bene nelle relazioni con le persone. Non cerco nemmeno di essere gentile, ma metto la questione sul tavolo. 'Questo non funzioner\u00e0 mai, perch\u00e9 uno, due e tre'. E loro sono tipo: 'Oh!'. Ci sono stati anche altri effetti, che sarebbe meglio forse saltare: per esempio, quelli che hanno portato al divorzio da mia moglie e a dieci anni di depressione dopo.<\/p>\n<p><\/p>\n<p>La sfida \u00e8 una lotta contro le persone, contro la loro percezione di ci\u00f2 che puoi o non puoi fare, di ci\u00f2 che \u00e8 importante e di ci\u00f2 che non lo \u00e8. Ci sono stati molti challenge sullo stile di codifica. Scrivo ancora molto codice, e in quei tempi ho dovuto persino rallentare perch\u00e9 stavo facendo troppe cose in parallelo e non le facevo bene, invece di concentrarmi su una sola cosa. Ripensando, ho scritto met\u00e0 del codice del team Java JIT, del team C2. Il prossimo programmatore in velocit\u00e0 scriveva la met\u00e0, l'altro ancora la met\u00e0, ed era una caduta esponenziale. Il settimo in quella fila era molto, molto lento \u2014 succede sempre cos\u00ec! Ho toccato un sacco di codice. Ho osservato chi scriveva cosa, senza eccezioni, fissavo il loro codice, revisionavo ciascuno di loro, e continuavo comunque a scrivere pi\u00f9 di chiunque altro. Con le persone, questo approccio non funziona molto bene. Alcuni non lo gradiscono. E quando non riescono a gestirlo, iniziano tutte le sorta di lamentele. Ad esempio, un giorno mi hanno detto di smettere di scrivere codice, perch\u00e9 stavo scrivendo troppo codice e questo metteva in pericolo il team, e per me tutto ci\u00f2 suonava come uno scherzo: amico, se tutto il resto del team scompare e io continuo a scrivere codice, perderai solo met\u00e0 del team. D'altra parte, se continuo a scrivere codice e perdi met\u00e0 del team \u2013 suona come una gestione molto scadente. Non ci ho mai pensato molto, non ne ho mai parlato, ma era comunque da qualche parte nella mia testa. Nelle retrovie della mia coscienza girava il pensiero: 'Ma state scherzando?'. Quindi, il problema pi\u00f9 grande ero io e le mie relazioni con le persone. Ora mi capisco molto meglio, ho fatto da team leader per programmatori per un lungo periodo, e ora dico chiaramente alle persone: sai, io sono come sono, e dovrai affrontarmi \u2013 va bene se rimango qui? E quando hanno iniziato a gestirlo, tutto ha funzionato. In realt\u00e0 non sono n\u00e9 buono n\u00e9 cattivo, non ho alcuna cattiva intenzione o obiettivi egoistici, \u00e8 semplicemente la mia essenza, e bisogna trovare un modo per vivere con esse.<\/p>\n<p><\/p>\n<p><strong>Andrey<\/strong>: Recentemente, tutti hanno iniziato a parlare di auto-consapevolezza per gli introversi e, in generale, delle soft skills. Cosa si pu\u00f2 dire al riguardo?<\/p>\n<p><\/p>\n<p><strong>Cliff<\/strong>: S\u00ec, \u00e8 stata una comprensione e una lezione che ho tratto dal mio divorzio con mia moglie. Ci\u00f2 che ho appreso dal divorzio \u00e8 stata una comprensione di me stesso. In questo modo ho cominciato a capire gli altri. Capire come funziona questa interazione. Questo ha portato a scoperte una dopo l'altra. \u00c8 arrivata la consapevolezza su chi sono e cosa rappresento. Cosa faccio: o sono impegnato in un compito, o evito il conflitto, o qualcos'altro \u2013 e questo livello di autoconsapevolezza davvero aiuta a mantenere la calma. Dopo di che tutto diventa molto pi\u00f9 semplice. Una cosa che ho scoperto non solo in me stesso, ma anche in altri programmatori \u00e8 l'impossibilit\u00e0 di verbalizzare i pensieri quando sei sotto stress emotivo. Ad esempio, ti trovi a scrivere codice, sei in uno stato di flusso, e all'improvviso qualcuno corre da te gridando in preda al panico che qualcosa \u00e8 andato storto e che ora verranno adottate misure estreme. E non riesci a dire una parola, perch\u00e9 sei in uno stato di stress emotivo. Le conoscenze acquisite ti permettono di prepararti a quel momento, affrontarlo e passare a un piano di fuga, dopo il quale puoi fare qualcosa. Quindi s\u00ec, quando inizi a renderti conto di come funziona tutto questo \u2013 \u00e8 un enorme evento che cambia la vita.\u00a0<br \/>\nNon sono riuscito a trovare le parole giuste, ma ho memorizzato la sequenza delle azioni. La sostanza \u00e8 che questa reazione \u00e8 tanto fisica quanto verbale, e hai bisogno di spazio. Uno spazio del genere, in senso zen. \u00c8 proprio questo che deve essere spiegato, e poi \u00e8 necessario allontanarsi fisicamente. Quando rimango in silenzio, posso elaborare la situazione dal punto di vista emotivo. Man mano che l'adrenalina raggiunge il cervello e ti mette in modalit\u00e0 'attacca o fuggi', non riesci pi\u00f9 a parlare: ora sei un idiota, un ingegnere da colpire, incapace di fornire una risposta adeguata o di fermare l'attacco, e l'attaccante pu\u00f2 colpire liberamente ancora e ancora. Prima di tutto, devi tornare a essere te stesso, riprendere il controllo, uscire dalla modalit\u00e0 'attacca o fuggi'. <\/p>\n<p><\/p>\n<p>Ecco, per questo \u00e8 necessario uno spazio verbale. Semplicemente uno spazio libero. Se si deve parlare di qualcosa, si pu\u00f2 dichiarare proprio questo e poi andare a cercare realmente il proprio \"spazio\": fare una passeggiata nel parco, chiudersi in doccia \u2013 non importa. L'importante \u00e8 disconnettersi temporaneamente dalla situazione. Non appena ti disconnetti anche solo per qualche secondo, il controllo ritorna e inizi a pensare in modo lucido. \"Bene, non sono un idiota, non faccio cose stupide, sono una persona piuttosto utile.\" Una volta che riesci a convincere te stesso, \u00e8 tempo di passare alla fase successiva: capire cosa \u00e8 successo. Sei stato attaccato, l'attacco \u00e8 arrivato da dove non te lo aspettavi, \u00e8 stata una trappola vile e sleale. Questo \u00e8 male. Il passo successivo \u00e8 capire perch\u00e9 l'aggressore ha fatto questo. Davvero, perch\u00e9? Forse perch\u00e9 \u00e8 arrabbiato lui stesso? Perch\u00e9 \u00e8 arrabbiato? Ad esempio, perch\u00e9 ha fallito e non pu\u00f2 accettare la responsabilit\u00e0? In questo modo bisogna elaborare con attenzione l'intera situazione. Ma per farlo serve uno spazio di manovra, uno spazio verbale. Il primo passo \u00e8 interrompere il contatto verbale. Allontanarsi dalla discussione verbale. Annullarla, andarsene il prima possibile. Se \u00e8 una telefonata \u2013 basta riattaccare \u2013 \u00e8 un'abilit\u00e0 che ho acquisito comunicando con la mia ex moglie. Se la conversazione non porta a nulla di buono, basta dire \"arrivederci\" e riattaccare. Dall'altra parte del telefono: \"bla-bla-bla\", tu rispondi: \"ok, ciao!\" e riattacchi. Interrompi semplicemente la conversazione. Cinque minuti dopo, quando la tua capacit\u00e0 di pensare lucidamente torna, ti sei un po' raffreddato, \u00e8 possibile riflettere su cosa sia effettivamente successo e su cosa ci sar\u00e0 dopo. E iniziare a formulare una risposta ponderata, non semplicemente reagire emotivamente. Per me, una svolta nella consapevolezza di s\u00e9 \u00e8 stata proprio il fatto che in caso di stress emotivo non riesco a parlare. Uscire da questo stato, riflettere e pianificare come rispondere e affrontare i problemi \u2013 questi sono i passaggi corretti nel caso in cui non si possa parlare. Il modo pi\u00f9 semplice \u00e8 fuggire dalla situazione in cui si manifesta lo stress emotivo e semplicemente smettere di partecipare a questo stress. Dopo di ci\u00f2, riacquisti la capacit\u00e0 di pensare, quando puoi pensare, hai la possibilit\u00e0 di parlare, e cos\u00ec via.<\/p>\n<p><\/p>\n<p>A proposito, in tribunale l'avvocato della parte avversa cerca di farlo con te \u2013 adesso \u00e8 chiaro perch\u00e9. Perch\u00e9 ha la possibilit\u00e0 di schiacciarti fino a un punto in cui non riuscirai nemmeno a pronunciare il tuo nome, ad esempio. Nel senso pi\u00f9 diretto, non riuscirai a parlare. Se ti trovi in questa situazione e se sai di dover affrontare un ambiente dove si svolgono battaglie verbali, come in tribunale, \u00e8 utile portare con te il tuo avvocato. L'avvocato si far\u00e0 carico di tutelarti e di fermare l'attacco verbale, e lo far\u00e0 in modo del tutto legittimo, permettendoti di recuperare il tuo spazio di zen perduto. Ad esempio, una volta o due ho dovuto chiamare la mia famiglia, e il giudice ha reagito in modo piuttosto amichevole, ma l'avvocato della parte opposta continuava a urlare e urlare verso di me, e non riuscivo nemmeno a inserire una parola. In tali casi, per me funziona meglio utilizzare un mediatore. Il mediatore interrompe tutta questa pressione che ti colpisce come un flusso incessante, e ti permette di ritrovare il tuo necessario spazio di zen, insieme al quale torna la capacit\u00e0 di parlare. \u00c8 un'intera area di conoscenze, in cui c'\u00e8 molto da studiare e molto da scoprire dentro di s\u00e9, e tutto questo si traduce in decisioni strategiche di alto livello, diverse per persone diverse. Alcuni non hanno problemi descritti sopra; di solito, non li hanno le persone che lavorano nelle vendite. Tutti questi individui che guadagnano da vivere con le parole \u2013 cantanti famosi, poeti, leader religiosi e politici \u2013 hanno sempre qualcosa da dire. Non hanno questi problemi, mentre io s\u00ec.<\/p>\n<p><\/p>\n<p><strong>Andrey<\/strong>: \u00c8 stato\u2026 inaspettato. Ottimo, abbiamo gi\u00e0 parlato parecchio ed \u00e8 ora di concludere questa intervista. Ci incontreremo sicuramente alla conferenza e potremo continuare questo dialogo. Ci vediamo su Hydra!<\/p>\n<p><\/p>\n<blockquote><p>Potrai continuare la conversazione con Cliff alla conferenza Hydra 2019, che si terr\u00e0 l'11-12 luglio 2019 a San Pietroburgo. Lui arriver\u00e0 con una presentazione <noindex><a rel=\"nofollow\" href=\"https:\/\/hydraconf.com\/2019\/talks\/2jix5mst7iduyp9linqhfj\/?utm_source=habr&amp;utm_medium=45871\">\u00abL'esperienza della memoria transazionale hardware di Azul\u00bb<\/a><\/noindex>. I biglietti possono essere acquistati <noindex><a rel=\"nofollow\" href=\"https:\/\/hydraconf.ru\/?utm_source=habr&amp;utm_medium=458718\">sul sito ufficiale<\/a><\/noindex>.<\/p><\/blockquote>\n<p>Fonte: <a content=\"nofollow\" rel=\"nofollow\" href=\"https:\/\/habr.com\/ru\/company\/jugru\/blog\/458718\/\">habr.com<\/a><\/p>","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"excerpt":{"rendered":"<p>\u041a\u043b\u0438\u0444\u0444 \u041a\u043b\u0438\u043a \u2014 CTO \u043a\u043e\u043c\u043f\u0430\u043d\u0438\u0438 Cratus (IoT \u0441\u0435\u043d\u0441\u043e\u0440\u044b \u0434\u043b\u044f \u0443\u043b\u0443\u0447\u0448\u0435\u043d\u0438\u044f \u043f\u0440\u043e\u0446\u0435\u0441\u0441\u043e\u0432), \u043e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u0438 \u0441\u043e\u043e\u0441\u043d\u043e\u0432\u0430\u0442\u0435\u043b\u044c \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u0445 \u0441\u0442\u0430\u0440\u0442\u0430\u043f\u043e\u0432 (\u0432\u043a\u043b\u044e\u0447\u0430\u044f Rocket Realtime School, Neurensic \u0438 H2O.ai) \u0441 \u043d\u0435\u0441\u043a\u043e\u043b\u044c\u043a\u0438\u043c\u0438 \u0443\u0441\u043f\u0435\u0448\u043d\u044b\u043c\u0438 \u044d\u043a\u0437\u0438\u0442\u0430\u043c\u0438. \u041a\u043b\u0438\u0444\u0444 \u043d\u0430\u043f\u0438\u0441\u0430\u043b \u0441\u0432\u043e\u0439 \u043f\u0435\u0440\u0432\u044b\u0439 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u0432 15 \u043b\u0435\u0442 (Pascal \u0434\u043b\u044f TRS Z-80)! \u041d\u0430\u0438\u0431\u043e\u043b\u0435\u0435 \u0438\u0437\u0432\u0435\u0441\u0442\u0435\u043d \u0437\u0430 \u0440\u0430\u0431\u043e\u0442\u0443 \u043d\u0430\u0434 \u04212 \u0432 Java (the Sea of Nodes IR). \u042d\u0442\u043e\u0442 \u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0442\u043e\u0440 \u043f\u043e\u043a\u0430\u0437\u0430\u043b [&hellip;]<\/p>\n","protected":false,"gt_translate_keys":[{"key":"rendered","format":"html"}]},"author":1,"featured_media":0,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[688],"tags":[],"class_list":["post-35851","post","type-post","status-publish","format-standard","hentry","category-administrirovanie"],"aioseo_notices":[],"aioseo_head":"\n\t\t<!-- All in One SEO 5.0.0.1 - aioseo.com -->\n\t<meta name=\"description\" content=\"\u041a\u043b\u0438\u0444\u0444 \u041a\u043b\u0438\u043a \u2014.\" \/>\n\t<meta name=\"robots\" content=\"max-image-preview:large\" \/>\n\t<meta name=\"author\" content=\"Yuri Gagarin\"\/>\n\t<link rel=\"canonical\" href=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java\" \/>\n\t<meta name=\"generator\" content=\"All in One SEO (AIOSEO) 5.0.0.1\" \/>\n\t\t<meta property=\"og:locale\" content=\"it_IT\" \/>\n\t\t<meta property=\"og:site_name\" content=\"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b\" \/>\n\t\t<meta property=\"og:type\" content=\"article\" \/>\n\t\t<meta property=\"og:title\" content=\"\ud83e\udd47\u0411\u043e\u043b\u044c\u0448\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u044c\u044e \u0441 \u041a\u043b\u0438\u0444\u0444\u043e\u043c \u041a\u043b\u0438\u043a\u043e\u043c \u2014 \u043e\u0442\u0446\u043e\u043c JIT-\u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 \u0432 Java | ProHoster\" \/>\n\t\t<meta property=\"og:description\" content=\"\u041a\u043b\u0438\u0444\u0444 \u041a\u043b\u0438\u043a \u2014.\" \/>\n\t\t<meta property=\"og:url\" content=\"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java\" \/>\n\t\t<meta property=\"og:image\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:secure_url\" content=\"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg\" \/>\n\t\t<meta property=\"og:image:width\" content=\"350\" \/>\n\t\t<meta property=\"og:image:height\" content=\"350\" \/>\n\t\t<meta property=\"article:published_time\" content=\"2019-10-31T19:07:03+00:00\" \/>\n\t\t<meta property=\"article:modified_time\" content=\"2019-10-31T19:07:03+00:00\" \/>\n\t\t<meta property=\"article:publisher\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<meta property=\"article:author\" content=\"https:\/\/www.facebook.com\/prohoster\" \/>\n\t\t<!-- All in One SEO -->\n\n","aioseo_head_json":{"title":"\ud83e\udd47Grande intervista con Cliff Click \u2014 padre della compilazione JIT in Java | ProHoster","description":"Cliff Click \u2014.","canonical_url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java","robots":"max-image-preview:large","keywords":"","webmasterTools":{"miscellaneous":""},"schema":null,"og:locale":"it_IT","og:site_name":"ProHoster | \u041a\u0443\u043f\u0438\u0442\u044c \u043d\u0430\u0434\u0435\u0436\u043d\u044b\u0439 \u0445\u043e\u0441\u0442\u0438\u043d\u0433 \u0434\u043b\u044f \u0441\u0430\u0439\u0442\u043e\u0432 \u0441 \u0437\u0430\u0449\u0438\u0442\u043e\u0439 \u043e\u0442 DDoS, VPS VDS \u0441\u0435\u0440\u0432\u0435\u0440\u044b","og:type":"article","og:title":"\ud83e\udd47\u0411\u043e\u043b\u044c\u0448\u043e\u0435 \u0438\u043d\u0442\u0435\u0440\u0432\u044c\u044e \u0441 \u041a\u043b\u0438\u0444\u0444\u043e\u043c \u041a\u043b\u0438\u043a\u043e\u043c \u2014 \u043e\u0442\u0446\u043e\u043c JIT-\u043a\u043e\u043c\u043f\u0438\u043b\u044f\u0446\u0438\u0438 \u0432 Java | ProHoster","og:description":"\u041a\u043b\u0438\u0444\u0444 \u041a\u043b\u0438\u043a \u2014.","og:url":"https:\/\/prohoster.info\/it\/blog\/administrirovanie\/bolshoe-intervyu-s-kliffom-klikom-ottsom-jit-kompilyatsii-v-java","og:image":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:secure_url":"https:\/\/prohoster.info\/wp-content\/uploads\/2021\/11\/logo-350.jpg","og:image:width":350,"og:image:height":350,"article:published_time":"2019-10-31T19:07:03+00:00","article:modified_time":"2019-10-31T19:07:03+00:00","article:publisher":"https:\/\/www.facebook.com\/prohoster","article:author":"https:\/\/www.facebook.com\/prohoster"},"aioseo_meta_data":{"post_id":"35851","title":null,"description":null,"keywords":null,"keyphrases":null,"primary_term":null,"canonical_url":null,"og_title":null,"og_description":null,"og_object_type":"default","og_image_type":"default","og_image_url":null,"og_image_width":null,"og_image_height":null,"og_image_custom_url":null,"og_image_custom_fields":null,"og_video":null,"og_custom_url":null,"og_article_section":null,"og_article_tags":null,"twitter_use_og":false,"twitter_card":"default","twitter_image_type":"default","twitter_image_url":null,"twitter_image_custom_url":null,"twitter_image_custom_fields":null,"twitter_title":null,"twitter_description":null,"schema":{"blockGraphs":[],"customGraphs":[],"default":{"data":{"Article":[],"Course":[],"Dataset":[],"FAQPage":[],"Movie":[],"Person":[],"Product":[],"ProductReview":[],"Car":[],"Recipe":[],"Service":[],"SoftwareApplication":[],"WebPage":[]},"graphName":"","isEnabled":true},"graphs":[]},"schema_type":null,"schema_type_options":null,"pillar_content":false,"robots_default":true,"robots_noindex":false,"robots_noarchive":false,"robots_nosnippet":false,"robots_nofollow":false,"robots_noimageindex":false,"robots_noodp":false,"robots_notranslate":false,"robots_max_snippet":null,"robots_max_videopreview":null,"robots_max_imagepreview":"large","priority":null,"frequency":null,"local_seo":null,"seo_analyzer_scan_date":"2026-01-22 01:01:19","breadcrumb_settings":null,"limit_modified_date":false,"reviewed_by":null,"ai":null,"created":"2021-03-01 01:55:25","updated":"2026-01-22 01:01:19","focus_keyword":null,"additional_keywords":null,"truseo_locale":null},"gt_translate_keys":[{"key":"link","format":"url"}],"_links":{"self":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/35851","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/users\/1"}],"replies":[{"embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/comments?post=35851"}],"version-history":[{"count":0,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/posts\/35851\/revisions"}],"wp:attachment":[{"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/media?parent=35851"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/categories?post=35851"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/prohoster.info\/it\/wp-json\/wp\/v2\/tags?post=35851"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}