Bra intervju med Cliff Click, fadern till JIT-kompileringen i Java

Bra intervju med Cliff Click, fadern till JIT-kompileringen i JavaCliff Klicka — CTO för Cratus (IoT-sensorer för processförbĂ€ttring), grundare och medgrundare av flera startups (inklusive Rocket Realtime School, Neurensic och H2O.ai) med flera framgĂ„ngsrika exit. Cliff skrev sin första kompilator vid 15 Ă„rs Ă„lder (Pascal för TRS Z-80)! Han Ă€r mest kĂ€nd för sitt arbete pĂ„ C2 i Java (The Sea of ​​​​Nodes IR). Denna kompilator visade vĂ€rlden att JIT kunde producera högkvalitativ kod, vilket var en av faktorerna i framvĂ€xten av Java som en av de viktigaste moderna mjukvaruplattformarna. Sedan hjĂ€lpte Cliff Azul Systems att bygga en stordator med 864 kĂ€rnor med ren Java-mjukvara som stödde GC-pauser pĂ„ en 500-gigabyte hög inom 10 millisekunder. I allmĂ€nhet lyckades Cliff arbeta med alla aspekter av JVM.

 
Denna habrapost Àr en bra intervju med Cliff. Vi kommer att prata om följande Àmnen:

  • ÖvergĂ„ng till lĂ„gnivĂ„optimeringar
  • Hur man gör en stor refaktorering
  • Kostnadsmodell
  • OptimeringstrĂ€ning pĂ„ lĂ„g nivĂ„
  • Praktiska exempel pĂ„ prestationsförbĂ€ttring
  • Varför skapa ditt eget programmeringssprĂ„k
  • KarriĂ€r som prestationsingenjör
  • Tekniska utmaningar
  • Lite om registertilldelning och multikĂ€rnor
  • Den största utmaningen i livet

Intervjuer genomförs av:

  • Andrey Satarin frĂ„n Amazon Web Services. I sin karriĂ€r lyckades han arbeta i helt andra projekt: han testade den distribuerade NewSQL-databasen i Yandex, ett molndetekteringssystem i Kaspersky Lab, ett multiplayer-spel i Mail.ru och en tjĂ€nst för att berĂ€kna valutapriser i Deutsche Bank. Intresserad av att testa storskaliga backend och distribuerade system.
  • Vladimir Sitnikov frĂ„n Netcracker. Tio Ă„rs arbete med prestanda och skalbarhet hos NetCracker OS, programvara som anvĂ€nds av telekomoperatörer för att automatisera processer för hantering av nĂ€tverk och nĂ€tverksutrustning. Intresserad av prestandaproblem i Java och Oracle Database. Författare till mer Ă€n ett dussin prestandaförbĂ€ttringar i den officiella PostgreSQL JDBC-drivrutinen.

ÖvergĂ„ng till lĂ„gnivĂ„optimeringar

Andrew: Du Ă€r ett stort namn i vĂ€rlden av JIT-kompilering, Java och prestandaarbete i allmĂ€nhet, eller hur? 

Klippa: Det Àr sÄ!

Andrew: LÄt oss börja med nÄgra allmÀnna frÄgor om prestationsarbete. Vad tycker du om valet mellan hög- och lÄgnivÄoptimeringar som att arbeta pÄ CPU-nivÄ?

Klippa: Ja, allt Ă€r enkelt hĂ€r. Den snabbaste koden Ă€r den som aldrig körs. DĂ€rför mĂ„ste du alltid utgĂ„ frĂ„n en hög nivĂ„, arbeta med algoritmer. En bĂ€ttre O-notation kommer att slĂ„ en sĂ€mre O-notation, om inte nĂ„gra tillrĂ€ckligt stora konstanter ingriper. Saker pĂ„ lĂ„g nivĂ„ gĂ„r sist. Vanligtvis, om du har optimerat resten av din stack tillrĂ€ckligt bra och det fortfarande finns en del intressanta saker kvar, Ă€r det en lĂ„g nivĂ„. Men hur ska man börja frĂ„n en hög nivĂ„? Hur vet du att tillrĂ€ckligt högt arbete har gjorts? NĂ„vĂ€l... inget sĂ€tt. Det finns inga fĂ€rdiga recept. Du mĂ„ste förstĂ„ problemet, bestĂ€mma vad du ska göra (för att inte ta onödiga steg i framtiden) och sedan kan du avslöja profileraren, som kan sĂ€ga nĂ„got anvĂ€ndbart. Vid nĂ„gon tidpunkt inser du sjĂ€lv att du har blivit av med onödiga saker och det Ă€r dags att göra lite finjustering pĂ„ lĂ„g nivĂ„. Detta Ă€r definitivt en speciell typ av konst. Det finns mĂ„nga mĂ€nniskor som gör onödiga saker, men gĂ„r sĂ„ snabbt att de inte har tid att oroa sig för produktiviteten. Men detta Ă€r tills frĂ„gan uppstĂ„r rakt pĂ„ sak. Vanligtvis 99% av tiden bryr sig ingen om vad jag gör, tills det ögonblick dĂ„ en viktig sak kommer pĂ„ den kritiska vĂ€gen som ingen bryr sig om. Och hĂ€r börjar alla tjata pĂ„ dig om "varför det inte fungerade perfekt frĂ„n början." Generellt sett finns det alltid nĂ„got att förbĂ€ttra i prestanda. Men 99% av tiden har du inga potentiella kunder! Du försöker bara fĂ„ nĂ„got att fungera och i processen kommer du pĂ„ vad som Ă€r viktigt. Du kan aldrig i förvĂ€g veta att den hĂ€r biten behöver vara perfekt, sĂ„ i sjĂ€lva verket mĂ„ste du vara perfekt i allt. Men det hĂ€r Ă€r omöjligt och du gör det inte. Det Ă€r alltid mycket att fixa – och det Ă€r helt normalt.

Hur man gör en stor refaktorering

Andrew: Hur jobbar du med en förestÀllning? Detta Àr ett tvÀrgÄende problem. Har du till exempel nÄgonsin behövt arbeta med problem som uppstÄr frÄn skÀrningspunkten mellan mÄnga befintliga funktioner?

Klippa: Jag försöker undvika det. Om jag vet att prestanda kommer att vara ett problem tÀnker jag pÄ det innan jag börjar koda, speciellt med datastrukturer. Men ofta upptÀcker man allt detta mycket senare. Och sedan mÄste du gÄ till extrema ÄtgÀrder och göra vad jag kallar "rewrite and conquer": du mÄste ta en tillrÀckligt stor bit. En del av koden mÄste fortfarande skrivas om pÄ grund av prestandaproblem eller nÄgot annat. Oavsett anledningen till att skriva om koden Àr det nÀstan alltid bÀttre att skriva om en större bit Àn en mindre bit. I det hÀr ögonblicket börjar alla skaka av rÀdsla: "herregud, du kan inte röra sÄ mycket kod!" Men i sjÀlva verket fungerar detta tillvÀgagÄngssÀtt nÀstan alltid mycket bÀttre. Du mÄste genast ta dig an ett stort problem, rita en stor cirkel runt det och sÀga: Jag kommer att skriva om allt i cirkeln. Kanten Àr mycket mindre Àn innehÄllet inuti den som behöver bytas ut. Och om en sÄdan avgrÀnsning av grÀnser gör att du kan utföra arbetet inuti perfekt, dina hÀnder Àr fria, gör vad du vill. NÀr du vÀl förstÄr problemet Àr omskrivningsprocessen mycket lÀttare, sÄ ta en stor bit!
Samtidigt, nÀr du gör en stor omskrivning och inser att prestanda kommer att bli ett problem, kan du omedelbart börja oroa dig för det. Detta förvandlas vanligtvis till enkla saker som "kopiera inte data, hantera data sÄ enkelt som möjligt, gör det litet." Vid stora omskrivningar finns det vanliga sÀtt att förbÀttra prestandan. Och de kretsar nÀstan alltid kring data.

Kostnadsmodell

Andrew: I en av podcasterna pratade du om kostnadsmodeller i produktivitetssammanhang. Kan du förklara vad du menade med detta?

