Stort intervju med Cliff Click – fadern till JIT-kompilering i Java

Stort intervju med Cliff Click – fadern till JIT-kompilering i JavaCliff Click – CTO för Cratus (IoT-sensorer för att förbĂ€ttra processer), grundare och medgrundare av flera startups (inklusive Rocket Realtime School, Neurensic och H2O.ai) med flera framgĂ„ngsrika exits. Cliff skrev sin första kompilator vid 15 Ă„rs Ă„lder (Pascal för TRS Z-80)! Mest kĂ€nd för sitt arbete med C2 i Java (the Sea of Nodes IR). Denna kompilator visade vĂ€rlden att JIT kan producera kvalitetskoden, vilket var en av faktorerna bakom Java som en av de frĂ€msta moderna programplattformarna. DĂ€refter hjĂ€lpte Cliff Azul Systems att bygga en 864-kĂ€rnig mainframe med programvara i ren Java, som hanterade GC-pausar pĂ„ 500 gigabyte heap inom 10 millisekunder. Generellt har Cliff arbetat med alla aspekter av JVM.

 
Detta huxleypost Àr ett stort intervju med Cliff. Vi kommer att diskutera följande Àmnen:

  • ÖvergĂ„ng till lĂ„gnivĂ„optimeringar
  • Hur man gör en omfattande omstrukturering
  • Kostnadsmodell
  • Utbildning i lĂ„gnivĂ„optimeringar
  • Praktiska exempel pĂ„ prestandaförbĂ€ttringar
  • Varför skapa ditt eget programmeringssprĂ„k
  • KarriĂ€r som prestandingenjör
  • Tekniska utmaningar
  • Lite om registerallokering och flerkĂ€rnighet
  • Den största utmaningen i livet

Intervjuas av:

  • Andrey Sataryan frĂ„n Amazon Web Services. Under sin karriĂ€r har han arbetat med helt olika projekt: testat distribuerad NewSQL-databas pĂ„ Yandex, ett molndetekteringssystem pĂ„ Kaspersky Lab, ett flerspelarspel pĂ„ Mail.ru och en valutakalkylatorstjĂ€nst pĂ„ Deutsche Bank. Han Ă€r intresserad av testning av storskaliga backend- och distribuerade system.
  • Vladimir Sitnikov frĂ„n Netcracker. Han har arbetat i tio Ă„r med prestanda och skalbarhet i NetCracker OS – mjukvara som anvĂ€nds av telekomoperatörer för att automatisera nĂ€tverks- och nĂ€tverksutrustningshantering. Han Ă€r intresserad av prestandafrĂ„gor relaterade till Java och Oracle Database. Författare av mer Ă€n ett dussin prestandaförbĂ€ttringar i den officiella PostgreSQL JDBC-drivern.

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

Andrey: Du Àr en vÀlkÀnd person inom JIT-kompilering, Java och arbete med prestanda i allmÀnhet, eller hur? 

Cliff: Det stÀmmer!

Andrey: LÄt oss börja med allmÀnna frÄgor om prestandaarbete. Vad tycker du om valet mellan hög- och lÄg-nivÄoptimeringar som arbete pÄ CPU-nivÄ?

Cliff: Det Ă€r enkelt. Den snabbaste koden Ă€r den som aldrig körs. SĂ„ det Ă€r viktigt att alltid börja pĂ„ en hög nivĂ„ och arbeta med algoritmer. En bĂ€ttre O-notation slĂ„r en sĂ€mre O-notation, sĂ„ lĂ€nge ingen stor konstant blandar sig i. LĂ„g-nivĂ„arbete kommer sist. Vanligtvis, om du har optimerat hela den andra stapeln tillrĂ€ckligt vĂ€l, och det fortfarande finns nĂ„got intressant kvar – dĂ„ Ă€r det lĂ„g nivĂ„. Men hur börjar man pĂ„ hög nivĂ„? Hur vet man att man har gjort tillrĂ€ckligt arbete pĂ„ hög nivĂ„? Tja
 ingen aning. Det finns inga fĂ€rdiga recept. Du mĂ„ste förstĂ„ problemet, bestĂ€mma vad du tĂ€nker göra (för att inte göra onödiga steg senare) och först dĂ„ kan du plocka fram en profilerare som kan sĂ€ga nĂ„got anvĂ€ndbart. Vid nĂ„got tillfĂ€lle förstĂ„r du sjĂ€lv att du har blivit av med onödiga saker och det Ă€r dags att börja finjustera pĂ„ lĂ„g nivĂ„. Det Ă€r absolut en speciell typ av konst. MĂ„nga gör onödiga saker, men rusar sĂ„ snabbt att de inte har tid att tĂ€nka pĂ„ prestanda. Men det Ă€r sĂ„ lĂ€nge frĂ„gan inte blir akut. Oftast Ă€r 99% av tiden ingen intresserad av vad jag gör, förrĂ€n nĂ„got viktigt blockerar den kritiska vĂ€gen och nĂ„gon bryr sig. Och dĂ„ börjar alla ifrĂ„gasĂ€tta varför det frĂ„n början inte fungerade perfekt. Det finns alltid nĂ„got att förbĂ€ttra i prestandan. Men 99% av tiden har du inga ledtrĂ„dar! Du försöker bara fĂ„ nĂ„got att fungera och under vĂ€gen inser du vad som Ă€r viktigt. Du kan aldrig veta i förvĂ€g att just den hĂ€r biten mĂ„ste vara perfekt, sĂ„ i grunden mĂ„ste du vara perfekt i allt. Och det Ă€r omöjligt och sĂ„ gör du inte. Det finns alltid massor av saker att fixa – och det Ă€r helt okej.

Hur man gör en omfattande omstrukturering

Andrey: Hur arbetar du med prestanda? Det Àr ju ett genomgÄende problem. Har du till exempel varit tvungen att hantera problem som uppstÄr till följd av att mÄnga redan existerande funktioner korsar varandra?

Cliff: Jag försöker undvika det. Om jag vet att prestanda kommer att bli ett problem tĂ€nker jag pĂ„ det innan jag börjar koda, sĂ€rskilt angĂ„ende datastrukturer. Men ofta upptĂ€cker du allt detta mycket senare. Och dĂ„ mĂ„ste du ta till extrema Ă„tgĂ€rder och göra det jag kallar "skriv om och hĂ€rska": du mĂ„ste greppa ett tillrĂ€ckligt stort stycke. En del av koden mĂ„ste Ă€ndĂ„ skrivas om pĂ„ grund av prestandaproblem eller nĂ„got annat. Oavsett orsak till att skriva om koden, Ă€r det nĂ€stan alltid bĂ€ttre att skriva om ett större segment Ă€n ett mindre. I det ögonblicket börjar alla darra av rĂ€dsla: "Åh nej, vi fĂ„r inte röra sĂ„ mycket kod!" Men i sjĂ€lva verket fungerar det nĂ€stan alltid mycket bĂ€ttre. Du mĂ„ste ta itu med det stora problemet, rita en stor cirkel runt det och sĂ€ga: allt inom cirkeln kommer jag att skriva om. GrĂ€nsen Ă€r mycket mindre Ă€n det innehĂ„ll som ska ersĂ€ttas. Och om sĂ„dan grĂ€nssĂ€ttning gör att arbetet inuti blir perfekt – har du fria hĂ€nder, gör vad du vill. SĂ„ snart du förstĂ„r problemet gĂ„r omprosesseringen mycket smidigare, sĂ„ ta en stor bit!
Samtidigt, nÀr du gör om ett stort segment och inser att prestanda kommer att bli ett problem, kan du börja oroa dig för det direkt. Vanligtvis blir det till enkla saker som "kopiera inte data, hantera data sÄ enkelt som möjligt, gör dem mindre". Vid stora omskrivningar finns det standardmetoder för att förbÀttra prestanda. Och de kretsar nÀstan alltid kring data.

Kostnadsmodell

Andrey: I ett av podcastavsnitten pratade ni om kostnadsmodeller i samband med prestanda. Kan ni förklara vad ni menade med det?