Klippa: Visst. Jag föddes i en tid dĂ„ processorprestanda var extremt viktigt. Och den hĂ€r eran Ă„tervĂ€nder igen - ödet Ă€r inte utan ironi. Jag började leva i Ă„ttabitars maskiners dagar; min första dator fungerade med 256 byte. Exakt bytes. Allt var vĂ€ldigt litet. Instruktioner var tvungna att rĂ€knas, och nĂ€r vi började flytta upp i programmeringssprĂ„ksstacken, tog sprĂ„ken pĂ„ sig mer och mer. Det var Assembler, sedan Basic, sedan C, och C tog hand om mycket av detaljerna, som registertilldelning och instruktionsval. Men allt var ganska klart dĂ€r, och om jag gjorde en pekare till en instans av en variabel, sĂ„ skulle jag fĂ„ last, och kostnaden för denna instruktion Ă€r kĂ€nd. HĂ„rdvaran producerar ett visst antal maskincykler, sĂ„ exekveringshastigheten för olika saker kan berĂ€knas helt enkelt genom att lĂ€gga ihop alla instruktioner som du ska köra. Varje jĂ€mför/test/filial/samtal/ladda/butik kan lĂ€ggas ihop och sĂ€ga: det Ă€r körningstiden för dig. NĂ€r du arbetar med att förbĂ€ttra prestanda kommer du definitivt att vara uppmĂ€rksam pĂ„ vilka siffror som motsvarar smĂ„ heta cykler. 
Men sÄ fort man byter till Java, Python och liknande sÄ gÄr man vÀldigt snabbt bort frÄn hÄrdvara pÄ lÄg nivÄ. Vad kostar det att ringa en getter i Java? Om JIT i HotSpot Àr korrekt infogat, kommer det att laddas, men om det inte gjorde detta, kommer det att vara ett funktionsanrop. Eftersom samtalet Àr i en het loop kommer det att ÄsidosÀtta alla andra optimeringar i den loopen. DÀrför blir den verkliga kostnaden mycket högre. Och du förlorar omedelbart möjligheten att titta pÄ en bit kod och förstÄ att vi bör exekvera den nÀr det gÀller processorns klockhastighet, minne och cache som anvÀnds. Allt detta blir intressant först om du verkligen kommer in i förestÀllningen.
Nu befinner vi oss i en situation dÀr processorhastigheterna knappt har ökat pÄ ett decennium. De gamla dagarna Àr tillbaka! Du kan inte lÀngre rÀkna med bra entrÄdig prestanda. Men om du plötsligt kommer in i parallell datoranvÀndning Àr det otroligt svÄrt, alla ser pÄ dig som James Bond. Tiofaldiga accelerationer hÀr sker vanligtvis pÄ platser dÀr nÄgon har stökat till nÄgot. Samtidighet krÀver mycket arbete. För att fÄ den dÀr XNUMXx hastigheten mÄste du förstÄ kostnadsmodellen. Vad och hur mycket kostar det? Och för att göra detta mÄste du förstÄ hur tungan passar pÄ den underliggande hÄrdvaran.
Martin Thompson valde ett bra ord för sin blogg Mekanisk sympati! Du mÄste förstÄ vad hÄrdvaran kommer att göra, exakt hur den kommer att göra det och varför den gör vad den gör i första hand. Med detta Àr det ganska enkelt att börja rÀkna instruktioner och ta reda pÄ vart exekveringstiden tar vÀgen. Om du inte har lÀmplig utbildning, letar du bara efter en svart katt i ett mörkt rum. Jag ser mÀnniskor som optimerar prestanda hela tiden som inte har en aning om vad fan de hÄller pÄ med. De lider mycket och gör inte mycket framsteg. Och nÀr jag tar samma kod, slinker in ett par smÄ hack och fÄr en fem- eller tiofaldig snabbare, Àr de som: ja, det Àr inte rÀttvist, vi visste redan att du var bÀttre. Fantastisk. Vad pratar jag om... kostnadsmodellen handlar om vilken typ av kod du skriver och hur snabbt den gÄr i snitt i det stora hela.

Andrew: Och hur kan man hÄlla en sÄdan volym i huvudet? UppnÄs detta med mer erfarenhet, eller? Var kommer en sÄdan erfarenhet ifrÄn?

Klippa: Tja, jag fick inte min erfarenhet pÄ det enklaste sÀttet. Jag programmerade i Assembly pÄ den tiden dÄ man kunde förstÄ varenda instruktion. Det lÄter dumt, men sedan dess har Z80-instruktionsuppsÀttningen alltid funnits kvar i mitt huvud, i mitt minne. Jag kommer inte ihÄg folks namn inom en minut efter att ha pratat, men jag minns kod som skrevs för 40 Är sedan. Det Àr roligt, det ser ut som ett syndrom"idiot vetenskapsman".

OptimeringstrÀning pÄ lÄg nivÄ

Andrew: Finns det ett enklare sÀtt att komma in?

Klippa: Ja och nej. HĂ„rdvaran vi alla anvĂ€nder har inte förĂ€ndrats sĂ„ mycket över tiden. Alla anvĂ€nder x86, med undantag för Arm-smarttelefoner. Om du inte gör nĂ„gon form av hardcore-inbĂ€ddning, gör du samma sak. Okej, nĂ€sta. Instruktionerna har inte heller Ă€ndrats pĂ„ Ă„rhundraden. Du mĂ„ste gĂ„ och skriva nĂ„got i Assembly. Inte mycket, men tillrĂ€ckligt för att börja förstĂ„. Du ler, men jag talar helt seriöst. Du mĂ„ste förstĂ„ överensstĂ€mmelsen mellan sprĂ„k och hĂ„rdvara. Efter det mĂ„ste du gĂ„ och skriva lite och göra en liten leksakskompilator för lite leksakssprĂ„k. Leksaksliknande betyder att den mĂ„ste göras inom rimlig tid. Det kan vara superenkelt, men det mĂ„ste generera instruktioner. Handlingen att generera en instruktion hjĂ€lper dig att förstĂ„ kostnadsmodellen för bryggan mellan högnivĂ„koden som alla skriver och maskinkoden som körs pĂ„ hĂ„rdvaran. Denna korrespondens kommer att brĂ€nnas in i hjĂ€rnan nĂ€r kompilatorn skrivs. Även den enklaste kompilatorn. Efter det kan du börja titta pĂ„ Java och det faktum att dess semantiska avgrund Ă€r mycket djupare, och det Ă€r mycket svĂ„rare att bygga broar över det. I Java Ă€r det mycket svĂ„rare att förstĂ„ om vĂ„r bro blev bra eller dĂ„lig, vad som kommer att fĂ„ den att falla isĂ€r och vad som inte kommer att göra det. Men du behöver nĂ„gon form av utgĂ„ngspunkt dĂ€r du tittar pĂ„ koden och förstĂ„r: "ja, den hĂ€r gettern bör infogas varje gĂ„ng." Och sĂ„ visar det sig att det ibland hĂ€nder, förutom situationen nĂ€r metoden blir för stor, och JIT börjar infoga allt. Prestandan för sĂ„dana platser kan förutsĂ€gas direkt. Vanligtvis fungerar getters bra, men dĂ„ tittar du pĂ„ stora heta loopar och inser att det finns nĂ„gra funktionsanrop som flyter runt dĂ€r som inte vet vad de gör. Detta Ă€r problemet med den utbredda anvĂ€ndningen av getter, anledningen till att de inte Ă€r inbyggda Ă€r att det inte Ă€r klart om de Ă€r en getter. Om du har en superliten kodbas kan du helt enkelt komma ihĂ„g den och sedan sĂ€ga: det hĂ€r Ă€r en getter, och det hĂ€r Ă€r en setter. I en stor kodbas lever varje funktion sin egen historia, som i allmĂ€nhet inte Ă€r kĂ€nd för nĂ„gon. Profileraren sĂ€ger att vi förlorade 24 % av tiden pĂ„ nĂ„gon slinga och för att förstĂ„ vad den hĂ€r slingan gör mĂ„ste vi titta pĂ„ varje funktion inuti. Det Ă€r omöjligt att förstĂ„ detta utan att studera funktionen, och detta saktar allvarligt ner förstĂ„elsesprocessen. Det Ă€r dĂ€rför jag inte anvĂ€nder getters och setters, jag har nĂ„tt en ny nivĂ„!
Var fÄr man tag i kostnadsmodellen? NÄvÀl, du kan lÀsa nÄgot sÄklart... Men jag tror att det bÀsta sÀttet Àr att agera. Att göra en liten kompilator kommer att vara det bÀsta sÀttet att förstÄ kostnadsmodellen och passa in i ditt eget huvud. En liten kompilator som skulle vara lÀmplig för att programmera en mikrovÄgsugn Àr en uppgift för en nybörjare. Tja, jag menar, om du redan har programmeringskunskaper sÄ borde det rÀcka. Alla dessa saker som att analysera en strÀng som du har som nÄgot slags algebraiskt uttryck, extrahera instruktioner för matematiska operationer dÀrifrÄn i rÀtt ordning, ta rÀtt vÀrden frÄn register - allt detta görs pÄ en gÄng. Och medan du gör det kommer det att prÀglas i din hjÀrna. Jag tror att alla vet vad en kompilator gör. Och detta kommer att ge en förstÄelse för kostnadsmodellen.

Praktiska exempel pÄ prestationsförbÀttring

Andrew: Vad mer bör du vara uppmÀrksam pÄ nÀr du arbetar med produktivitet?

Klippa: Data struktur. Förresten, ja, jag har inte undervisat i dessa klasser pÄ lÀnge... Raketskola. Det var kul, men det krÀvde mycket anstrÀngning, och jag har ocksÄ ett liv! OK. SÄ, i en av de stora och intressanta klasserna, "Var tar din prestation vÀgen", gav jag eleverna ett exempel: tvÄ och en halv gigabyte fintech-data lÀstes frÄn en CSV-fil och sedan var de tvungna att berÀkna antalet sÄlda produkter . Regelbunden tick-marknadsdata. UDP-paket konverterade till textformat sedan 70-talet. Chicago Mercantile Exchange - alla möjliga saker som smör, majs, sojabönor, sÄdana saker. Det var nödvÀndigt att rÀkna dessa produkter, antalet transaktioner, den genomsnittliga rörelsevolymen för pengar och varor etc. Det Àr ganska enkel handelsmatematik: hitta produktkoden (det Àr 1-2 tecken i hashtabellen), hÀmta beloppet, lÀgg till det i en av handelsseten, lÀgg till volym, lÀgg till vÀrde och ett par andra saker. Mycket enkel matematik. Leksaksimplementeringen var vÀldigt enkel: allt finns i en fil, jag lÀser filen och gÄr igenom den, delar upp enskilda poster i Java-strÀngar, letar efter de nödvÀndiga sakerna i dem och lÀgger ihop dem enligt matematiken som beskrivs ovan. Och det fungerar i lite lÄg hastighet.

Med detta tillvĂ€gagĂ„ngssĂ€tt Ă€r det uppenbart vad som hĂ€nder, och parallell berĂ€kning hjĂ€lper inte, eller hur? Det visar sig att en femfaldig ökning av prestanda kan uppnĂ„s helt enkelt genom att vĂ€lja rĂ€tt datastrukturer. Och detta överraskar Ă€ven erfarna programmerare! I just mitt fall var tricket att man inte skulle göra minnesallokeringar i en het loop. Tja, detta Ă€r inte hela sanningen, men i allmĂ€nhet - du bör inte markera "en gĂ„ng i X" nĂ€r X Ă€r tillrĂ€ckligt stort. NĂ€r X Ă€r tvĂ„ och en halv gigabyte, bör du inte tilldela nĂ„got "en gĂ„ng per bokstav", eller "en gĂ„ng per rad", eller "en gĂ„ng per fĂ€lt", nĂ„got liknande. Det Ă€r hĂ€r tiden spenderas. Hur fungerar detta ens? FörestĂ€ll dig att jag ringer ett samtal String.split() eller BufferedReader.readLine(). Readline gör en strĂ€ng frĂ„n en uppsĂ€ttning byte som kom över nĂ€tverket, en gĂ„ng för varje rad, för var och en av de hundratals miljoner raderna. Jag tar den hĂ€r raden, analyserar den och kastar den. Varför slĂ€nger jag det - ja, jag har redan bearbetat det, det Ă€r allt. SĂ„ för varje byte som lĂ€ses frĂ„n dessa 2.7G kommer tvĂ„ tecken att skrivas i raden, det vill sĂ€ga redan 5.4G, och jag behöver dem inte för nĂ„got mer, sĂ„ de slĂ€ngs. Tittar man pĂ„ minnesbandbredden sĂ„ laddar vi 2.7G som gĂ„r genom minnet och minnesbussen i processorn och sedan skickas dubbelt sĂ„ mycket till linjen som ligger i minnet och allt detta slits nĂ€r varje ny linje skapas. Men jag behöver lĂ€sa den, hĂ„rdvaran lĂ€ser den, Ă€ven om allt Ă€r slitet senare. Och jag mĂ„ste skriva ner det eftersom jag skapade en rad och cacherna Ă€r fulla - cachen kan inte ta emot 2.7G. SĂ„ för varje byte jag lĂ€ser lĂ€ser jag ytterligare tvĂ„ byte och skriver tvĂ„ byte till, och i slutĂ€ndan har de ett förhĂ„llande pĂ„ 4:1 - i detta förhĂ„llande slösar vi med minnesbandbredd. Och sĂ„ visar det sig att om jag gör det String.split() – det Ă€r inte sista gĂ„ngen jag gör det hĂ€r, det kan finnas ytterligare 6-7 fĂ€lt inuti. SĂ„ den klassiska koden att lĂ€sa CSV och sedan analysera strĂ€ngarna resulterar i ett slöseri med minnesbandbredd pĂ„ cirka 14:1 i förhĂ„llande till vad du faktiskt skulle vilja ha. Om du slĂ€nger dessa val kan du fĂ„ en femfaldig speedup.

Och det Ă€r inte sĂ„ svĂ„rt. Om du tittar pĂ„ koden frĂ„n rĂ€tt vinkel blir det hela ganska enkelt nĂ€r du vĂ€l inser problemet. Du bör inte sluta allokera minne helt: det enda problemet Ă€r att du allokerar nĂ„got och det dör omedelbart, och lĂ€ngs vĂ€gen brĂ€nner det en viktig resurs, som i det hĂ€r fallet Ă€r minnesbandbredd. Och allt detta resulterar i en minskning av produktiviteten. PĂ„ x86 behöver du vanligtvis aktivt brĂ€nna processorcykler, men hĂ€r brĂ€nde du upp allt minne mycket tidigare. Lösningen Ă€r att minska mĂ€ngden utslĂ€pp. 
Den andra delen av problemet Ă€r att om du kör profilern nĂ€r minnesremsan tar slut, precis nĂ€r det hĂ€nder, sĂ„ vĂ€ntar du vanligtvis pĂ„ att cachen ska komma tillbaka eftersom den Ă€r full av skrĂ€p som du just producerat, alla dessa rader. DĂ€rför blir varje laddning eller butiksoperation lĂ„ngsam, eftersom de leder till cachemissar - hela cachen har blivit lĂ„ngsam och vĂ€ntar pĂ„ att skrĂ€pet ska lĂ€mna det. DĂ€rför kommer profileraren bara att visa varmt slumpmĂ€ssigt brus utsmetat över hela slingan - det kommer inte att finnas nĂ„gon separat hetsinstruktion eller plats i koden. Bara buller. Och om du tittar pĂ„ GC-cyklerna Ă€r de alla Young Generation och supersnabba - mikrosekunder eller millisekunder maximalt. NĂ€r allt kommer omkring dör allt detta minne omedelbart. Du tilldelar miljarder gigabyte, och han skĂ€r dem, och skĂ€r dem, och skĂ€r dem igen. Allt detta hĂ€nder vĂ€ldigt snabbt. Det visar sig att det finns billiga GC-cykler, varmt ljud lĂ€ngs hela cykeln, men vi vill fĂ„ en 5x speedup. I det hĂ€r ögonblicket borde nĂ„got stĂ€ngas i ditt huvud och ljuda: "varför Ă€r det hĂ€r?!" Memory strip overflow visas inte i den klassiska debuggern, du mĂ„ste köra hĂ„rdvaruprestandarĂ€knaren och se den sjĂ€lv och direkt. Men detta kan inte direkt misstĂ€nkas frĂ„n dessa tre symtom. Det tredje symptomet Ă€r nĂ€r du tittar pĂ„ det du lyfter fram, frĂ„gar profileraren och han svarar: "Du gjorde en miljard rader, men GC fungerade gratis." SĂ„ fort detta hĂ€nder inser du att du har skapat för mĂ„nga objekt och brĂ€nt upp hela minnesbanan. Det finns ett sĂ€tt att reda ut detta, men det Ă€r inte sjĂ€lvklart. 

Problemet ligger i datastrukturen: den nakna strukturen som ligger bakom allt som hĂ€nder, den Ă€r för stor, den Ă€r 2.7 G pĂ„ disken, sĂ„ att göra en kopia av den hĂ€r saken Ă€r mycket oönskad - du vill ladda den frĂ„n nĂ€tverkets bytebuffert omedelbart in i registren, för att inte lĂ€sa-skriva till raden fram och tillbaka fem gĂ„nger. TyvĂ€rr ger Java inte dig ett sĂ„dant bibliotek som en del av JDK som standard. Men det hĂ€r Ă€r trivialt, eller hur? I grund och botten Ă€r dessa 5-10 rader kod som kommer att anvĂ€ndas för att implementera din egen buffrade strĂ€ngladdare, som upprepar strĂ€ngklassens beteende, samtidigt som den Ă€r ett omslag runt den underliggande bytebufferten. Som ett resultat visar det sig att du arbetar nĂ€stan som med strĂ€ngar, men i sjĂ€lva verket rör sig pekare till bufferten dit, och de rĂ„a byten kopieras inte nĂ„gonstans, och dĂ€rför Ă„teranvĂ€nds samma buffertar om och om igen, och operativsystemet tar gĂ€rna pĂ„ sig saker som det Ă€r designat för, som dold dubbelbuffring av dessa byte-buffertar, och du slipar inte lĂ€ngre genom en oĂ€ndlig ström av onödig data. FörstĂ„r du förresten att nĂ€r man arbetar med GC Ă€r det garanterat att varje minnesallokering inte kommer att vara synlig för processorn efter den senaste GC-cykeln? Allt detta kan dĂ€rför omöjligen finnas i cachen, och dĂ„ uppstĂ„r en 100% garanterad miss. NĂ€r du arbetar med en pekare, pĂ„ x86, tar det 1-2 klockcykler att subtrahera ett register frĂ„n minnet, och sĂ„ fort detta hĂ€nder betalar du, betalar, betalar, eftersom minnet Ă€r pĂ„. NIO cacher – och detta Ă€r kostnaden för minnesallokering. Riktigt vĂ€rde.