Cliff: SjĂ€lvklart. Jag föddes i en tid nĂ€r processorprestanda var oerhört viktigt. Och den eran Ă„tervĂ€nder – ödet Ă€r inte utan ironi. Jag började leva under tiden för Ă„ttabitarsmaskiner, min första dator fungerade med 256 byte. Just byte. Allt var vĂ€ldigt litet. Man var tvungen att rĂ€kna instruktioner och sĂ„ snart vi började avancera uppĂ„t i programmeringssprĂ„kens stack, tog sprĂ„ken pĂ„ sig mer och mer. Det fanns assembler, sedan Basic, sedan C, och C tog hand om mĂ„nga detaljer, som registreringsfördelning och instruktionstypning. Men dĂ€r var allt ganska tydligt och om jag gjorde en pekare till en instans av en variabel, dĂ„ skulle jag fĂ„ load, och kostnaden för den instruktionen Ă€r kĂ€nd. HĂ„rdvaran ger ett kĂ€nt antal maskincykler, sĂ„ hastigheten för att köra olika saker kan berĂ€knas genom att helt enkelt summera alla instruktioner som du tĂ€nker köra. Varje compare/test/branch/call/load/store kunde summeras och sĂ€gas: hĂ€r har du körningstiden. NĂ€r du arbetar med att förbĂ€ttra prestanda kommer du definitivt att notera vilka siffror som motsvarar de smĂ„ heta cyklerna. 
Men sĂ„ snart du vĂ€xlar till Java, Python och liknande, avlĂ€gsnar du dig vĂ€ldigt snabbt frĂ„n lĂ„gnivĂ„hĂ„rdvara. Vad kostar det att anropa en getter i Java? Om JIT i HotSpot gjorde allt rĂ€tt inlinat, kommer det att bli load, men om det inte gjorde det – kommer det att bli ett funktionsanrop. Eftersom anropet ligger pĂ„ den heta cykeln, kommer det att avbryta alla andra optimeringar i denna cykel. SĂ„ den verkliga kostnaden kommer att vara mycket högre. Och du förlorar omedelbart förmĂ„gan att titta pĂ„ en kodsnutt och förstĂ„ vad det kostar att köra den i termer av processorhastighet, anvĂ€nd minne och cache. Allt detta blir intressant först nĂ€r du verkligen grĂ€ver ner dig i prestanda.
Just nu befinner vi oss i en situation dĂ€r processorernas hastighet knappt har ökat pĂ„ ett decennium. Gamla tider Ă„tervĂ€nder! Du kan inte lĂ€ngre rĂ€kna med bra prestanda i enkeltrĂ„dar. Men om du plötsligt ger dig in pĂ„ parallella berĂ€kningar – det Ă€r galet komplicerat, alla ser pĂ„ dig som om du vore James Bond. Tio gĂ„nger snabbare prestanda uppstĂ„r vanligtvis dĂ€r nĂ„gon har missat nĂ„got. Parallellitet krĂ€ver mycket jobb. För att uppnĂ„ den dĂ€r tio gĂ„nger snabbare prestandan behöver man förstĂ„ kostnadsmodellen. Vad kostar vad? Och för att göra det mĂ„ste man förstĂ„ hur sprĂ„ket ligger över den underliggande hĂ„rdvaran.
Martin Thompson har valt ett utmÀrkt ord för sin blogg Mechanical Sympathy! Man mÄste förstÄ vad hÄrdvaran kommer att göra, hur den kommer att göra det, och varför den överhuvudtaget gör det den gör. Genom att anvÀnda detta Àr det ganska enkelt att börja rÀkna instruktioner och ta reda pÄ vart exekveringstiden tar vÀgen. Om du inte har rÀtt förberedelse, letar du bara efter en svart katt i ett mörkt rum. Jag ser hela tiden folk som optimerar prestanda utan att ha minsta aning om vad de egentligen gör. De kÀmpar hÄrt och kommer inte sÀrskilt lÄngt. Och nÀr jag tar samma kodbit, lÀgger till ett par smÄ hacks och fÄr fem gÄnger eller tio gÄnger snabbare prestanda, sÀger de: tja, det Àr inte rÀttvist, vi visste ju att du var bÀttre. Otroligt. Vad var jag pÄ vÀg att sÀga... kostnadsmodellen handlar om den kod du skriver och hur snabbt den i genomsnitt fungerar i det stora hela.

Andrey: Och hur innehÄller man en sÄ stor mÀngd information i huvudet? Det uppnÄs genom mycket erfarenhet, eller? Var hittar man sÄdan erfarenhet?

Cliff: Tja, min erfarenhet fick jag inte pÄ ett enkelt sÀtt. Jag programmerade i assembler pÄ den tiden nÀr man kunde förstÄ varje enskild instruktion. Det lÄter kanske dumt, men sedan dess har jag i mitt minne ett för alltid bestÄende set av Z80-instruktioner. Jag kommer inte ihÄg namnen pÄ mÀnniskor en minut efter samtalet, men jag kommer ihÄg kod som skrevs för 40 Är sedan. Roligt, det ser ut som ett "idiotens vetenskapsman».

Utbildning i lÄgnivÄoptimeringar

Andrey": Finns det nÄgot enklare sÀtt att komma in pÄ det?

Cliff: Ja och nej. HÄrdvaran som vi alla anvÀnder har inte förÀndrats sÄ mycket under denna tid. Alla anvÀnder x86, förutom smartphones som kör pÄ Arm. Om du inte sysslar med nÄgot hardcore embedded sÄ har du helt enkelt samma sak. Okej, vidare. Instruktionerna har inte heller förÀndrats pÄ Ärhundraden. Man mÄste gÄ och skriva nÄgot i assembler. Lite grann, men tillrÀckligt för att börja förstÄ. Ni ler, men jag sÀger det helt seriöst. Man behöver förstÄ kopplingen mellan sprÄk och hÄrdvara. Efter det mÄste man gÄ och skriva lite kod och skapa en liten leksakskompilator för ett litet leksakssprÄk.
Var kan man fĂ„ en kostnadsmodell? Jo, man kan lĂ€sa nĂ„got, sjĂ€lvklart... Men jag tror att det bĂ€sta sĂ€ttet Ă€r att agera. Göra en liten kompilator och det skulle vara det bĂ€sta sĂ€ttet att förstĂ„ kostnadsmodellen och passa in den i sitt eget sinne. En liten kompilator som skulle fungera för att programmera en mikrovĂ„gsugn – det Ă€r en uppgift för nybörjare. Jag menar, om du redan har programmeringskunskaper borde det rĂ€cka. Allt detta med att parsa en strĂ€ng som kommer att vara nĂ„got slags algebraiskt uttryck, hĂ€mta instruktionerna för matematiska operationer i rĂ€tt ordning, ta rĂ€tt vĂ€rden frĂ„n registren – allt detta gĂ„r att göra ganska lĂ€tt. Och medan du gör det kommer det att etsa sig in i hjĂ€rnan. Jag tror alla vet vad en kompilator gör. Och detta ger förstĂ„else för kostnadsmodellen.

Praktiska exempel pÄ prestandaförbÀttringar

Andrey: Vad mer bör man tÀnka pÄ nÀr man arbetar med prestanda?

Cliff: Datatyper. Förresten, jag har inte hĂ„llit de hĂ€r lektionerna pĂ„ lĂ€nge... Rocket School. Det var roligt, men det krĂ€vde sĂ„ mycket energi, och jag har ju ett liv ocksĂ„! NĂ„vĂ€l. SĂ„, pĂ„ en av de stora och intressanta lektionerna, "Vart tar din prestanda vĂ€gen", gav jag studenterna ett exempel: tvĂ„ och ett halvt gigabyte fintech-data lĂ€stes frĂ„n en CSV-fil och nĂ€sta steg var att rĂ€kna antalet sĂ„lda produkter. Vanliga tickande marknadsdata. UDP-paket, omvandlade till textformat, som började pĂ„ 70-talet. Chicago Mercantile Exchange – olika saker som olja, majs, sojabönor, och liknande. Det var nödvĂ€ndigt att rĂ€kna dessa produkter, antalet affĂ€rer, det genomsnittliga beloppet av rörelse av medel och varor, etc. Det Ă€r ganska enkel handelsmatematik: hitta produktkoden (detta Ă€r 1-2 tecken i en hash-tabell), fĂ„ summan, lĂ€gga till den i en av affĂ€rerna, lĂ€gga till volym, lĂ€gga till kostnad, och nĂ„gra andra saker. Mycket enkel matematik. Den leksaksmĂ€ssiga implementationen var mycket rak pĂ„ sak: allt ligger i filen, jag lĂ€ser filen och gĂ„r igenom den, delar upp individuella poster i Java-strĂ€ngar, letar efter de nödvĂ€ndiga sakerna och lĂ€gger ihop enligt ovanstĂ„ende matematik. Och detta fungerar med en ganska liten hastighet.