Datastrukturer Àr med andra ord det svÄraste att Àndra. Och nÀr du vÀl inser att du har valt fel datastruktur som kommer att döda prestanda senare, finns det vanligtvis mycket arbete att göra, men om du inte gör det kommer saker och ting att bli vÀrre. Först och frÀmst mÄste du tÀnka pÄ datastrukturer, detta Àr viktigt. Huvudkostnaden hÀr faller pÄ feta datastrukturer, som börjar anvÀndas i stil med "Jag kopierade datastruktur X till datastruktur Y eftersom jag gillar formen pÄ Y bÀttre." Men kopieringsoperationen (som verkar billig) slösar faktiskt minnesbandbredd och det Àr dÀr all bortkastad exekveringstid ligger begravd. Om jag har en gigantisk strÀng av JSON och jag vill förvandla den till ett strukturerat DOM-trÀd med POJO:er eller nÄgot, kommer operationen att tolka den strÀngen och bygga POJO, och sedan komma Ät POJO igen senare, resultera i onödiga kostnader - det Àr inte billigt. Förutom om du springer runt POJOs mycket oftare Àn du springer runt ett snöre. Direkt kan du istÀllet försöka dekryptera strÀngen och extrahera bara det du behöver dÀrifrÄn, utan att förvandla det till nÄgon POJO. Om allt detta hÀnder pÄ en vÀg frÄn vilken maximal prestanda krÀvs, inga POJOs för dig, mÄste du pÄ nÄgot sÀtt grÀva i linjen direkt.

Varför skapa ditt eget programmeringssprÄk

Andrew: Du sa att för att förstÄ kostnadsmodellen mÄste du skriva ditt eget lilla sprÄk...

Klippa: Inte ett sprĂ„k, utan en kompilator. Ett sprĂ„k och en kompilator Ă€r tvĂ„ olika saker. Den viktigaste skillnaden Ă€r i ditt huvud. 

Andrew: Förresten, sÄ vitt jag vet, experimenterar du med att skapa dina egna sprÄk. För vad?

Klippa: Eftersom jag kan! Jag Àr halvpensionÀr, sÄ det hÀr Àr min hobby. Jag har implementerat andras sprÄk hela mitt liv. Jag arbetade ocksÄ mycket med min kodningsstil. Och Àven för att jag ser problem pÄ andra sprÄk. Jag ser att det finns bÀttre sÀtt att göra bekanta saker. Och jag skulle anvÀnda dem. Jag Àr bara trött pÄ att se problem i mig sjÀlv, i Java, i Python, pÄ vilket annat sprÄk som helst. Jag skriver nu i React Native, JavaScript och Elm som en hobby som inte handlar om pension, utan om aktivt arbete. Jag skriver ocksÄ i Python och kommer troligen att fortsÀtta arbeta med maskininlÀrning för Java-backends. Det finns mÄnga populÀra sprÄk och de har alla intressanta funktioner. Alla Àr bra pÄ sitt sÀtt och du kan försöka fÄ ihop alla dessa funktioner. SÄ jag studerar saker som intresserar mig, sprÄkets beteende, och försöker komma pÄ rimlig semantik. Och Àn sÄ lÀnge har jag lyckats! För tillfÀllet kÀmpar jag med minnessemantik, eftersom jag vill ha det som i C och Java, och fÄ en stark minnesmodell och minnessemantik för laddningar och butiker. Har samtidigt automatisk typinferens som i Haskell. HÀr försöker jag blanda Haskell-liknande typinferens med minnesarbete i bÄde C och Java. Det Àr vad jag har gjort de senaste 2-3 mÄnaderna till exempel.

Andrew: Om du bygger ett sprÄk som tar bÀttre aspekter frÄn andra sprÄk, tror du att nÄgon kommer att göra tvÀrtom: ta dina idéer och anvÀnda dem?

Klippa: Det Ă€r precis sĂ„ nya sprĂ„k visas! Varför liknar Java C? Eftersom C hade en bra syntax som alla förstod och Java inspirerades av denna syntax, lade till typsĂ€kerhet, kontroll av arraygrĂ€nser, GC, och de förbĂ€ttrade Ă€ven en del saker frĂ„n C. De lade till sina egna. Men de inspirerades rĂ€tt mycket, eller hur? Alla stĂ„r pĂ„ axlarna av jĂ€ttarna som kom före dig – det Ă€r sĂ„ framsteg görs.

Andrew: Som jag förstÄr det kommer ditt sprÄk att vara minnessÀkert. Har du funderat pÄ att implementera nÄgot som en lÄna checker frÄn Rust? Har du tittat pÄ honom, vad tycker du om honom?

Klippa: Tja, jag har skrivit C i evigheter, med all denna malloc och gratis, och manuellt hanterat livstiden. Du vet, 90-95 % av manuellt styrd livslĂ€ngd har samma struktur. Och det Ă€r vĂ€ldigt, vĂ€ldigt smĂ€rtsamt att göra det manuellt. Jag skulle vilja att kompilatorn helt enkelt berĂ€ttar för dig vad som hĂ€nder dĂ€r och vad du uppnĂ„dde med dina handlingar. För vissa saker, lĂ„n checker gör detta ur lĂ„dan. Och den ska automatiskt visa information, förstĂ„ allt och inte ens belasta mig med att presentera denna förstĂ„else. Den mĂ„ste Ă„tminstone göra lokal escape-analys, och bara om den misslyckas, mĂ„ste den lĂ€gga till typkommentarer som kommer att beskriva livslĂ€ngden - och ett sĂ„dant schema Ă€r mycket mer komplicerat Ă€n en lĂ„nekontroll, eller faktiskt vilken befintlig minneskontroll som helst. Valet mellan "allt Ă€r bra" och "Jag förstĂ„r ingenting" - nej, det mĂ„ste finnas nĂ„got bĂ€ttre. 
SÄ, som nÄgon som har skrivit mycket kod i C, tror jag att det Àr det viktigaste att ha stöd för automatisk livstidskontroll. Jag Àr ocksÄ trött pÄ hur mycket Java anvÀnder minne och det frÀmsta klagomÄlet Àr GC. NÀr du allokerar minne i Java fÄr du inte tillbaka det minne som var lokalt vid den senaste GC-cykeln. Detta Àr inte fallet i sprÄk med mer exakt minneshantering. Ringer man malloc fÄr man direkt det minne som oftast bara anvÀndes. Vanligtvis gör man nÄgra tillfÀlliga saker med minnet och returnerar det omedelbart. Och den ÄtervÀnder omedelbart till mallocpoolen, och nÀsta malloccykel drar ut den igen. DÀrför reduceras faktisk minnesanvÀndning till uppsÀttningen av levande objekt vid en given tidpunkt, plus lÀckor. Och om allt inte lÀcker pÄ ett helt oanstÀndigt sÀtt, hamnar det mesta av minnet i cacher och processorn, och det fungerar snabbt. Men krÀver mycket manuell minneshantering med malloc och gratis anropade i rÀtt ordning, pÄ rÀtt plats. Rust klarar detta ordentligt pÄ egen hand, och ger i mÄnga fall Ànnu bÀttre prestanda, eftersom minnesförbrukningen begrÀnsas till bara den aktuella berÀkningen - i motsats till att vÀnta pÄ nÀsta GC-cykel för att frigöra minne. Som ett resultat fick vi ett mycket intressant sÀtt att förbÀttra prestandan. Och ganska kraftfullt - jag menar, jag gjorde sÄdana saker nÀr jag bearbetade data för fintech, och detta gjorde att jag kunde fÄ en hastighet pÄ cirka fem gÄnger. Det Àr ett ganska stort uppsving, sÀrskilt i en vÀrld dÀr processorerna inte blir snabbare och vi fortfarande vÀntar pÄ förbÀttringar.

KarriÀr som prestationsingenjör