Med en sĂ„dan metod Ă€r det uppenbart vad som hĂ€nder, och parallella berĂ€kningar hjĂ€lper inte hĂ€r, eller hur? Det visar sig att fem gĂ„nger ökad prestanda kan uppnĂ„s enbart genom att vĂ€lja rĂ€tt datastrukturer. Detta överraskar till och med erfarna programmerare! I mitt specifika fall var fokus att man inte bör allokera minne i den heta loopen. NĂ„vĂ€l, det Ă€r inte hela sanningen, men i stort sett – man bör inte allokera "varje X", nĂ€r X Ă€r tillrĂ€ckligt stort. NĂ€r X Ă€r tvĂ„ och en halv gigabyte, bör man inte allokera nĂ„got "per bokstav", eller "per rad", eller "per fĂ€lt"; inget sĂ„dant. Det Ă€r just detta som tar tid. Hur fungerar det egentligen? FörestĂ€ll dig att jag gör anropet String.split() eller BufferedReader.readLine(). Readline gör en strĂ€ng av en uppsĂ€ttning bytes som kom över nĂ€tverket, en gĂ„ng för varje rad, för var och en av hundratals miljoner rader. Jag tar den hĂ€r strĂ€ngen, analyserar den och kastar bort den. Varför kastar jag bort den – ja, jag har redan bearbetat den, det rĂ€cker. SĂ„, för varje byte som lĂ€ses frĂ„n dessa 2.7G kommer tvĂ„ tecken att skrivas till strĂ€ngen, vilket ger 5.4G, och de behövs inte lĂ€ngre, sĂ„ de kastas bort. Om man ser pĂ„ minnesbandbredden, sĂ„ laddar vi 2.7G som gĂ„r genom minnet och minnesbussen i processorn, och sen skickas dubbelt sĂ„ mycket in i strĂ€ngen som ligger i minnet, och allt detta bearbetas vid skapandet av varje ny strĂ€ng. Men jag mĂ„ste lĂ€sa den, hĂ„rdvaran lĂ€ser den, Ă€ven om allt senare kommer att bearbetas. Och jag mĂ„ste skriva den, för jag har skapat en strĂ€ng och cacheminnena Ă€r överfulla – cachen kan inte rymma 2.7G. SĂ„, för varje lĂ€st byte lĂ€ser jag tvĂ„ ytterligare bytes och skriver tvĂ„ ytterligare bytes, vilket ger ett förhĂ„llande pĂ„ 4:1 – pĂ„ detta sĂ€tt slösar vi bort minnesbandbredden. Och det visar sig att om jag gör String.split() – sĂ„ gör jag det lĂ„ngt ifrĂ„n för sista gĂ„ngen, det kan finnas ytterligare 6-7 fĂ€lt inuti. DĂ€rför leder klassisk CSV-lĂ€sning med efterföljande radparsing till förluster i minnesbandbredd pĂ„ omkring 14:1 jĂ€mfört med vad man egentligen skulle vilja ha. Om man slĂ€nger bort dessa allokeringar kan man uppnĂ„ fem gĂ„nger hastighetsökning.

Och det Àr inte sÀrskilt svÄrt. Om du ser pÄ koden frÄn rÀtt vinkel blir allt ganska enkelt, sÄ snart du inser essensen av problemet. Du bör inte sluta allokera minne överhuvudtaget: problemet Àr bara att du allokerar nÄgot och det dör omedelbart, vilket brÀnner en viktig resurs, som i det hÀr fallet Àr minnesbandbredden. Och allt detta leder till prestandafall. PÄ x86 behöver du vanligtvis aktivt brÀnna processortakter, men hÀr har du brÀnt all minne mycket tidigare. Lösningen Àr att minska antalet allokeringar. 
En annan del av problemet Ă€r att om du kör en profilerare nĂ€r minnesbandbredden Ă€r slut, precis i det ögonblick det hĂ€nder, förvĂ€ntar du dig vanligtvis att cache-minnet ska Ă„tervĂ€nda, eftersom det Ă€r fullt av skrĂ€p som du just har skapat, alla dessa strĂ€ngar. DĂ€rför blir varje load eller store-operation lĂ„ngsam, eftersom de leder till cache-missar – hela cachen blev lĂ„ngsam, medan den vĂ€ntade pĂ„ att skrĂ€pet skulle lĂ€mna. SĂ„ profileraren kommer bara att visa ett varmt, slumpmĂ€ssigt brus som Ă€r utspritt över hela cykeln – det kommer inte att finnas nĂ„gon specifik het instruktion eller plats i koden. Bara brus. Och om du tittar pĂ„ GC-cykler, kommer de alla att vara i Young Generation och super snabba – mikrosekunder eller millisekunder högst. För all denna minne dör omedelbart. Du allokerar miljarder gigabyte, och den skĂ€r, och skĂ€r, och skĂ€r igen. Allt detta sker mycket snabbt. SĂ„ det finns billiga GC-cykler, varmt brus över hela cykeln, men vi vill ha en 5-faldig acceleration. Vid det ögonblicket borde nĂ„got klicka i huvudet och lĂ„ta som: 'Varför Ă€r det sĂ„?!'. Överskridande av minnesbandbredden visas inte i en klassisk debugger, du mĂ„ste köra en debugger för hĂ„rdvaruövervakning av prestanda och se det direkt och personligen. Inte direkt kan det misstĂ€nkas frĂ„n dessa tre symptom. Det tredje symptomet Ă€r nĂ€r du ser vad du allokerar, frĂ„gar profileraren, och den svarar: 'Du har gjort en miljard strĂ€ngar, men GC har arbetat gratis'. SĂ„ fort detta intrĂ€ffar, förstĂ„r du att du har skapat för mĂ„nga objekt och brĂ€nt hela minnesbandbredden. Det finns ett sĂ€tt att förstĂ„ detta, men det Ă€r inte uppenbart. 

Problemet ligger i datakonstruktionen: den rĂ„a strukturen bakom allt som pĂ„gĂ„r Ă€r för stor, det Ă€r 2,7G pĂ„ disken, sĂ„ att göra en kopia av den hĂ€r saken Ă€r mycket oönskat – det Ă€r bĂ€ttre att ladda den direkt frĂ„n det nĂ€tverksbytebuffret till registren, sĂ„ att man inte behöver lĂ€sa och skriva fram och tillbaka fem gĂ„nger. TyvĂ€rr ger Java dig som standard inte ett sĂ„dant bibliotek i JDK. Men det Ă€r ju trivialt, eller hur? I praktiken handlar det om 5-10 rader kod som gĂ„r till att implementera en egen buffrad radladdare, som upprepar beteendet hos klassstrĂ€ngar, men som Ă€ndĂ„ Ă€r en omslutning kring den underliggande bytebuffern. Resultatet blir att du arbetar nĂ€stan som om du hade att göra med strĂ€ngar, men i sjĂ€lva verket flyttas pekarna pĂ„ bufferten, och rĂ„a byte kopieras ingenstans, och pĂ„ sĂ„ sĂ€tt Ă„teranvĂ€nds samma buffrar gĂ„ng pĂ„ gĂ„ng, och operativsystemet Ă€r lyckligt över att hantera de saker som det Ă€r avsett för, som dold dubbel buffring av dessa bytebuffrar, och du sjĂ€lv maler inte lĂ€ngre en oĂ€ndlig ström av onödiga data. Förresten, förstĂ„r ni, nĂ€r man arbetar med GC garanteras det att varje minnesallokering inte kommer att synas av processorn efter den senaste GC-cykeln? SĂ„, allt detta kan inte vara i cachen, och sen intrĂ€ffar en 100%-ig garanti pĂ„ en miss. NĂ€r man arbetar med en pekare tar det 1-2 cykler att lĂ€sa en register frĂ„n minnet i x86, och sĂ„ snart detta sker, betalar du, betalar du, betalar du, för allt minne Ă€r pĂ„ NIO-cache – och detta Ă€r kostnaden för minnesallokering. Den verkliga kostnaden.