Andrew: Jag skulle ocksÄ vilja frÄga runt om karriÀrer i allmÀnhet. Du blev framtrÀdande med ditt JIT-arbete pÄ HotSpot och flyttade sedan till Azul, som ocksÄ Àr ett JVM-företag. Men vi arbetade redan mer med hÄrdvara Àn mjukvara. Och sÄ gick de plötsligt över till Big Data och Machine Learning, och sedan till bedrÀgeriupptÀckt. Hur hÀnde det hÀr? Det Àr vÀldigt olika utvecklingsomrÄden.

Klippa: Jag har programmerat ganska lĂ€nge och har lyckats ta mĂ„nga olika klasser. Och nĂ€r folk sĂ€ger: "Ă„h, det var du som gjorde JIT för Java!", Ă€r det alltid roligt. Men innan dess arbetade jag pĂ„ en klon av PostScript – sprĂ„ket som Apple en gĂ„ng anvĂ€nde för sina laserskrivare. Och innan dess gjorde jag en implementering av Forth-sprĂ„ket. Jag tror att det gemensamma temat för mig Ă€r verktygsutveckling. Hela mitt liv har jag gjort verktyg som andra mĂ€nniskor skriver sina coola program med. Men jag var ocksĂ„ involverad i utvecklingen av operativsystem, drivrutiner, felsökare pĂ„ kĂ€rnnivĂ„, sprĂ„k för OS-utveckling, vilket började trivialt, men med tiden blev mer och mer komplext. Men huvudĂ€mnet Ă€r fortfarande utvecklingen av verktyg. En stor del av mitt liv gick mellan Azul och Sun, och det handlade om Java. Men nĂ€r jag kom in pĂ„ Big Data och Machine Learning, tog jag pĂ„ mig min snygga hatt igen och sa: "Åh, nu har vi ett icke-trivialt problem, och det finns mĂ„nga intressanta saker pĂ„ gĂ„ng och mĂ€nniskor som gör saker." Det hĂ€r Ă€r en fantastisk utvecklingsvĂ€g att ta.

Ja, jag Ă€lskar verkligen distribuerad datoranvĂ€ndning. Mitt första jobb var som student i C, pĂ„ ett reklamprojekt. Detta var distribuerad datoranvĂ€ndning pĂ„ Zilog Z80-chips som samlade in data för analog OCR, producerad av en riktig analog analysator. Det var ett coolt och helt galet Ă€mne. Men det fanns problem, nĂ„gon del kĂ€ndes inte igen korrekt, sĂ„ man fick ta ut en bild och visa den för en person som redan kunde lĂ€sa med ögonen och rapportera vad det stod, och dĂ€rför fanns det jobb med data, och dessa jobb hade sitt eget sprĂ„k. Det fanns en backend som bearbetade allt detta - Z80s körde parallellt med vt100-terminaler igĂ„ng - en per person, och det fanns en parallell programmeringsmodell pĂ„ Z80. NĂ„got gemensamt minne som delas av alla Z80s inom en stjĂ€rnkonfiguration; Bakplanet delades ocksĂ„, och hĂ€lften av RAM-minnet delades inom nĂ€tverket, och en annan hĂ€lften var privat eller gick till nĂ„got annat. Ett meningsfullt komplext parallellt distribuerat system med delat... semi-delat minne. NĂ€r var det hĂ€r... Jag kan inte ens minnas, nĂ„gonstans i mitten av 80-talet. Ganska lĂ€nge sedan. 
Ja, lĂ„t oss anta att 30 Ă„r Ă€r ganska lĂ€nge sedan. Problem relaterade till distribuerad datoranvĂ€ndning har funnits ganska lĂ€nge; mĂ€nniskor har lĂ€nge varit i krig med Beowulf-kluster. SĂ„dana kluster ser ut som... Till exempel: det finns Ethernet och din snabba x86 Ă€r ansluten till detta Ethernet, och nu vill du fĂ„ falskt delat minne, eftersom ingen kunde göra distribuerad datorkodning dĂ„, det var för svĂ„rt och dĂ€rför fanns det var falskt delat minne med skyddsminnessidor pĂ„ x86, och om du skrev till den hĂ€r sidan, dĂ„ sa vi till andra processorer att om de kommer Ă„t samma delade minne, skulle det behöva laddas frĂ„n dig, och dĂ€rmed nĂ„got liknande ett protokoll för att stödja cachekoherens dök upp och programvara för detta. Intressant koncept. Det verkliga problemet var naturligtvis nĂ„got annat. Allt detta fungerade, men man fick snabbt prestandaproblem, eftersom ingen förstod prestandamodellerna pĂ„ en tillrĂ€ckligt bra nivĂ„ – vilka minnesĂ„tkomstmönster som fanns, hur man ser till att noderna inte plingade varandra oĂ€ndligt osv.

Det jag kom fram till i H2O Ă€r att det Ă€r utvecklarna sjĂ€lva som Ă€r ansvariga för att avgöra var parallellismen döljer sig och var den inte Ă€r det. Jag kom pĂ„ en kodningsmodell som gjorde det enkelt och enkelt att skriva högpresterande kod. Men att skriva lĂ„ngsamt körande kod Ă€r svĂ„rt, det kommer att se dĂ„ligt ut. Du mĂ„ste seriöst försöka skriva lĂ„ngsam kod, du mĂ„ste anvĂ€nda icke-standardiserade metoder. Bromskoden syns vid första anblicken. Som ett resultat brukar du skriva kod som gĂ„r snabbt, men du mĂ„ste ta reda pĂ„ vad du ska göra vid delat minne. Allt detta Ă€r knutet till stora arrayer och beteendet dĂ€r liknar icke-flyktiga stora arrayer i parallell Java. Jag menar, förestĂ€ll dig att tvĂ„ trĂ„dar skriver till en parallell array, en av dem vinner och den andra förlorar följaktligen, och du vet inte vilken som Ă€r vilken. Om de inte Ă€r flyktiga kan bestĂ€llningen vara vad du vill – och det hĂ€r fungerar riktigt bra. MĂ€nniskor bryr sig verkligen om operationsordningen, de lĂ€gger flyktigt pĂ„ rĂ€tt stĂ€llen och förvĂ€ntar sig minnesrelaterade prestationsproblem pĂ„ rĂ€tt stĂ€llen. Annars skulle de helt enkelt skriva kod i form av loopar frĂ„n 1 till N, dĂ€r N Ă€r nĂ„gra biljoner, i hopp om att alla komplexa fall automatiskt ska bli parallella - och det fungerar inte dĂ€r. Men i H2O Ă€r detta varken Java eller Scala, du kan betrakta det som "Java minus minus" om du vill. Detta Ă€r en mycket tydlig programmeringsstil och liknar att skriva enkel C- eller Java-kod med loopar och arrayer. Men samtidigt kan minnet bearbetas i terabyte. Jag anvĂ€nder fortfarande H2O. Jag anvĂ€nder det dĂ„ och dĂ„ i olika projekt – och det Ă€r fortfarande det snabbaste, dussintals gĂ„nger snabbare Ă€n sina konkurrenter. Om du gör Big Data med kolumnĂ€r data Ă€r det vĂ€ldigt svĂ„rt att slĂ„ H2O.

Tekniska utmaningar

Andrew: Vilken har varit din största utmaning i hela din karriÀr?

Klippa: Diskuterar vi den tekniska eller icke-tekniska delen av frĂ„gan? Jag skulle sĂ€ga att de största utmaningarna inte Ă€r tekniska. 
NÀr det gÀller tekniska utmaningar. Jag besegrade dem helt enkelt. Jag vet inte ens vad den största var, men det fanns nÄgra ganska intressanta som tog ganska lÄng tid, mental kamp. NÀr jag gick till Sun var jag sÀker pÄ att jag skulle göra en snabb kompilator, och ett gÀng seniorer sa som svar att jag aldrig skulle lyckas. Men jag följde den hÀr vÀgen, skrev ner en kompilator till registerfördelaren, och det gick ganska snabbt. Den var lika snabb som modern C1, men allokatorn var mycket lÄngsammare dÄ, och i efterhand var det ett stort problem med datastrukturen. Jag behövde det för att skriva en grafisk registerfördelare och jag förstod inte dilemmat mellan koduttrycksförmÄga och hastighet, som fanns pÄ den tiden och var vÀldigt viktigt. Det visade sig att datastrukturen vanligtvis överstiger cachestorleken pÄ den tidens x86:or, och dÀrför, om jag frÄn början antog att registerfördelaren skulle arbeta ut 5-10 procent av den totala jittertiden, sÄ visade det sig i verkligheten vara 50 procent.

Allt eftersom tiden gick blev kompilatorn renare och effektivare, slutade generera fruktansvÀrd kod i fler fall, och prestandan började allt mer likna vad en C-kompilator producerar. Om du förstÄs inte skriver nÄgot skit som inte ens C snabbar pÄ . Om du skriver kod som C fÄr du prestanda som C i fler fall. Och ju lÀngre du gick, desto oftare fick du kod som asymptotiskt sammanföll med nivÄ C, registerfördelaren började se ut som nÄgot komplett... oavsett om din kod gÄr snabbt eller lÄngsamt. Jag fortsatte att arbeta med fördelaren för att fÄ den att göra bÀttre urval. Han blev lÄngsammare och lÄngsammare, men han presterade bÀttre och bÀttre i de fall ingen annan orkade. Jag kunde dyka in i en registerfördelare, begrava en mÄnads arbete dÀr, och plötsligt skulle hela koden börja exekveras 5 % snabbare. Detta hÀnde gÄng pÄ gÄng och registerfördelaren blev nÄgot av ett konstverk - alla Àlskade det eller hatade det, och folk frÄn akademin stÀllde frÄgor om Àmnet "varför görs allt pÄ det hÀr sÀttet", varför inte linjeskanning, och vad Àr skillnaden. Svaret Àr fortfarande detsamma: en allokator baserad pÄ graffÀrgning plus mycket noggrant arbete med buffertkoden Àr lika med ett segervapen, den bÀsta kombinationen som ingen kan besegra. Och det hÀr Àr en ganska icke sjÀlvklar sak. Allt annat som kompilatorn gör dÀr Àr ganska vÀl studerade saker, Àven om de ocksÄ har förts till konstnivÄ. Jag gjorde alltid saker som skulle göra kompilatorn till ett konstverk. Men inget av detta var nÄgot extraordinÀrt - förutom registerfördelaren. Tricket Àr att vara försiktig hugga ner under belastning och, om detta hÀnder (jag kan förklara mer detaljerat om det Àr intresserat), betyder det att du kan inline mer aggressivt, utan risk för att ramla omkull i prestationsschemat. PÄ den tiden fanns det ett gÀng fullskaliga kompilatorer, hÀngda med grannlÄt och visselpipor, som hade registerfördelare, men ingen annan kunde göra det.

Problemet Ă€r att om du lĂ€gger till metoder som Ă€r föremĂ„l för inlining, ökar och ökar inliningomrĂ„det, övertrĂ€ffar uppsĂ€ttningen av anvĂ€nda vĂ€rden omedelbart antalet register, och du mĂ„ste klippa dem. Den kritiska nivĂ„n kommer vanligtvis nĂ€r fördelaren ger upp, och en bra kandidat för ett spill Ă€r vĂ€rd en annan, kommer du att sĂ€lja nĂ„gra allmĂ€nt vilda saker. VĂ€rdet av inlining hĂ€r Ă€r att du förlorar en del av overheaden, overhead för att ringa och spara, du kan se vĂ€rdena inuti och kan optimera dem ytterligare. Kostnaden för inlining Ă€r att ett stort antal levande vĂ€rden bildas, och om din registerfördelare brinner upp mer Ă€n nödvĂ€ndigt, förlorar du omedelbart. DĂ€rför har de flesta fördelare ett problem: nĂ€r inlining korsar en viss linje, börjar allt i vĂ€rlden skĂ€ras ner och produktiviteten kan spolas ner i toaletten. De som implementerar kompilatorn lĂ€gger till nĂ„gra heuristik: till exempel att sluta inlina, börja med en tillrĂ€ckligt stor storlek, eftersom allokeringarna kommer att förstöra allt. SĂ„ hĂ€r bildas en kink i prestandagrafen - du inline, inline, prestationen vĂ€xer sakta - och sedan boom! – den faller ner som en snabb domkraft för att du fodrat för mycket. SĂ„ hĂ€r fungerade allt före Javas tillkomst. Java krĂ€ver mycket mer inlining, sĂ„ jag var tvungen att göra min allokator mycket mer aggressiv sĂ„ att den planar ut snarare Ă€n att kraschar, och om du infogar för mycket, börjar den att spilla, men dĂ„ kommer "ingen mer spillning"-ögonblicket fortfarande. Det hĂ€r Ă€r en intressant observation och den kom bara till mig frĂ„n ingenstans, inte uppenbar, men den gav bra resultat. Jag tog upp aggressiv inlining och det tog mig till platser dĂ€r Java och C-prestanda fungerar sida vid sida. De Ă€r verkligen nĂ€ra - jag kan skriva Java-kod som Ă€r betydligt snabbare Ă€n C-kod och sĂ„nt, men i genomsnitt, i den stora bilden av saker, Ă€r de ungefĂ€r jĂ€mförbara. Jag tror att en del av denna merit Ă€r registerfördelaren, som gör att jag kan lĂ€gga in sĂ„ dumt som möjligt. Jag lĂ€gger bara in allt jag ser. FrĂ„gan hĂ€r Ă€r om allokatorn fungerar bra, om resultatet Ă€r en intelligent fungerande kod. Detta var en stor utmaning: att förstĂ„ allt detta och fĂ„ det att fungera.

Lite om registertilldelning och multikÀrnor

vladimir: Problem som registertilldelning verkar vara nÄgot slags evigt, oÀndligt Àmne. Jag undrar om det nÄgonsin har funnits en idé som verkade lovande och sedan misslyckats i praktiken?

Klippa: Visst! Registerallokering Ă€r ett omrĂ„de dĂ€r man försöker hitta lite heuristik för att lösa ett NP-komplett problem. Och du kan aldrig uppnĂ„ en perfekt lösning, eller hur? Detta Ă€r helt enkelt omöjligt. Titta, Ahead of Time-kompilering - den fungerar ocksĂ„ dĂ„ligt. Samtalet hĂ€r handlar om nĂ„gra genomsnittliga fall. Om typisk prestation, sĂ„ du kan gĂ„ och mĂ€ta nĂ„got som du tycker Ă€r bra typisk prestation – du jobbar trots allt pĂ„ att förbĂ€ttra det! Registertilldelning Ă€r ett Ă€mne som handlar om prestation. NĂ€r man vĂ€l har den första prototypen fungerar den och mĂ„lar det som behövs, prestationsarbetet börjar. Du mĂ„ste lĂ€ra dig att mĂ€ta bra. Varför Ă€r det viktigt? Om du har tydliga data kan du titta pĂ„ olika omrĂ„den och se: ja, det hjĂ€lpte hĂ€r, men det var dĂ€r allt gick sönder! NĂ„gra bra idĂ©er dyker upp, man lĂ€gger till ny heuristik och plötsligt börjar allt fungera lite bĂ€ttre i genomsnitt. Eller sĂ„ startar den inte. Jag hade ett gĂ€ng fall dĂ€r vi kĂ€mpade för de fem procentiga prestationerna som skilde vĂ„r utveckling frĂ„n den tidigare fördelaren. Och varje gĂ„ng ser det ut sĂ„ hĂ€r: nĂ„gonstans vinner du, nĂ„gonstans förlorar du. Om du har bra verktyg för resultatanalys kan du hitta de förlorade idĂ©erna och förstĂ„ varför de misslyckas. Kanske Ă€r det vĂ€rt att lĂ„ta allt vara som det Ă€r, eller kanske ta ett mer seriöst grepp om att finjustera, eller gĂ„ ut och fixa nĂ„got annat. Det Ă€r en hel massa saker! Jag gjorde det hĂ€r coola hacket, men jag behöver ocksĂ„ det hĂ€r, och det hĂ€r, och det hĂ€r - och deras totala kombination ger nĂ„gra förbĂ€ttringar. Och ensamma kan misslyckas. Detta Ă€r karaktĂ€ren av prestationsarbete pĂ„ NP-kompletta problem.

vladimir: Man fÄr en kÀnsla av att saker som att mÄla i allokatorer Àr ett problem som redan Àr löst. Tja, det Àr bestÀmt för dig, att döma av vad du sÀger, sÄ Àr det ens vÀrt det dÄ...