Med andra ord Ă€r datastrukturer det som Ă€r svĂ„rast att Ă€ndra. Och sĂ„ snart du inser att du har valt fel datastruktur, som senare kommer att pĂ„verka prestandan negativt, krĂ€vs det vanligtvis en betydande arbetsinsats för att korrigera detta. Om du inte gör det, kommer det bara att bli vĂ€rre. FrĂ€mst mĂ„ste man tĂ€nka pĂ„ datastrukturer, det Ă€r viktigt. Den största kostnaden hĂ€r infaller med tunga datastrukturer, som man börjar anvĂ€nda med tanken "jag kopierade datastrukturen X till datastrukturen Y, för Y tilltalar mig mer estetiskt". Men kopieringsoperationen (som verkar billig) tar faktiskt upp minnesbandbredd, och hĂ€r ligger allt förlorad exekveringstid. Om jag har en enorm strĂ€ng med JSON och jag vill omvandla den till ett strukturerat DOM-trĂ€d av POJO eller nĂ„got liknande, sĂ„ blir parsningen av den strĂ€ngen och konstruktionen av POJO, och sedan nya referenser till POJO kostsamma – det Ă€r ingen billig affĂ€r. Utom nĂ€r du kommer att arbeta med POJO mycket oftare Ă€n med strĂ€ngen. I stĂ€llet kan man övervĂ€ga att avkoda strĂ€ngen och dra ut det som behövs, utan att förvandla det till nĂ„gra POJO. Om allt detta sker i en situation dĂ€r maximal prestanda krĂ€vs, Ă€r det bĂ€st att undvika POJO – man behöver pĂ„ nĂ„got sĂ€tt grĂ€va direkt i strĂ€ngen.

Varför skapa ditt eget programmeringssprÄk

Andrey: Du sa att för att förstĂ„ kostnadsmodellen behöver man skriva ett litet eget sprĂ„k


Cliff: Inte ett sprÄk, utan en kompilator. Ett sprÄk och en kompilator Àr olika saker. Den största skillnaden ligger i ditt eget sinne. 

Andrey: För övrigt, sÄ vitt jag vet, experimenterar du med att skapa egna sprÄk. Varför?

Cliff: För jag kan! Jag Àr delvis pensionerad, sÄ det hÀr Àr min hobby. Jag har hela mitt liv realiserat andras sprÄk. Jag har ocksÄ arbetat mycket med kodningsstil. Dessutom ser jag problem i andra sprÄk. Jag ser att det finns bÀttre sÀtt att göra vanliga saker. Och jag skulle anvÀnda dem. Jag Àr bara frustrerad över att se problem hos mig sjÀlv, i Java, i Python, i vilket annat sprÄk som helst. Just nu skriver jag i React Native, JavaScript och Elm som en hobby, som inte handlar om pensionering, utan om aktivt arbete. Och jag skriver ocksÄ i Python och kommer troligtvis fortsÀtta arbeta med maskininlÀrning för Java-backends. Det finns mÄnga populÀra sprÄk och alla har intressanta egenskaper. Varje sprÄk har nÄgot unikt och man kan försöka sammanfoga alla dessa funktioner. SÄ jag studerar intressanta saker, sprÄkets beteende, försöker komma pÄ rimlig semantik. Och hittills lyckas jag! Just nu arbetar jag med minnessemantiken, för jag vill ha den som i C och Java, och fÄ en stark minnesmodell och minnessemantik för laddningar och lagringar. Samtidigt vill jag ha automatisk typutdragning som i Haskell. SÄ jag försöker blanda Haskell-liknande typutdragning med minne som fungerar som i C och Java. Det Àr vad jag har sysslat med de senaste 2-3 mÄnaderna, till exempel.

Andrey: Om du bygger ett sprÄk som tar de bÀsta aspekterna frÄn andra sprÄk, har du tÀnkt pÄ att nÄgon skulle kunna göra det motsatta: ta dina idéer och anvÀnda dem i sitt sinne?

Cliff: Just sĂ„ hĂ€r föds nya sprĂ„k! Varför liknar Java C? För att C hade en bra syntax som alla förstod, och Java inspirerades av den syntaxen, medan den lade till typ-sĂ€kerhet, grĂ€nskontroller för arrayer, GC, och de förbĂ€ttrade Ă€ven vissa saker frĂ„n C. De lade till sina egna. Men de var ganska inspirerade, eller hur? Alla stĂ„r pĂ„ axlarna av jĂ€ttar som kom före dig – det Ă€r sĂ„ framsteg görs.

Andrey: Som jag förstÄr det kommer ditt sprÄk att vara sÀkert nÀr det gÀller anvÀndning av minne. Har du funderat pÄ att implementera nÄgot liknande som borrow checker frÄn Rust? Har du tittat pÄ det, vad tycker du om det?

Cliff: Ja, jag har programmerat i C i en evighet, med alla dessa malloc och free, och jag hanterar minneslivscykeln manuellt. Ni vet, 90-95% av den manuella hanteringen av livscykeln har samma struktur. Och det Ă€r vĂ€ldigt, vĂ€ldigt smĂ€rtsamt att göra detta för hand. Jag önskar att compilern bara kunde berĂ€tta vad som hĂ€nder och vad jag har Ă„stadkommit med mina handlingar. För vissa saker gör borrow checker det hĂ€r direkt. Dessutom borde det automatiskt generera information, förstĂ„ allt och inte ens belasta mig med att behöva formulera den förstĂ„elsen. Det borde Ă„tminstone göra en lokal escape-analys, och först om det misslyckas, dĂ„ mĂ„ste man lĂ€gga till typannoteringar som beskriver livscykeln – och en sĂ„dan schema Ă€r mycket mer komplicerad Ă€n borrow checker, eller överhuvudtaget vilken befintlig minneskontroller som helst. Valet mellan 'allt Ă€r bra' och 'jag förstod ingenting' – nej, det borde finnas nĂ„got bĂ€ttre. 
Som en person som har skrivit mycket kod i C anser jag att ha stöd för automatisk livscykelhantering Ă€r avgörande. Dessutom har jag tröttnat pĂ„ hur mycket Java anvĂ€nder minne, och huvudsakligt klagomĂ„l Ă€r pĂ„ GC. NĂ€r minne allokeras i Java fĂ„r du inte tillbaka det minne som var lokalt under den senaste GC-cykeln. I sprĂ„k med mer exakt minneshantering Ă€r det inte sĂ„. Om du kallar malloc fĂ„r du direkt minne som vanligtvis just har anvĂ€nts. Oftast gör du tillfĂ€lliga saker med minnet och returnerar det omedelbart. Och det returneras direkt till malloc-poolen, och nĂ€sta malloc-cykel drar upp det igen. DĂ€rför minskar det verkliga minnesanvĂ€ndandet till de aktiva objekten vid en viss tidpunkt, plus lĂ€ckor. Om du inte har lĂ€ckor pĂ„ ett helt oacceptabelt sĂ€tt, sĂ„ lagras det mesta av minnet i cachar och processorn, och det fungerar snabbt. Men det krĂ€ver mycket manuell minneshantering med malloc och free, anropade i rĂ€tt ordning och pĂ„ rĂ€tt stĂ€llen. Rust kan hantera detta korrekt sjĂ€lv och i mĂ„nga fall ge till och med bĂ€ttre prestanda, eftersom minnesförbrukningen begrĂ€nsas till aktuella berĂ€kningar – till skillnad frĂ„n att vĂ€nta pĂ„ nĂ€sta GC-cykel som ska frigöra minne. I slutĂ€ndan har vi fĂ„tt ett mycket intressant sĂ€tt att förbĂ€ttra prestanda. Och ganska kraftfullt – jag har arbetat med sĂ„dana saker vid databehandling för fintech, och det gjorde det möjligt att uppnĂ„ ungefĂ€r femfaldig hastighetsökning. Det Ă€r en ganska stor ökning, sĂ€rskilt i en vĂ€rld dĂ€r processorer inte blir snabbare, medan vi fortfarande vĂ€ntar pĂ„ förbĂ€ttringar.

KarriÀr som prestandingenjör

Andrey: Jag skulle ocksĂ„ vilja frĂ„ga lite om karriĂ€ren i stort. Du blev kĂ€nd genom ditt arbete med JIT i HotSpot och flyttade sedan till Azul – och det Ă€r ocksĂ„ ett JVM-företag. Men du har jobbat mer med hĂ„rdvara Ă€n mjukvara. Och sedan plötsligt bytte du till Big Data och MaskininlĂ€rning, och sedan till bedrĂ€geridetektion. Hur kom det sig? Det Ă€r vĂ€ldigt olika utvecklingsomrĂ„den.

Cliff: Jag har sysslat med programmering ganska lĂ€nge och har hunnit med mĂ„nga olika projekt. Och nĂ€r folk sĂ€ger: „Äh, du Ă€r den som gjorde JIT för Java!“, sĂ„ Ă€r det alltid roligt. Men innan dess var jag involverad i en klon av PostScript – det sprĂ„ket som Apple en gĂ„ng anvĂ€nde för sina laserskrivare. Innan dess gjorde jag en implementation av sprĂ„ket Forth. Jag tror att ett gemensamt tema för mig Ă€r utveckling av verktyg. Jag har alltid skapat verktyg som hjĂ€lper andra att skriva sina fantastiska program. Men jag har ocksĂ„ varit involverad i utvecklingen av operativsystem, drivrutiner, kĂ€rndebuggers, sprĂ„k för operativsystem som började enkelt men med tiden blev mer och mer komplexa. Men det huvudsakliga temat Ă€r Ă€ndĂ„ verktygsutveckling. En stor del av mitt liv har Ă€gnats Ă„t Azul och Sun, och det handlade om Java. Men nĂ€r jag började med Big Data och MaskininlĂ€rning tog jag Ă„terigen pĂ„ mig min festhatt och sa: „Åh, nu har vi ett icke-trivialt problem, och hĂ€r hĂ€nder det verkligen massor av intressanta saker och mĂ€nniskor som gör nĂ„got.“ Det Ă€r en fantastisk vĂ€g för utveckling som Ă€r vĂ€rd att gĂ„.

Ja, jag Ă€lskar verkligen distribuerade berĂ€kningar. Mitt första jobb var under studietiden med C, pĂ„ ett reklamprojekt. Det handlade om distribuerade berĂ€kningar pĂ„ Zilog Z80-chip som samlade data för analog optisk teckenigenkĂ€nning, utförd av en riktig analog analysator. Det var ett coolt och helt galet Ă€mne. Men det fanns problem; vissa delar kĂ€nde inte igen rĂ€tt, sĂ„ man var tvungen att ta fram bilden och visa den för en person som redan sett pĂ„ den och rapportera vad den sa. DĂ€rför fanns det jobb med data, och dessa jobb hade sitt eget sprĂ„k. Det fanns en backend som hanterade allt detta – parallella Z80:or som körde vt100-terminaler – en per person, och det fanns en modell för parallell programmering pĂ„ Z80. En gemensam minnesdel som alla Z80 delade inom en stjĂ€rnkonfiguration; backplane delades och hĂ€lften av RAM delades inom nĂ€tverket, medan den andra hĂ€lften var privat eller gick till nĂ„got annat. En meningsfullt komplex parallell distribuerad system med delad... halvdelad minne. NĂ€r var det
 Redan svĂ„rt att minnas, nĂ„gon gĂ„ng i mitten av 80-talet. Ganska lĂ€nge sedan. 
Ja, vi kan anse att 30 Ă„r Ă€r ganska lĂ€nge. Utmaningar relaterade till distribuerade berĂ€kningar har funnits lĂ€nge; mĂ€nniskor har kĂ€mpat mot dem i Ă„ratal. Beowulf-kluster. Dessa kluster ser ut som... Till exempel: det finns Ethernet och din snabba x86 Ă€r ansluten till detta Ethernet, och nu vill du fĂ„ fake shared memory, eftersom ingen kunde syssla med kodning av distribuerad berĂ€kning dĂ„, det var för svĂ„rt, och dĂ€rför fanns det fake shared memory med sidminnesskydd pĂ„ x86, och om du skrev till denna sida, sĂ„ sa vi till de andra processorerna att om de fick tillgĂ„ng till samma shared memory, mĂ„ste den laddas frĂ„n dig, och pĂ„ sĂ„ sĂ€tt skapades nĂ„got som liknar ett protokoll för cache-kohĂ€rens och mjukvara för detta. En intressant koncept. Det verkliga problemet var förstĂ„s ett annat. Allt detta fungerade, men du fick snabbt prestandaproblem, eftersom ingen förstod prestandamodellen pĂ„ tillrĂ€ckligt bra nivĂ„ – vilka Ă„tkomstmönster för minnet det fanns, hur man skulle se till att noderna inte oĂ€ndligt pingade varandra, och sĂ„ vidare.

I H2O kom jag pĂ„ följande: utvecklarna sjĂ€lva ansvarar för att definiera var parallellism gömmer sig och var den inte finns. Jag har kommit pĂ„ en kodningsmodell som gör det enkelt och lĂ€tt att skriva högpresterande kod. Men att skriva lĂ„ngsam kod Ă€r svĂ„rt, den kommer att se dĂ„lig ut. Det krĂ€vs en seriös insats för att skriva lĂ„ngsam kod; man mĂ„ste anvĂ€nda ovanliga metoder. LĂ„ngsam kod syns vid första ögonkastet. Som en följd av detta skrivs kod som vanligtvis fungerar snabbt, men man mĂ„ste ta reda pĂ„ vad man ska göra vid delad minne. Allt detta Ă€r knutet till stora arrayer, och beteendet dĂ€r liknar icke-volatile stora arrayer i Java parallellkörning. TĂ€nk dig att tvĂ„ trĂ„dar skriver till en parallell array, en av dem vinner, och den andra förlorar, och du vet inte vem som Ă€r vem. Om de inte Ă€r volatila kan ordningen vara vad som helst – och det fungerar verkligen bra. MĂ€nniskor bryr sig verkligen om operationsordning; de placerar volatile korrekt och förvĂ€ntar sig problem med prestanda, relaterade till minnet. Annars skulle de bara skriva kod som loopar frĂ„n 1 till N, dĂ€r N Ă€r nĂ„gra triljoner, i hopp om att alla komplexa fall automatiskt blir parallella – och det fungerar inte dĂ€r. Men i H2O Ă€r det varken Java eller Scala, man kan kalla det 'Java minus minus', om man vill. Det Ă€r en vĂ€ldigt lĂ€ttförstĂ„elig programmeringsstil och liknar skrivandet av enkel kod i C eller Java med loopar och arrayer. Men man kan ocksĂ„ hantera terabyte av minne. Jag anvĂ€nder fortfarande H2O. Ibland anvĂ€nder jag det i olika projekt – och det Ă€r fortfarande den snabbaste saken, tiotals gĂ„nger snabbare Ă€n konkurrenterna. Om du arbetar med Big Data med kolumnĂ€ra data Ă€r det mycket svĂ„rt att övertrĂ€ffa H2O.

Tekniska utmaningar

Andrey: Vad har varit den största utmaningen under din karriÀr?

Cliff: Diskuterar vi den tekniska eller icke-tekniska delen av frÄgan? Jag skulle sÀga att de största utmaningarna Àr icke-tekniska. 
NÀr det kommer till tekniska utmaningar. Jag övervann dem enkelt. Jag vet knappt vilken som var den största, men det fanns flera ganska intressanta som tog en hel del tid och mental kamp. NÀr jag började pÄ Sun var jag sÀker pÄ att jag skulle skapa en snabb kompilator, medan en massa seniorer sa att jag aldrig skulle lyckas. Men jag fortsatte pÄ den vÀgen, skrev en kompilator hela vÀgen till registerallokatorn, och den blev ganska snabb. Den var lika snabb som den moderna C1, men dÄ var allokatorn mycket lÄngsammare, och nÀr jag ser tillbaka var det ett problem med den stora datakonstruktionen. Jag behövde den för att skriva en grafisk registerallokator och jag förstod inte dilemmat mellan kodens uttrycksfullhet och hastighet, som var relevant pÄ den tiden och vÀldigt viktigt. Det visade sig att datakonstruktionen vanligtvis översteg cache-storleken pÄ x86 frÄn den tiden, sÄ om jag ursprungligen antog att registerallokatorn skulle stÄ för 5-10 procent av den totala tid som JIT:ade, sÄ blev det i verkligheten 50 procent.