Klippa: Det Ă€r inte löst som sĂ„dant. Det Ă€r du som mĂ„ste göra det till "löst". Det finns svĂ„ra problem och de mĂ„ste lösas. NĂ€r detta Ă€r gjort Ă€r det dags att arbeta med produktiviteten. Du mĂ„ste nĂ€rma dig detta arbete i enlighet med detta - gör benchmarks, samla in mĂ€tvĂ€rden, förklara situationer nĂ€r, nĂ€r du rullade tillbaka till en tidigare version, ditt gamla hack började fungera igen (eller vice versa, slutade). Och ge inte upp förrĂ€n du uppnĂ„tt nĂ„got. Som jag redan sa, om det finns coola idĂ©er som inte fungerade, men nĂ€r det gĂ€ller tilldelning av idĂ©register Ă€r det ungefĂ€r oĂ€ndligt. Du kan till exempel lĂ€sa vetenskapliga publikationer. Även om nu detta omrĂ„de har börjat röra sig mycket lĂ„ngsammare och har blivit tydligare Ă€n i sin ungdom. Men det finns otaliga mĂ€nniskor som arbetar inom detta omrĂ„de och alla deras idĂ©er Ă€r vĂ€rda att pröva, de vĂ€ntar alla i kulisserna. Och du kan inte sĂ€ga hur bra de Ă€r om du inte provar dem. Hur vĂ€l integrerar de med allt annat i din allokator, eftersom en allokator gör mĂ„nga saker, och vissa idĂ©er kommer inte att fungera i din specifika allocator, men i en annan tilldelare kommer de att göra det lĂ€tt. Det huvudsakliga sĂ€ttet att vinna för allokatorn Ă€r att dra de lĂ„ngsamma sakerna utanför huvudbanan och tvinga den att dela lĂ€ngs grĂ€nserna för de lĂ„ngsamma banorna. SĂ„ om du vill köra en GC, ta den lĂ„ngsamma vĂ€gen, deoptimera, kasta ett undantag, allt det dĂ€r - du vet att dessa saker Ă€r relativt sĂ€llsynta. Och de Ă€r verkligen sĂ€llsynta, kollade jag. Du gör extra arbete och det tar bort mĂ„nga av restriktionerna pĂ„ dessa lĂ„ngsamma stigar, men det spelar egentligen ingen roll eftersom de Ă€r lĂ„ngsamma och sĂ€llan fĂ€rdas. Till exempel en nollpekare - det hĂ€nder aldrig, eller hur? Du mĂ„ste ha flera vĂ€gar för olika saker, men de bör inte störa den huvudsakliga. 

vladimir: Vad tycker du om flera kĂ€rnor, nĂ€r det finns tusentals kĂ€rnor samtidigt? Är detta en anvĂ€ndbar sak?

Klippa: FramgÄngen för GPU:n visar att den Àr ganska anvÀndbar!

vladimir: De Àr ganska specialiserade. Hur Àr det med processorer för allmÀnna ÀndamÄl?

Klippa: Tja, det var Azuls affĂ€rsmodell. Svaret kom tillbaka i en tid dĂ„ folk verkligen Ă€lskade förutsĂ€gbara prestationer. Det var svĂ„rt att skriva parallellkod dĂ„. H2O-kodningsmodellen Ă€r mycket skalbar, men den Ă€r inte en generell modell. Kanske lite mer allmĂ€nt Ă€n nĂ€r man anvĂ€nder en GPU. Pratar vi om komplexiteten i att utveckla en sĂ„dan sak eller komplexiteten i att anvĂ€nda den? Till exempel, Azul lĂ€rde mig en intressant lĂ€xa, en ganska icke-uppenbar sĂ„dan: smĂ„ cacher Ă€r normala. 

Den största utmaningen i livet

vladimir: Hur Àr det med icke-tekniska utmaningar?

Klippa: Den största utmaningen var att inte vara... snĂ€ll och trevlig mot mĂ€nniskor. Och som ett resultat befann jag mig hela tiden i extremt konfliktsituationer. De dĂ€r jag visste att saker gick fel, men inte visste hur jag skulle gĂ„ vidare med dessa problem och inte kunde hantera dem. MĂ„nga lĂ„ngvariga problem, som varade i decennier, uppstod pĂ„ detta sĂ€tt. Att Java har C1- och C2-kompilatorer Ă€r en direkt följd av detta. Att det inte fanns nĂ„gon kompilering pĂ„ flera nivĂ„er i Java tio Ă„r i rad Ă€r ocksĂ„ en direkt konsekvens. Det Ă€r uppenbart att vi behövde ett sĂ„dant system, men det Ă€r inte sjĂ€lvklart varför det inte fanns. Jag hade problem med en ingenjör... eller en grupp ingenjörer. En gĂ„ng i tiden nĂ€r jag började jobba pĂ„ Sun var jag... Okej, inte bara dĂ„, jag har i allmĂ€nhet alltid min egen Ă„sikt om allt. Och jag trodde att det var sant att du bara kunde ta den hĂ€r sanningen om dig och sĂ€ga det direkt. Speciellt eftersom jag hade chockerande rĂ€tt för det mesta. Och om du inte gillar det hĂ€r tillvĂ€gagĂ„ngssĂ€ttet... speciellt om du uppenbarligen har fel och gör dumheter... Generellt sett kan fĂ„ mĂ€nniskor tolerera denna form av kommunikation. Även om vissa kunde, som jag. Jag har byggt hela mitt liv pĂ„ meritokratiska principer. Om du visar mig nĂ„got fel sĂ„ vĂ€nder jag mig omedelbart om och sĂ€ger: du sa strunt. Samtidigt ber jag förstĂ„s om ursĂ€kt och allt det dĂ€r, jag kommer att notera fördelarna, om nĂ„gra, och vidta andra korrekta Ă„tgĂ€rder. Å andra sidan har jag chockerande rĂ€tt om en chockerande stor andel av den totala tiden. Och det fungerar inte sĂ€rskilt bra i relationer med mĂ€nniskor. Jag försöker inte vara trevlig, men jag stĂ€ller frĂ„gan rakt ut. "Det hĂ€r kommer aldrig att fungera, för ett, tvĂ„ och tre." Och de var som, "Åh!" Det fanns andra konsekvenser som förmodligen var bĂ€ttre att ignorera: till exempel de som ledde till en skilsmĂ€ssa frĂ„n min fru och tio Ă„r av depression efter det.

Utmaning Ă€r en kamp med mĂ€nniskor, med deras uppfattning om vad du kan eller inte kan göra, vad som Ă€r viktigt och vad som inte Ă€r det. Det fanns mĂ„nga utmaningar kring kodningsstil. Jag skriver fortfarande mycket kod, och pĂ„ den tiden var jag till och med tvungen att sakta ner eftersom jag gjorde för mĂ„nga parallella uppgifter och gjorde dem dĂ„ligt, istĂ€llet för att fokusera pĂ„ en. NĂ€r jag ser tillbaka skrev jag halva koden för Java JIT-kommandot, C2-kommandot. Den nĂ€st snabbaste kodaren skrev hĂ€lften sĂ„ lĂ„ngsamt, nĂ€sta hĂ€lften sĂ„ lĂ„ngsamt, och det var en exponentiell nedgĂ„ng. Den sjunde personen i den hĂ€r raden var vĂ€ldigt, vĂ€ldigt lĂ„ngsam - det hĂ€nder alltid! Jag rörde mycket kod. Jag tittade pĂ„ vem som skrev vad, utan undantag stirrade jag pĂ„ deras kod, granskade var och en av dem och fortsatte Ă€ndĂ„ att skriva mer sjĂ€lv Ă€n nĂ„gon av dem. Detta tillvĂ€gagĂ„ngssĂ€tt fungerar inte sĂ€rskilt bra med mĂ€nniskor. Vissa mĂ€nniskor gillar inte detta. Och nĂ€r de inte kan hantera det börjar alla möjliga klagomĂ„l. Till exempel blev jag en gĂ„ng tillsagd att sluta koda för att jag skrev för mycket kod och det Ă€ventyrade laget, och allt lĂ€t som ett skĂ€mt för mig: grabb, om resten av teamet försvinner och jag fortsĂ€tter skriva kod, kommer bara att förlora halva lag. Å andra sidan, om jag fortsĂ€tter skriva kod och du tappar halva laget, lĂ„ter det som vĂ€ldigt dĂ„lig ledning. Jag tĂ€nkte aldrig riktigt pĂ„ det, pratade aldrig om det, men det fanns Ă€ndĂ„ nĂ„gonstans i mitt huvud. Tanken snurrade i bakhuvudet: "SkĂ€mtar ni med mig?" SĂ„ det största problemet var mig och mina relationer med mĂ€nniskor. Nu förstĂ„r jag mig sjĂ€lv mycket bĂ€ttre, jag var teamledare för programmerare under lĂ„ng tid, och nu sĂ€ger jag direkt till folk: du vet, jag Ă€r den jag Ă€r, och du kommer att behöva ta itu med mig - Ă€r det okej om jag stĂ„r hĂ€r? Och nĂ€r de började ta itu med det fungerade allt. Faktum Ă€r att jag varken Ă€r dĂ„lig eller bra, jag har inga dĂ„liga avsikter eller sjĂ€lviska ambitioner, det Ă€r bara mitt vĂ€sen, och jag mĂ„ste leva med det pĂ„ nĂ„got sĂ€tt.

Andrew: Nyligen började alla prata om sjÀlvkÀnnedom för introverta och mjuka fÀrdigheter i allmÀnhet. Vad kan du sÀga om detta?