Tiden gick, kompilatorn blev allt mer tydlig och effektiv, slutade generera avskyvĂ€rt kod i fler fall, och prestandan började allt oftare likna det som C-kompilatorn levererade. Om du förstĂ„s inte skrev skrĂ€p som ens C inte kunde optimera. Om du skrev kod som i C, fick du ocksĂ„ prestanda som i C i fler fall. Ju lĂ€ngre tiden gick, desto oftare fick jag kod som asymptotiskt överensstĂ€mde med C-nivĂ„, registerallokatorn började likna nĂ„got fĂ€rdigt... oavsett om din kod kördes snabbt eller lĂ„ngsamt. Jag fortsatte att arbeta pĂ„ allokatorn sĂ„ att den skulle göra bĂ€ttre allokeringar. Den blev lĂ„ngsammare och lĂ„ngsammare, men levererade allt bĂ€ttre prestanda i de fall dĂ€r ingen annan lyckades. Jag kunde dyka ner i registerallokatorn, grĂ€va ner en mĂ„nads arbete dĂ€r, och plötsligt började hela koden köra 5% snabbare. Det hĂ€nde gĂ„ng pĂ„ gĂ„ng och registerallokatorn blev som ett konstverk – alla Ă€lskade eller hatade den, och akademikerna stĂ€llde frĂ„gor som "varför görs allt pĂ„ det hĂ€r sĂ€ttet", varför inte. linjĂ€r skanning, och vad Ă€r skillnaden. Svaret Ă€r fortfarande detsamma: en allokerare baserad pĂ„ grafmĂ„lning plus mycket noggrant arbete med buffertkoden Ă€r ett vinnande verktyg, den bĂ€sta kombinationen som ingen kan besegra. Och detta Ă€r en ganska subtil sak. Allt annat som dĂ€r görs av kompilatorn Ă€r ganska vĂ€ldokumenterade saker, Ă€ven om de ocksĂ„ har förfinats till en konstform. Jag har alltid gjort saker som skulle omvandla kompilatorn till ett konstverk. Men inget av detta har varit extraordinĂ€rt – förutom registerallokeraren. PoĂ€ngen Ă€r att man mĂ„ste vara noggrann sĂ„ga av under belastning, och om det hĂ€nder (jag kan förklara mer om det om det Ă€r intressant) innebĂ€r det att man kan inlinera mer aggressivt, utan risk för att överskrida brytpunkten för prestandadiagrammet. PĂ„ den tiden fanns det massor av fullfjĂ€drade kompilatorer, överbelamrade med gimmickar och pryttlar, som hade registerallokerare, men ingen kunde nĂ„gonsin göra det pĂ„ nytt.

Problemet Ă€r att om du lĂ€gger till metoder som ska inlineas, och ökar inlineomrĂ„det, sĂ„ överstiger uppsĂ€ttningen av anvĂ€nda vĂ€rden genast antalet register, och du mĂ„ste börja spilla. Den kritiska nivĂ„n intrĂ€ffar vanligtvis nĂ€r allokatorn ger upp, och en bra kandidat för spilling Ă€r lika med en annan, och du börjar spilla riktigt vilda saker. VĂ€rdet av inlining hĂ€r Ă€r att du förlorar en del overhead, overhead för anrop och bevarande; du kan se vĂ€rdena inuti och du kan optimera dem vidare. Kostnaden för inlining Ă€r att det skapas ett stort antal levande vĂ€rden, och om din registerallokator spillar mer Ă€n nödvĂ€ndigt, förlorar du direkt. DĂ€rför har de flesta allokatorer ett problem: nĂ€r inlining passerar en viss grĂ€ns börjar allt i vĂ€rlden att spilla, och prestandan kan svĂ€mmas bort i toaletten. De som implementerar kompilatorn lĂ€gger till vissa heuristik: till exempel, att stoppa inlining efter en viss tillrĂ€ckligt stor storlek, eftersom allokeringarna förstör allt. Det skapas en böjning i prestandadiagrammet – du inlinear, inlinear, prestandan ökar lĂ„ngsamt – och sedan bang! – den faller ner snabbt som en domkraft, för du har inlineat för mycket. SĂ„ har det fungerat fram till Java. Java krĂ€ver mycket mer inlining, sĂ„ jag var tvungen att göra min allokator mycket mer aggressiv, sĂ„ att den jĂ€mnades ut istĂ€llet för att falla, och om du inlinear för mycket – börjar den spillas, men sedan kommer Ă€ndĂ„ ett ögonblick dĂ€r det inte finns mer spilling. Det Ă€r en intressant observation och den kom till mig helt plötsligt, inte uppenbar, men vĂ€l vĂ€rd insatsen. Jag började med aggressiv inlining och det ledde mig till platser dĂ€r prestandan för Java och C gĂ„r hand i hand. De Ă€r faktiskt ganska lika – jag kan skriva Java-kod som Ă€r betydligt snabbare Ă€n C-kod och liknande, men i det stora hela Ă€r de ungefĂ€r jĂ€mförbara. Jag tror att en del av denna förtjĂ€nst kommer frĂ„n registerallokatorn, som lĂ„ter mig inlinea pĂ„ ett rĂ€tt primitivt sĂ€tt. Jag inlinear helt enkelt allt jag ser. FrĂ„gan hĂ€r Ă€r om allokatorn fungerar bra, och om det resulterande koden Ă€r rimligt fungerande. Det var en stor utmaning: att förstĂ„ allt detta och fĂ„ det att fungera.

Lite om registerallokering och flerkÀrnighet

Vladimir: Problem som registreringsallokering verkar vara ett evigt Àmne. Det Àr intressant att frÄga sig om det fanns nÄgon idé som verkade lovande men sedan misslyckades i praktiken?

Cliff: SjĂ€lvklart! Registreringsallokering Ă€r ett omrĂ„de dĂ€r du försöker hitta heuristik för att lösa ett NP-fullstĂ€ndigt problem. Och du kommer aldrig att kunna uppnĂ„ en perfekt lösning, eller hur? Det Ă€r helt enkelt omöjligt. TĂ€nk pĂ„ Ahead of Time-kompilering – den fungerar ocksĂ„ dĂ„ligt. HĂ€r talar vi om genomsnittliga fall. Om typisk prestanda, sĂ„ du kan gĂ„ och mĂ€ta nĂ„got som du anser vara bra typisk prestanda – i slutĂ€ndan arbetar du ju med att förbĂ€ttra den! Registreringsallokering Ă€r ett Ă€mne helt Ă€gnat Ă„t prestanda. SĂ„ snart du har en första prototyp som fungerar och mĂ„lar det som behöver mĂ„las, börjar arbetet med prestanda. Du mĂ„ste lĂ€ra dig att mĂ€ta ordentligt. Varför Ă€r detta viktigt? Om det finns tydliga data kan du titta pĂ„ olika omrĂ„den och se: aha, det hjĂ€lpte hĂ€r, men dĂ€r bröts allt ihop! Det dyker upp bra idĂ©er, du lĂ€gger till en ny heuristik och plötsligt fungerar allt i genomsnitt lite bĂ€ttre. Eller inte. Jag har haft mĂ„nga fall dĂ€r vi kĂ€mpade för fem procent prestanda som skiljde vĂ„r utveckling frĂ„n den tidigare allokatorn. Och varje gĂ„ng ser det ut sĂ„ hĂ€r: nĂ„gonstans vinner, nĂ„gonstans förlorar. Om du har bra verktyg för prestandaanalys kan du hitta de förlorande idĂ©erna och förstĂ„ varför de förlorar. Kanske Ă€r det vĂ€rt att lĂ„ta allt vara som det Ă€r, eller sĂ„ kanske vi behöver ta ett mer seriöst grepp om finjusteringen, eller gĂ„ och fixa nĂ„got annat. Det handlar om en hel uppsĂ€ttning saker! Jag gjorde den hĂ€r coola hack, men jag behöver ocksĂ„ det, och det, och det – och den sammanlagda kombinationen ger vissa förbĂ€ttringar. Enskilda insatser kan misslyckas. SĂ„dan Ă€r naturen av att arbeta med prestanda för NP-fullstĂ€ndiga problem.

Vladimir: Det kĂ€nns som om frĂ„gor som mĂ„lning i allokatorer Ă€r ett redan löst problem. Tja, för er tycks det löst, baserat pĂ„ vad ni berĂ€ttar, sĂ„ Ă€r det ens vĂ€rt


Cliff: Det Ă€r inte varken löst eller olöst. Det Ă€r du som mĂ„ste göra det till "löst". Det finns tunga uppgifter och de mĂ„ste lösas. NĂ€r detta Ă€r gjort, kommer det dags att fokusera pĂ„ prestanda. Man mĂ„ste förhĂ„lla sig till detta arbete pĂ„ rĂ€tt sĂ€tt – utföra benchmarktester, samla in mĂ€tvĂ€rden, förklara situationer nĂ€r en rollback till en tidigare version fĂ„r din gamla hack att fungera igen (eller tvĂ€rtom, sluta fungera). Och ge inte upp förrĂ€n du har uppnĂ„tt nĂ„got. Som jag nĂ€mnde, finns det fantastiska idĂ©er som inte fungerade, men inom registerallokeringsomrĂ„det Ă€r idĂ©er i princip oĂ€ndliga. Man kan till exempel lĂ€sa vetenskapliga publikationer. Även om detta omrĂ„de nu rör sig lĂ„ngsammare och har blivit tydligare Ă€n pĂ„ sin ungdom, arbetar en oĂ€ndlig mĂ€ngd mĂ€nniskor inom detta omrĂ„de, och alla deras idĂ©er Ă€r vĂ€rda att pröva; alla vĂ€ntar pĂ„ sitt tillfĂ€lle. Och du kan inte sĂ€ga hur bra de Ă€r om du inte prövar dem. Hur vĂ€l de integreras med allt annat i din allokerare, eftersom allokeraren gör mĂ„nga saker, och vissa idĂ©er kommer inte att fungera i din specifika allokerare, men kanske fungerar de i en annan. Huvudstrategin för en allokerare Ă€r att ta bort lĂ„ngsamma sekvenser frĂ„n den primĂ€ra vĂ€gen och tvinga uppdelning vid grĂ€nserna för de lĂ„ngsamma vĂ€garna. DĂ€rför, om du vill starta GC, följa den lĂ„ngsamma vĂ€gen, deoptimera, kasta undantag, allt sĂ„dant – du vet att dessa saker Ă€r relativt sĂ€llsynta. Och de Ă€r faktiskt sĂ€llsynta, jag har kollat. Du gör extra arbete och dĂ€rmed försvinner mĂ„nga begrĂ€nsningar pĂ„ dessa lĂ„ngsamma vĂ€gar, men det spelar inte sĂ„ stor roll eftersom de Ă€r lĂ„ngsamma och sĂ€llan anvĂ€nds. Till exempel, en nullpekaresituation – det hĂ€nder aldrig, eller hur? Du behöver ha flera vĂ€gar för olika situationer, men de ska inte kollidera pĂ„ den primĂ€ra vĂ€gen. 

Vladimir: Vad tycker du om flerkĂ€rniga processorer nĂ€r det finns tusentals kĂ€rnor? Är det nĂ„got anvĂ€ndbart?

Cliff: FramgÄngen för GPU:er visar att det faktiskt Àr ganska anvÀndbart!

Vladimir: De Àr ganska specialiserade. Vad sÀgs om allmÀnna processorer?

Cliff: Ja, det var affÀrsmodellen för Azul. Svaret kom under en tid nÀr mÀnniskor verkligen uppskattade förutsÀgbar prestanda. Det var ganska svÄrt att skriva parallellkod dÄ. H2O-kodningsmodellen Àr vÀlskalbar, men den Àr inte en allmÀn anvÀndningsmodell. Kanske nÄgot mer generell Àn nÀr man anvÀnder GPU. Pratar vi om svÄrigheterna med att utveckla nÄgot sÄdant eller svÄrigheterna med att anvÀnda det? Till exempel, en intressant lÀxa som Azul lÀrde mig, ganska ovÀntad: smÄ cachar Àr bra. 

Den största utmaningen i livet

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

Cliff: Den största utmaningen var att inte vara... snĂ€ll och bra mot folk. Som en följd hamnade jag stĂ€ndigt i extremt konfliktfyllda situationer. I de som jag visste gick snett, men jag visste inte hur jag skulle gĂ„ vidare med att lösa dessa problem och kunde inte hantera dem. MĂ„nga lĂ„ngvariga problem som pĂ„gĂ„tt i Ă„rtionden uppkom just pĂ„ det sĂ€ttet. Att det finns C1- och C2-kompilatorer i Java Ă€r en direkt följd av detta. Att det inte fanns nĂ„got flernivĂ„kompilering i Java under ett decennium Ă€r ocksĂ„ en direkt följd. Det Ă€r uppenbart att vi behövde ett sĂ„dant system, men inte uppenbart varför det inte fanns. Jag hade problem med en ingenjör... eller en grupp ingenjörer. LĂ„ngt tillbaka, nĂ€r jag började arbeta pĂ„ Sun, var jag... Okej, inte bara dĂ„, jag har alltid min egen Ă„sikt om allt. Och jag ansĂ„g att jag kunde ta den hĂ€r min sanningen och framföra den rakt pĂ„ sak. Speciellt eftersom jag var chockerande rĂ€tt merparten av tiden. Och om du inte gillar det förhĂ„llningssĂ€ttet... sĂ€rskilt om du uppenbarligen har fel och gör dumheter... Ja, fĂ„ mĂ€nniskor kunde tolerera den typen av kommunikation. Men vissa kunde, till exempel jag. Jag har byggt hela mitt liv pĂ„ meritoriska principer. Om du visar mig nĂ„got fel, vĂ€nder jag mig genast om och sĂ€ger: du sa nĂ„got dumt. Givetvis ber jag om ursĂ€kt, och allt det dĂ€r, jag erkĂ€nner meriterna om det finns nĂ„gra, och jag gör andra korrekta handlingar. Å andra sidan har jag chockerande rĂ€tt en chockerande stor procent av tiden. Och det fungerar inte sĂ€rskilt bra i relationer med mĂ€nniskor. Jag strĂ€var inte efter att vara trevlig, utan stĂ€ller frĂ„gan pĂ„ sin spets. "Det hĂ€r kommer aldrig att fungera, för att ett, tvĂ„ och tre." Och de sĂ€ger: "Oj!". Det fanns andra konsekvenser ocksĂ„, som kanske Ă€r bĂ€ttre att hoppa över: till exempel de som ledde till skilsmĂ€ssa frĂ„n min fru och ett decennium av depression efter det.

Utmaningen handlar om att kĂ€mpa mot mĂ€nniskor och deras uppfattningar om vad du kan eller inte kan göra, vad som Ă€r viktigt och vad som inte Ă€r det. Det har funnits mĂ„nga utmaningar kring kodningsstil. Jag skriver fortfarande mycket kod, och under den tiden var jag 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 enda. NĂ€r jag ser tillbaka skrev jag hĂ€lften av koden för Java JIT-teamet och C2-teamet. NĂ€sta snabbaste kodare skrev hĂ€lften sĂ„ snabbt, den efter honom Ă€nnu lĂ„ngsammare och det var en exponentiell nedgĂ„ng. Den sjunde personen i raden var vĂ€ldigt, vĂ€ldigt lĂ„ngsam – sĂ„ Ă€r det alltid! Jag har rört vid en massa kod. Jag har sett vad alla skriver, utan undantag, jag har granskat deras kod, och jag fortsatte fortfarande att skriva mer Ă€n nĂ„gon av dem. Denna metod fungerar inte sĂ„ bra med mĂ€nniskor. Vissa gillar inte det. Och nĂ€r de inte kan hantera det, börjar alla möjliga klagomĂ„l. Till exempel sa nĂ„gon till mig en gĂ„ng att jag skulle sluta skriva kod, eftersom jag skrev för mycket kod, och det hotade teamet. För mig lĂ€t allt detta som ett skĂ€mt: om hela kvarvarande teamet försvinner och jag fortsĂ€tter skriva kod, förlorar du bara hĂ€lften av teamet. Å andra sidan, om jag fortsĂ€tter skriva kod och du förlorar hĂ€lften av teamet – det lĂ„ter som vĂ€ldigt dĂ„lig ledarskap. Jag har aldrig tĂ€nkt sĂ€rskilt mycket pĂ„ det, aldrig pratat om det, men det var Ă€ndĂ„ nĂ„gonstans i mitt huvud. En tanke som snurrade runt i bakhuvudet: "Är ni pĂ„ riktigt?". SĂ„ den största problemen var jag och mina relationer med mĂ€nniskor. Nu förstĂ„r jag mig sjĂ€lv mycket bĂ€ttre, jag har lĂ€nge varit teamledare för programmerare, och nu sĂ€ger jag rakt ut till folk: du vet, jag Ă€r den jag Ă€r, och ni fĂ„r hantera mig – Ă€r det okej om jag stĂ„r hĂ€r? Och nĂ€r de började hantera det, började allt fungera. Jag Ă€r faktiskt varken dĂ„lig eller bra, jag har inga dĂ„liga avsikter eller sjĂ€lviska strĂ€vanden, det Ă€r bara min natur, och man mĂ„ste pĂ„ nĂ„got sĂ€tt leva med det.

Andrey: Nyligen har alla börjat prata om sjÀlvmedvetenhet för introverta och om mjuka fÀrdigheter överlag. Vad kan man sÀga om det?