Klippa: Ja, det var insikten och lĂ€rdomen jag fick av min skilsmĂ€ssa frĂ„n min fru. Det jag lĂ€rde mig av skilsmĂ€ssan var att förstĂ„ mig sjĂ€lv. Det var sĂ„ jag började förstĂ„ andra mĂ€nniskor. FörstĂ„ hur denna interaktion fungerar. Detta ledde till upptĂ€ckter en efter en. Det fanns en medvetenhet om vem jag Ă€r och vad jag representerar. Vad gör jag: antingen Ă€r jag upptagen av uppgiften, eller sĂ„ undviker jag konflikt, eller nĂ„got annat - och denna nivĂ„ av sjĂ€lvmedvetenhet hjĂ€lper verkligen till att hĂ„lla mig sjĂ€lv i kontroll. Efter detta gĂ„r allt mycket lĂ€ttare. En sak som jag upptĂ€ckte inte bara hos mig sjĂ€lv utan Ă€ven hos andra programmerare Ă€r oförmĂ„gan att verbalisera tankar nĂ€r du Ă€r i ett tillstĂ„nd av kĂ€nslomĂ€ssig stress. Till exempel, du sitter dĂ€r och kodar, i ett tillstĂ„nd av flöde, och sedan kommer de springande till dig och börjar skrika i hysteri att nĂ„got Ă€r trasigt och nu kommer extrema Ă„tgĂ€rder att vidtas mot dig. Och du kan inte sĂ€ga ett ord eftersom du Ă€r i ett tillstĂ„nd av kĂ€nslomĂ€ssig stress. Den förvĂ€rvade kunskapen gör att du kan förbereda dig för detta ögonblick, överleva det och gĂ„ vidare till en retrĂ€ttplan, varefter du kan göra nĂ„got. SĂ„ ja, nĂ€r du börjar inse hur det hela fungerar Ă€r det en enorm livsförĂ€ndrande hĂ€ndelse. 
Jag sjÀlv kunde inte hitta de rÀtta orden, men jag kom ihÄg sekvensen av handlingar. PoÀngen Àr att denna reaktion Àr lika mycket fysisk som den Àr verbal, och du behöver utrymme. SÄdant utrymme, i zen-bemÀrkelse. Det Àr precis detta som behöver förklaras, och sedan omedelbart kliva Ät sidan - rent fysiskt kliva undan. NÀr jag Àr tyst verbalt kan jag bearbeta situationen kÀnslomÀssigt. NÀr adrenalinet nÄr din hjÀrna, förvandlar dig till fight or flight-lÀge kan du inte lÀngre sÀga nÄgonting, nej - nu Àr du en idiot, en piskande ingenjör, oförmögen till ett anstÀndigt svar eller ens stoppa attacken, och angriparen Àr fri att attackera om och om igen. Du mÄste först bli dig sjÀlv igen, Äterta kontrollen, ta dig ur "fight or flight"-lÀget.

Och för detta behöver vi verbalt utrymme. Bara ledigt utrymme. Om du sĂ€ger nĂ„got alls, dĂ„ kan du sĂ€ga exakt det och sedan gĂ„ och verkligen hitta "utrymme" för dig sjĂ€lv: gĂ„ en promenad i parken, lĂ„s in dig i duschen - det spelar ingen roll. Det viktigaste Ă€r att tillfĂ€lligt koppla bort frĂ„n den situationen. SĂ„ fort du stĂ€nger av i minst nĂ„gra sekunder kommer kontrollen tillbaka, du börjar tĂ€nka nyktert. "Okej, jag Ă€r inte nĂ„gon sorts idiot, jag gör inga dumma saker, jag Ă€r en ganska anvĂ€ndbar person." NĂ€r du har kunnat övertyga dig sjĂ€lv Ă€r det dags att gĂ„ vidare till nĂ€sta steg: att förstĂ„ vad som hĂ€nde. Du blev attackerad, attacken kom frĂ„n dĂ€r du inte förvĂ€ntade dig den, det var ett oĂ€rligt, vidrigt bakhĂ„ll. Det hĂ€r Ă€r dĂ„ligt. NĂ€sta steg Ă€r att förstĂ„ varför angriparen behövde detta. Verkligen varför? Kanske för att han sjĂ€lv Ă€r rasande? Varför Ă€r han arg? Till exempel för att han skruvat pĂ„ sig och inte kan ta ansvar? Detta Ă€r sĂ€ttet att noggrant hantera hela situationen. Men detta krĂ€ver manöverutrymme, verbalt utrymme. Det allra första steget Ă€r att bryta den verbala kontakten. Undvik diskussion med ord. Avbryt det, gĂ„ ivĂ€g sĂ„ fort som möjligt. Om det Ă€r ett telefonsamtal Ă€r det bara att lĂ€gga pĂ„ – det hĂ€r Ă€r en fĂ€rdighet jag lĂ€rde mig genom att kommunicera med min ex-fru. Om samtalet inte gĂ„r bra, sĂ€g bara "hej dĂ„" och lĂ€gg pĂ„. FrĂ„n andra sidan telefonen: "bla bla bla", svarar du: "ja, hejdĂ„!" och lĂ€gg pĂ„. Du avslutar bara konversationen. Fem minuter senare, nĂ€r förmĂ„gan att tĂ€nka förnuftigt Ă„terkommer till dig, har du svalnat lite, det blir möjligt att tĂ€nka pĂ„ allt, vad som hĂ€nde och vad som kommer att hĂ€nda hĂ€rnĂ€st. Och börja formulera ett eftertĂ€nksamt svar, snarare Ă€n att bara reagera av kĂ€nslor. För mig var genombrottet i sjĂ€lvkĂ€nnedom just det faktum att jag vid kĂ€nslomĂ€ssig stress inte kan prata. Att ta sig ur det hĂ€r tillstĂ„ndet, tĂ€nka och planera hur man ska svara och kompensera för problem - det hĂ€r Ă€r de rĂ€tta stegen i fallet nĂ€r man inte kan prata. Det enklaste sĂ€ttet Ă€r att fly frĂ„n situationen dĂ€r kĂ€nslomĂ€ssig stress visar sig och helt enkelt sluta delta i denna stress. Efter det blir man i stĂ„nd att tĂ€nka, nĂ€r man kan tĂ€nka blir man i stĂ„nd att prata osv.

Förresten, i rĂ€tten försöker den motsatta advokaten göra detta mot dig - nu Ă€r det klart varför. För han har förmĂ„gan att förtrycka dig till ett sĂ„dant tillstĂ„nd att du inte ens kan uttala ditt namn, till exempel. I en mycket verklig mening kommer du inte att kunna prata. Om detta hĂ€nder dig, och om du vet att du kommer att hamna pĂ„ en plats dĂ€r verbala strider rasar, pĂ„ en plats som domstol, dĂ„ kan du komma med din advokat. Advokaten kommer att stĂ„ upp för dig och stoppa den verbala attacken, och kommer att göra det pĂ„ ett helt lagligt sĂ€tt, och det förlorade Zen-utrymmet kommer tillbaka till dig. Till exempel var jag tvungen att ringa min familj ett par gĂ„nger, domaren var ganska vĂ€nlig om detta, men den motsatta advokaten skrek och skrek Ă„t mig, jag kunde inte ens fĂ„ in ett ord pĂ„ kanten. I dessa fall fungerar det bĂ€st för mig att anvĂ€nda en medlare. Medlaren stoppar allt detta tryck som vĂ€ller pĂ„ dig i en kontinuerlig ström, du hittar det nödvĂ€ndiga Zen-utrymmet, och med det Ă„terkommer förmĂ„gan att tala. Det hĂ€r Ă€r ett helt kunskapsomrĂ„de dĂ€r det finns mycket att studera, mycket att upptĂ€cka inom dig sjĂ€lv, och allt detta förvandlas till strategiska beslut pĂ„ hög nivĂ„ som Ă€r olika för olika mĂ€nniskor. Vissa mĂ€nniskor har inte de problem som beskrivs ovan, vanligtvis har mĂ€nniskor som Ă€r professionella sĂ€ljare inte det. Alla dessa mĂ€nniskor som försörjer sig med ord – kĂ€nda sĂ„ngare, poeter, religiösa ledare och politiker, de har alltid nĂ„got att sĂ€ga. De har inte sĂ„dana problem, men jag har det.

Andrew: Det var... ovÀntat. Bra, vi har redan pratat mycket och det Àr dags att avsluta den hÀr intervjun. Vi kommer definitivt att trÀffas pÄ konferensen och kommer att kunna fortsÀtta denna dialog. Vi ses pÄ Hydra!

Du kan fortsÀtta ditt samtal med Cliff pÄ konferensen Hydra 2019, som kommer att hÄllas den 11-12 juli 2019 i St. Petersburg. Han kommer med en rapport "The Azul Hardware Transactional Memory-upplevelse". Biljetter kan köpas pÄ den officiella webbplatsen.

KĂ€lla: will.com

LĂ€gg en kommentar