CliffJa, det var en insikt och en lĂ€xa som jag fick ut av min skilsmĂ€ssa frĂ„n min fru. Det jag lĂ€rde mig av skilsmĂ€ssan – Ă€r förstĂ„elsen för mig sjĂ€lv. SĂ„ började jag förstĂ„ andra mĂ€nniskor. FörstĂ„ hur denna interaktion fungerar. Det ledde till insikter en efter en. Jag blev medveten om vem jag Ă€r och vad jag representerar. Vad jag gör: antingen Ă€r jag fokuserad pĂ„ uppgiften, eller sĂ„ undviker jag konflikt, eller nĂ„got annat – och denna nivĂ„ av sjĂ€lvkĂ€nnedom hjĂ€lper verkligen till att hĂ„lla mig lugn. Efter det gĂ„r allt mycket lĂ€ttare. En sak som jag har upptĂ€ckt, inte bara hos mig sjĂ€lv utan ocksĂ„ hos andra programmörer – Ă€r oförmĂ„gan att verbalisera tankar nĂ€r man Ă€r i ett tillstĂ„nd av kĂ€nslomĂ€ssig stress. Till exempel, du sitter och kodar, Ă€r i ett flödeslĂ€ge, och plötsligt kommer nĂ„gon springande och börjar skrika i panik om att nĂ„got har gĂ„tt sönder, och att extrema Ă„tgĂ€rder nu kommer att tillĂ€mpas. Och du kan inte fĂ„ fram ett ord, för du befinner dig 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Ă„ över till en retrĂ€ttplan, efter vilket du kan börja agera. SĂ„ ja, nĂ€r du börjar förstĂ„ hur allt det hĂ€r fungerar – Ă€r det en enorm livsförĂ€ndrande hĂ€ndelse. 
Jag kunde sjĂ€lv inte hitta de rĂ€tta orden, men jag minns handlingssekvensen. PoĂ€ngen Ă€r att denna reaktion Ă€r lika fysisk som verbal, och du behöver utrymme. SĂ„dant utrymme, i zen-sinne. Det Ă€r precis detta som mĂ„ste förklaras, och sedan omedelbart backa – rent fysiskt backa. NĂ€r jag Ă€r tyst med ord, kan jag bearbeta situationen pĂ„ en kĂ€nslomĂ€ssig nivĂ„. NĂ€r adrenalin nĂ„r hjĂ€rnan och slĂ„r om dig till "slĂ„ eller fly"-lĂ€get, kan du inte lĂ€ngre sĂ€ga nĂ„got, nej – nu Ă€r du en idiot, en ingenjör för att bli slagen, oförmögen till ett vĂ€rdigt svar eller ens för att stoppa attacken, och angriparen kan fritt attackera igen och igen. Först mĂ„ste du bli dig sjĂ€lv igen, Ă„terfĂ„ kontrollen, och komma ut ur "slĂ„ eller fly"-lĂ€get.

Och för detta behövs ett verbalt utrymme. Bara en fri plats. Om man överhuvudtaget ska sĂ€ga nĂ„got, kan man just pĂ„stĂ„ detta och sedan gĂ„ ut och faktiskt hitta sitt "utrymme": ta en promenad i parken, stĂ€nga in sig i duschen – spelar ingen roll. Det viktiga Ă€r att temporĂ€rt koppla bort frĂ„n situationen. SĂ„ snart du ens för en kort stund kopplar bort, kommer kontrollen tillbaka, och du börjar tĂ€nka klart. "Okej, jag Ă€r inte nĂ„gon idiot, jag gör inte dumma saker, jag Ă€r en rĂ€tt sĂ„ nyttig person." SĂ„ snart du har övertygat dig sjĂ€lv, Ă€r det dags att gĂ„ vidare till nĂ€sta steg: förstĂ„ vad som har hĂ€nt. Du blev attackerad, attacken kom frĂ„n ett ovĂ€ntat hĂ„ll, det var en orĂ€ttvis och skamlig fĂ€lla. Det Ă€r dĂ„ligt. NĂ€sta steg Ă€r att förstĂ„ varför angriparen behövde göra det. Verkligen, varför? Kanske för att han sjĂ€lv Ă€r rasande? Varför Ă€r han rasande? Till exempel för att han sjĂ€lv har misslyckats och inte kan ta ansvar? PĂ„ detta sĂ€tt behöver du noggrant bearbeta hela situationen. Men för detta behövs utrymme för manövrering, ett verbalt utrymme. Det första steget Ă€r att bryta det verbala kontakten. GĂ„ bort frĂ„n diskussionen verbalt. Avbryt det, kom bort sĂ„ snabbt som möjligt. Om det Ă€r ett telefonsamtal – lĂ€gg bara pĂ„ luren – detta Ă€r en fĂ€rdighet jag fick frĂ„n att prata med min exfru. Om samtalet inte leder till nĂ„got bra, sĂ€g bara "hejdĂ„" och lĂ€gg pĂ„. PĂ„ andra sidan röret: "bla-bla-bla", du svarar: "aha, hejdĂ„!" och lĂ€gger pĂ„. Du avbryter samtalet. Fem minuter senare, nĂ€r du fĂ„r tillbaka din förmĂ„ga att tĂ€nka klart, har du svalnat lite, blir det möjligt att tĂ€nka igenom vad som verkligen hĂ€nde och vad som ska hĂ€nda hĂ€rnĂ€st. Och börja formulera ett genomtĂ€nkt svar, istĂ€llet för att bara reagera kĂ€nslomĂ€ssigt. För mig har det varit en genombrott i sjĂ€lvmedvetande att inse att jag inte kan prata i situationer med kĂ€nslomĂ€ssig stress. Att ta sig ur detta tillstĂ„nd, tĂ€nka igenom och planera hur man ska svara och kompensera för problemen – det Ă€r de rĂ€tta stegen nĂ€r man inte kan prata. Det enklaste sĂ€ttet Ă€r att springa bort frĂ„n situationen dĂ€r den kĂ€nslomĂ€ssiga stressen uppstĂ„r och bara sluta delta i den stressen. Efter det Ă„terfĂ„r du förmĂ„gan att tĂ€nka, nĂ€r du kan tĂ€nka, dyker möjligheten att prata upp, och sĂ„ vidare.

För att vara Ă€rlig, i domstolen försöker motpartens advokat anvĂ€nda detta mot dig – nu förstĂ„r du varför. För att han har möjlighet att trycka ner dig till en punkt dĂ€r du inte ens kan uttala ditt eget namn, till exempel. I bokstavlig mening, kan du inte prata. Om detta hĂ€nder dig och om du vet att du kommer att befinna dig pĂ„ en plats dĂ€r verbala strider pĂ„gĂ„r, som i en domstol, kan du ta med dig din advokat. Advokaten kommer att stĂ„ upp för dig och avbryta den verbala attacken, och han gör det helt lagenligt, och ditt förlorade zenutrymme kommer att Ă„tervĂ€nda. Till exempel, jag behövde ringa min familj ett par gĂ„nger, domaren var helt vĂ€nlig, men motpartens advokat skrek och skrek pĂ„ mig, jag kunde inte ens fĂ„ in ett ord. I sĂ„dana fall fungerar anvĂ€ndningen av en medlare bĂ€st för mig. Medlaren stoppar allt detta tryck som flödar över dig frĂ„n en oavbruten ström, du Ă„terfinner det nödvĂ€ndiga zenutrymmet, och med det kommer förmĂ„gan att prata tillbaka. Det Ă€r ett helt kunskapsomrĂ„de, dĂ€r det finns mycket att lĂ€ra sig, mycket att upptĂ€cka inom sig sjĂ€lv, och allt detta förvandlas till högkvalitativa strategiska beslut, som Ă€r olika för olika mĂ€nniskor. Vissa har inte de ovan beskrivna problemen, vanligtvis har de inte problem, personer som arbetar professionellt med försĂ€ljning. Alla dessa mĂ€nniskor som lever pĂ„ sina ord – kĂ€nda sĂ„ngare, poeter, religiösa ledare och politiker, de har alltid nĂ„got att sĂ€ga. De har inte dessa problem, men jag har dem.

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

Det kommer att vara möjligt att fortsÀtta kommunicera med Cliff pÄ konferensen Hydra 2019, som kommer att Àga rum den 11-12 juli 2019 i Sankt Petersburg. Han kommer att delta med en presentation «The Azul Hardware Transactional Memory experience». Biljetter kan köpas pÄ den officiella sajten.

KĂ€lla: habr.com

Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar đŸ”„ Köp pĂ„litlig webbhotell med DDoS-skydd, VPS VDS-servrar | ProHoster