Liels intervija ar Klifu Kliku — JIT kompilācijas tēvu Java valodā

Liels intervija ar Klifu Kliku — JIT kompilācijas tēvu Java valodāKliffs Kliks — CTO uzņēmumā Cratus (IoT sensori procesu uzlaboÅ”anai), dibinātājs un lÄ«dzdibinātājs vairākiem start-up uzņēmumiem (ieskaitot Rocket Realtime School, Neurensic un H2O.ai) ar vairākiem veiksmÄ«giem izieÅ”anas darÄ«jumiem. Kliffs uzrakstÄ«ja savu pirmo kompilatoru 15 gadu vecumā (Pascal priekÅ” TRS Z-80)! ViņŔ ir vislabāk pazÄ«stams ar darbu pie C2 valodas Java (the Sea of Nodes IR). Å is kompilators parādÄ«ja pasaulei, ka JIT var ražot kvalitatÄ«vu kodu, kas kļuva par vienu no faktoriem, kāpēc Java ir viena no galvenajām mÅ«sdienu programmatÅ«ras platformām. Pēc tam Kliffs palÄ«dzēja uzņēmumam Azul Systems uzbÅ«vēt 864 kodolu galveno datoru ar programmatÅ«ru tÄ«rā Java, kas atbalstÄ«ja GC pauzes 500 gigabaitu heapā 10 milisekundēs. PatiesÄ«bā Kliffs ir strādājis visos JVM aspektos.

Ā 
Å is habraposts ir liels intervija ar Kliffu. Mēs runāsim par Ŕādām tēmām:

  • PārslēgÅ”anās uz zemo lÄ«meņu optimizācijām
  • Kā veikt lielu refaktorizāciju
  • Izmaksu modelis
  • MācÄ«bas par zemo lÄ«meņu optimizācijām
  • Praktiski piemēri veiktspējas uzlaboÅ”anai
  • Kāpēc radÄ«t savu programmēŔanas valodu
  • Veiktspējas inženiera karjera
  • Tehniskie izaicinājumi
  • Nedaudz par reÄ£istru pieŔķirÅ”anu un daudzprocesoru skaitļoÅ”anu
  • Lielākais izaicinājums dzÄ«vē

Interviju vada:

  • Andrej Satārin no Amazon Web Services. Savā karjerā ir paspējis strādāt dažādos projektos: testējis izplatÄ«tu NewSQL datubāzi Yandex, mākoņdetectēŔanas sistēmu Kaspersky laboratorijā, daudzu spēlētāju spēli Mail.ru un valÅ«tas cenu aprēķina servisu Deutsche Bank. Interesējas par plaÅ”u aizmugurē esoÅ”o un izplatÄ«to sistēmu testēŔanu.
  • Vladimirs Sitņikovs no Netcracker. Desmit gadus strādā pie veiktspējas un mērogotspējas NetCracker OS — programmatÅ«ra, ko lieto sakaru operatori tÄ«klu pārvaldÄ«bas un tÄ«kla aprÄ«kojuma automatizācijas procesu automatizēŔanai. Interesējas par Java un Oracle Database veiktspēju. Vairāk nekā desmit veiktspējas uzlabojumu autors oficiālajam PostgreSQL JDBC draiverim.

PārslēgÅ”anās uz zemo lÄ«meņu optimizācijām

Andrej: JÅ«s esat pazÄ«stams cilvēks JIT kompilācijas, Java un veiktspējas jomā kopumā, vai ne?Ā 

Kliffs: TieŔi tā!

Andrej: Sāksim ar vispārÄ«giem jautājumiem par veiktspējas uzlaboÅ”anu. Ko jÅ«s domājat par izvēli starp augsta lÄ«meņa un zemo lÄ«meņu optimizācijām, piemēram, darbu pie CPU lÄ«meņa?

Kliffs: Å eit viss ir vienkārÅ”i. Ātrākais kods ir tas, kuru nekad nesāk. Tāpēc vienmēr jāsāk no augsta lÄ«meņa un jāstrādā pie algoritmiem. Labāka O-notācija uzvarēs sliktāku O-notāciju, izņemot gadÄ«jumus, ja iejaucas pietiekami lieli koeficienti. Zema lÄ«meņa lietas tiek apskatÄ«tas paŔās beigās. Parasti, ja esat pietiekami labi optimizējis visu pārējo steku un joprojām ir kaut kas interesants, tad tas arÄ« ir zems lÄ«menis. Bet kā sākt no augsta lÄ«meņa? Kā zināt, ka augstā lÄ«meņa darbā ir paveikts pietiekami daudz? Nu... nav nekādu gatavu recepÅ”u. Jāizpēta problēma, jānoskaidro, ko grasies darÄ«t (lai neveiktu nevajadzÄ«gus nākamos soļus), un tad var sākt izmantot profileru, kas var pateikt kaut ko noderÄ«gu. Kādā brÄ«dÄ« jÅ«s paÅ”i saprotat, ka esat atbrÄ«voties no nevajadzÄ«gām lietām un pienākusi laiks pievērst uzmanÄ«bu zema lÄ«meņa optimizācijai. Tas noteikti ir Ä«paÅ”a veida māksla. Daudzi cilvēki dara nevajadzÄ«gas lietas, bet kustas tik ātri, ka par sniegumu viņiem nav laika domāt. Bet tā ir, lÄ«dz brÄ«dim, kad jautājums kļūst kritisks. Parasti 99% laika nevienam nav interese, ko es daru, lÄ«dz kritiskās ceļā neparādās kāda svarÄ«ga lieta, kas kādam interesē. Un tieÅ”i tad visi sāk par tevi prasÄ«t, ā€žkāpēc tas no paÅ”a sākuma nedarbojās ideāliā€. Kopumā vienmēr ir kas jāuzlabo sniegumā. Bet 99% laika tev nav pavedienu! Tu vienkārÅ”i mēģini panākt, lai kaut kas strādātu, un Ŕī procesa laikā saproti, kas ir svarÄ«gi. Nekad nav iespējams iepriekÅ” zināt, ka tieÅ”i Å”is fragments ir jāizveido ideāls, tāpēc bÅ«tÄ«bā jācenÅ”as bÅ«t ideālam visā. Bet tas nav iespējams un tā tu nedari. Vienmēr ir daudz lietu, kas jāremontē – un tas ir pilnÄ«gi normāli.

Kā veikt lielu refaktorizāciju

Andrej: Kā jÅ«s strādājat pie snieguma? Tas ir universāls jautājums. Piemēram, vai jums ir nācies strādāt pie problēmām, kas rodas no daudzu jau esoÅ”o funkcionalitātes savietojuma?

Kliffs: Es cenÅ”os to izvairÄ«ties. Ja es zinu, ka veiktspēja kļūs par problēmu, es par to domāju vēl pirms kodēŔanas uzsākÅ”anas, it Ä«paÅ”i par datu struktÅ«rām. Bet bieži vien tu to atklāj tikai daudz vēlāk. Un tad ir jāiet uz ekstrēmiem pasākumiem un jādara tas, ko es saucu par 'pārprogrammēŔanu un pārvaldīŔanu': jāņem pietiekami liels gabals. Kāda daļa koda tāpat jāpārprogrammē sakarā ar veiktspējas problēmām vai kādu citu iemeslu. Kāds jebkurÅ” koda pārprogrammēŔanas iemesls var bÅ«t – gandrÄ«z vienmēr ir labāk pārprogrammēt lielāku gabalu nekā mazāku. Tajā brÄ«dÄ« visi sāk drebēt no bailēm: 'Ak Dievs, nevar taču izmainÄ«t tik daudz koda!'. Bet, patiesÄ«bā, Ŕāds pieejas veids gandrÄ«z vienmēr darbojas daudz labāk. Jāķeras pie lielas problēmas uzreiz, jāizveido liels aplis ap to un jāsaka: viss, kas ir iekŔā aplÄ«, es to pārprogrammēŔu. Robeža taču ir daudz mazāka par to saturu iekŔā, kuru nepiecieÅ”ams nomainÄ«t. Un, ja Ŕāds robežas noteikums ļaus perfekti paveikt iekŔējās darbu – tev ir brÄ«vas rokas, dari ko vēlies. TiklÄ«dz tu saproti problēmu, pārprogrammēŔanas process norit daudz vieglāk, tāpēc ņem lielu gabalu!
Tajā paŔā laikā, kad tu pārprogrammē lielu gabalu un apzini, ka veiktspēja kļūs par problēmu, vari uzreiz sākt par to uztraukties. Parasti tas pārvērÅ”as vienkārŔās lietās, piemēram, 'nepārvietojiet datus, pārvaldiet datus pēc iespējas vienkārŔāk, padariet tos mazākus'. Lielās pārprogrammēŔanās gadÄ«jumos ir standartveida veidi, kā uzlabot veiktspēju. Un tie gandrÄ«z vienmēr ir saistÄ«ti ar datiem.

Izmaksu modelis

Andrej: Vienā no podkāstiem jūs runājāt par cenu modeļiem veiktspējas kontekstā. Vai varētu paskaidrot, ko jūs ar to domājāt?

Kliffs: Protams. Es piedzimstÄ«ju laikmetā, kad procesoru veiktspēja bija ārkārtÄ«gi svarÄ«ga. Un Ŕī ēra atkal atgriežas – liktenis neizbēgami ironizē. Es sāku dzÄ«vot astoņbitu maŔīnu laikmetā, mans pirmais dators darbojās ar 256 baitiem. TieŔām, ar baitiem. Viss bija ļoti neliels. Bija jāskaita instrukcijas, un tiklÄ«dz sākām virzÄ«ties augstāk programmēŔanas valodu stekā, valodas uzņēma aizvien vairāk un vairāk. Bija Assembler, tad Basic, tad C, un C uzņēma darbu ar daudzām detaļām, piemēram, reÄ£istru sadali un instrukciju plānoÅ”anu. Bet tur viss bija diezgan skaidrs, un, ja es izveidoju rādÄ«tāju uz mainÄ«gā instanci, es saņemÅ”u load, un Å”ai instrukcijai izmaksas ir zināmas. Dzelži sniedz zināmu skaitu maŔīnu ciklu, tāpēc dažādu uzdevumu izpildes ātrumu var aprēķināt, vienkārÅ”i summējot visas instrukcijas, kuras tu plāno palaist. Katru compare/test/branch/call/load/store varējām saskaitÄ«t un teikt: lÅ«k, te ir izpildes laiks. Nodarbējoties ar veiktspējas uzlaboÅ”anu, tu noteikti pievērsÄ«si uzmanÄ«bu tam, kādi skaitļi atbilst mazām karstām ciklu reakcijām.Ā 
Bet tiklÄ«dz tu pāriet uz Java, Python un tamlÄ«dzÄ«gām lietām, tu ļoti ātri attālinies no zemo lÄ«meņu dzelžiem. Kāda ir getters izsaukuma izmaksas Java? Ja JIT HotSpotā viss ir pareizi iekļauts, tas bÅ«s load, bet, ja viņŔ to nav izdarÄ«jis – tas bÅ«s funkcijas izsaukums. Tā kā izsaukums atrodas karstajā ciklā, tas atcels visas pārējās optimizācijas Å”ajā ciklā. Tāpēc reālā cena bÅ«s daudz lielāka. Un tu uzreiz zaudē spēju skatÄ«ties uz koda fragmentu un saprast, kādēļ mums to vērts veikt attiecÄ«bā uz procesora takts frekvenci, izmantoto atmiņu un keÅ”atmiņu. Viss tas kļūst interesants tikai tad, ja tieŔām dziļi iegrims veiktspējā.
Tagad mēs esam situācijā, kad procesoru ātrumi jau vairāk nekā desmit gadu laikā gandrÄ«z nav auguÅ”i. Vecie laiki atgriežas! JÅ«s vairs nevarat paļauties uz labu vienas pavediena veiktspēju. Bet, ja notiks paralēlās aprēķina uzsākÅ”ana – tas ir ārkārtÄ«gi sarežģīti, visi skatās uz jums kā uz Džeimsu Bondu. DesmitkārtÄ«gas paātrināŔanas parasti notiek tajās vietās, kur kāds ir kaut ko izlaidis. Paralēlismam ir nepiecieÅ”ams daudz darba. Lai sasniegtu Å”o desmitkārtÄ«go paātrinājumu, ir jāizprot izmaksu modelis. Kas un cik maksā. Un, lai to izprastu, jāsaprot, kā valoda tiek uzlikta uz zemāk esoŔā dzelzs.
Mārtins Tompsons savam emuāram atrada lielisku vārdu Mehāniskā simpātija! Ir jāizprot, ko plāno darÄ«t dzelzs, kā tieÅ”i tas to darÄ«s un kāpēc tas vispār dara to, ko dara. Izmantojot to, ir diezgan vienkārÅ”i sākt saskaitÄ«t instrukcijas un noskaidrot, kurp iet izpildes laiks. Ja tev nav attiecÄ«gās sagatavotÄ«bas, tu vienkārÅ”i meklē melnu kaÄ·i tumŔā istabā. Es pastāvÄ«gi redzu cilvēkus, kas optimizē veiktspēju, kuriem nav ne mazākās nojausmas, ko lāga viņi vispār darā. Viņi ļoti cieÅ” un nekur Ä«paÅ”i nevirzās uz priekÅ”u. Un, kad es paņemu to paÅ”u koda gabalu, ielikdams tur pāris sÄ«ku haku, un iegÅ«stu pieckārtÄ«gu vai desmitkārtÄ«gu paātrinājumu, viņi saka: nu, tas nav godÄ«gi, mēs arÄ« tā zinājām, ka tu esi labāks. PārsteidzoÅ”i. Par ko es runāju… izmaksu modelis – tas ir par to, kādu kodu tu raksti un cik ātri tas vidēji darbojas vispārējā attēlā.

Andrej: Un kā tādu apjomu noturēt prātā? To sasniedz ar lielu pieredzi, vai ne? Kur iegūt tādu pieredzi?

Kliffs: Nu, savu pieredzi es ieguvu ne vistuvākajā ceļā. Es programmēju AssemblerÄ« vēl tajos laikos, kad varēja izprast katru atseviŔķu instrukciju. Tas izklausās muļķīgi, bet no tā laika man prātā, atmiņā, uz visiem laikiem ir palicis Z80 instrukciju komplekts. Es neatceros cilvēku vārdus jau minÅ«ti pēc sarunas, bet atceros kodu, kas rakstÄ«ts pirms 40 gadiem. Interesanti, tas izskatās kā ā€žzinātnieka idiotsĀ».

Mācības par zemo līmeņu optimizācijām

Andrej: Vai ir kāds vienkārŔāks veids, kā iesaistīties?

Kliffs: Jā un nē. Dzelzs, ko mēs visi izmantojam, Å”ajā laikā bÅ«tiski nemainÄ«jās. Visi izmanto x86, izņemot viedtālruņus uz Arm. Ja tu nenodarbojies ar kādu hardcore iebÅ«vēto sistēmu, tev viss ir tieÅ”i tāds pats. Labi, tālāk. Instrukcijas arÄ« gadsimtus nav mainÄ«juŔās. Jāiet un jāraksta kaut kas par Assembler. Nedaudz, bet pietiekami, lai sāktu saprast. JÅ«s smaidāt, bet es pilnÄ«gi nopietni to saku. Jāizprot valodas un dzelža attiecÄ«bas. Pēc tam jāiet, jāuzraksta nedaudz un jāizveido neliels rotaļu kompilers nelielai rotaļu valodai. 'Rotaļu' nozÄ«mē, ka jāizveido to saprātÄ«gā laikā. Tam var bÅ«t supervienkārÅ”a struktÅ«ra, bet tam jāģenerē instrukcijas. Instrukciju Ä£enerēŔanas akts ļaus saprast izmaksu modeli tiltam starp augstā lÄ«meņa kodu, ko visi raksta, un maŔīnkodu, kas izpildās uz dzelža. Å Ä« attiecÄ«ba paliks prātos brÄ«dÄ«, kad rakstÄ«siet kompileri. Pat visvienkārŔākajam kompilerim. Pēc tam var sākt skatÄ«ties uz Java un saprast, ka tās semantiskā kloķēŔana ir daudz dziļāka, un tās virsÅ« uzcelt tiltus ir daudz grÅ«tāk. Java ir daudz grÅ«tāk saprast, vai mÅ«su tilts ir labs vai slikts, kas to var sabojāt un kas nē. Bet tev ir nepiecieÅ”ama kāda sākumpunkts, kad tu skaties uz kodu un saproti: 'Aha, Å”im getteram katru reizi jāsaturās iekŔā'. Un tālāk izrādās, ka dažreiz tā arÄ« notiek, izņemot situācijas, kad metode kļūst pārāk liela, un JIT sāks saturēt visu kopā. Šādu vietu veiktspēju var paredzēt nekavējoties. Parasti getterÄ« darbojas labi, bet pēc tam tu skaties uz lieliem karstiem cikliem un saproti, ka tur ir kādi funkcionālie izsaukumi, kas nav skaidrs, ko dara. Å eit ir problēma ar plaÅ”u getteru lietoÅ”anu, iemesls, kāpēc tie neiekļaujas - nav skaidrs, vai tas ir getters. Ja tev ir supermaza kodu bāze, to var vienkārÅ”i iegaumēt un pēc tam teikt: Å”is ir getters, bet Å”is ir setters. Lielā kodu bāzē katrai funkcionālijai ir sava stāsts, kas nevienam, bÅ«tÄ«bā, nav zināms. Profileris saka, ka mēs zaudējuÅ”i 24% laika kaut kādā ciklā, un lai saprastu, ko dara Å”is cikls, jāskatās uz katru funkciju iekŔā. Nevar to saprast, nepētÄ«jot funkciju, un tas nopietni palēnina izpratnes procesu. Tāpēc es neizmantoju getterus un setterus, es esmu sasniedzis jaunu lÄ«meni!
Kā iegÅ«t izmaksu modeli? Protams, var palasÄ«t kaut ko... Bet es domāju, labākais veids ir rÄ«koties. Izveidot mazu kompilatoru, un tas bÅ«s labākais veids, kā apzināt izmaksu modeli un iekļaut to savā prātā. Mazs kompilators, kas derētu mikroviļņu krāsns programmēŔanai – tas ir uzdevums iesācējiem. Nu, es domāju, ja tev jau ir programmēŔanas prasmes, tad tās pietiks. Visas tās lietas, piemēram, izparsināt rindu, kas bÅ«s kāds algebriskais izteikums, izvilkt no tās matemātisko operāciju instrukcijas pareizajā secÄ«bā, iegÅ«t pareizās vērtÄ«bas no reÄ£istriem – to visu var izdarÄ«t viegli. Un kamēr tu to darÄ«si, tas ieies prātā. Domāju, visi zina, ko dara kompilators. Un tas dos izpratni par izmaksu modeli.

Praktiski piemēri veiktspējas uzlaboÅ”anai

Andrej: Uz ko vēl jāpievērÅ” uzmanÄ«ba, strādājot pie veiktspējas?

Kliffs: Datu struktÅ«ras. Starp citu, jā, jau sen neesmu vadÄ«jis Å”os pasākumus… Rocket School. Tas bija interesanti, bet prasÄ«ja tik daudz spēku, bet man taču ir arÄ« dzÄ«ve! Labi. Tātad, vienā no lielajām un interesantajām nodarbÄ«bām, "Kur pazÅ«d jÅ«su veiktspēja", es devu studentiem piemēru: divarpus gigabaiti finteh datu tika nolasÄ«ti no CSV faila, un tālāk bija jāskaita pārdoto produktu skaits. Parasti tirgus dati. UDP paketes, kas pārvērstas teksta formātā, sākot ar 70. gadiem. Čikāgas Preču birža – visādas lietas kā eļļa, kukurÅ«za, sojas pupas un tamlÄ«dzÄ«gi. Bija jāskaita Å”ie produkti, darÄ«jumu skaits, vidējais naudas un preču apgrozÄ«jums utt. Tā ir diezgan vienkārÅ”a tirdzniecÄ«bas matemātika: atrast produkta kodu (tas ir 1-2 simboli hash tabulā), iegÅ«t summu, pievienot to vienam no darÄ«jumu kopumiem, pievienot apjomu, pievienot izmaksas un pāris citas lietas. Ä»oti vienkārÅ”a matemātika. Rotaļīgā realizācija bija ļoti vienkārÅ”a: viss atrodas failā, es lasu failu un pārvietojos pa to, sadalot atseviŔķas ierakstu Java rindās, meklēju tajās nepiecieÅ”amās lietas un sumēju saskaņā ar iepriekÅ” aprakstÄ«to matemātiku. Un tas darbojas ar kādu nelielu ātrumu.

Ar Ŕādu pieeju viss ir acÄ«mredzams, kas notiek, un paralēlie aprēķini Å”eit nepalÄ«dz, vai ne? Izrādās, piecu reizes lielāku veiktspēju var sasniegt, vienkārÅ”i izvēloties pareizās datu struktÅ«ras. Un tas pārsteidz pat pieredzējuÅ”us programmētājus! Manā konkrētajā gadÄ«jumā uzmanÄ«ba bija pievērsta tam, ka nevajadzētu veikt atmiņas rezervēŔanu karstajā ciklā. Nu, tas nav viss, bet kopumā – nevajadzētu veikt rezervācijas ā€žreizi Xā€, kad X ir pietiekami liels. Kad X ir divi ar pusi gigabaiti, nevajadzētu rezervēt neko ā€žreizi par burtuā€, vai ā€žreizi par rindiņuā€, vai ā€žreizi par laukuā€, neko tamlÄ«dzÄ«gu. TieÅ”i tajā tiek tērēts laiks. Kā tas vispār darbojas? Iedomājieties, ka veicu izsaukumu String.split() vai BufferedReader.readLine(). Readline izveido virkni no byte kopas, kas nāk pa tÄ«klu, vienu reizi katrai rindiņai, katrai no simtiem miljoniem rindiņu. Es paņemu Å”o rindu, analizēju to un izmetu. Kāpēc izmetu – nu, es to jau esmu apstrādājis, viss. Tātad katram byte, kas nolasÄ«ts no Å”iem 2.7G, tiks ierakstÄ«ti divi simboli virknes, t.i., jau 5.4G, un tie man turpmāk nav nepiecieÅ”ami, tāpēc tie tiek izmestti. Ja paskatāmies uz atmiņas caurlaidspēju, mēs slodze 2.7G, kas iet cauri atmiņai un atmiņas bus, procesoram, un tālāk divreiz vairāk tiek nosÅ«tÄ«ti uz virkni, kas atrodas atmiņā, un viss tas tiek pārstrādāts, veidojot katru jauno virkni. Bet man taču ir jāizlasa tā, dzelzs to nolasa, pat ja pēc tam viss tiks pārstrādāts. Un man tas jāieraksta, jo es izveidoju virkni, un keÅ”i ir pārpildÄ«ti – keÅ”s nevar uzņemt 2.7G. Tātad katram nolasÄ«tajam byte es nolasu vēl divus papildu byte un ierakstu divus papildu byte, un kopumā tiem ir attiecÄ«ba 4:1 – Ŕādā attiecÄ«bā mēs nevajadzÄ«gi tērējam atmiņas caurlaidspēju. Un tālāk izrādās, ka, ja es veicu String.split() – tad es to noteikti nedaru pēdējo reizi, tur iekŔā var bÅ«t vēl 6-7 lauki. Tāpēc klasiskā CSV nolasīŔanas un pēc tam virknēŔanas kods noved pie atmiņas caurlaidspējas zuduma aptuveni 14:1 attiecÄ«bā pret to, ko jums faktiski vēlētos. Ja izmetam Ŕīs rezervācijas, tad varam sasniegt piecu reizes ātrumu.

Un tas nav ļoti sarežģīti. Ja jÅ«s paskatÄ«sieties uz kodu no pareizā leņķa, viss kļūst diezgan vienkārÅ”i, tiklÄ«dz saprotat problēmas bÅ«tÄ«bu. Nav vērts vispār pārtraukt atmiņas sadali: problēma ir tajā, ka jÅ«s kaut ko sadalāt un tas tÅ«lÄ«t mirst, un pa ceļam iznÄ«cina svarÄ«gu resursu, kas Å”ajā gadÄ«jumā ir atmiņas caurlaidÄ«ba. Un viss tas beidzas ar veiktspējas kritumu. x86 parasti ir nepiecieÅ”ams aktÄ«vi izmantot procesora taktis, bet Å”eit jÅ«s iztērējāt visu atmiņu daudz agrāk. Risinājums ir samazināt sadalÄ«jumu skaitu.Ā 
Vēl viena problēmas daļa ir tā, ka, ja jÅ«s palaidÄ«sit profileru brÄ«dÄ«, kad beidzās atmiņas josla, tieÅ”i tajā brÄ«dÄ«, kad tas notiek, jÅ«s parasti gaidāt keÅ”atmiņas atgrieÅ”anos, jo tā ir pilna ar miskasti, ko jÅ«s tikko radÄ«jāt visiem Å”iem rindām. Tāpēc katra load vai store operācija kļūst lēna, jo tās izraisa keÅ”atmiņas trÅ«kumus - visa keÅ”atmiņa kļūst lēna, gaidot, kad no tās izvestÄ«s miskasti. Tāpēc profilers parādÄ«s tikai siltus nejauÅ”us trokŔņus, kas izsisti pa visu ciklu - nebÅ«s nekādas atseviŔķas karstas instrukcijas vai vietas kodā. Tikai troksnis. Un, ja jÅ«s paskatÄ«sieties uz GC cikliem, tie visi bÅ«s Young Generation un supers ātri - mikroskēnas vai milisekundes maksimums. Jo visa Ŕī atmiņa mirst nekavējoties. JÅ«s sadalāt miljardiem gigabaitu, un tas tos nogriež, un nogriež un vēlreiz nogriež. Viss tas notiek ļoti ātri. Rezultātā ir lēti GC cikli, silts troksnis visā ciklā, bet mēs vēlamies iegÅ«t 5-reizes paātrinājumu. Å ajā brÄ«dÄ« kaut kas jāsakrājas galvā un jāskaidrojās: "kāpēc tā?!". Atmiņas joslas pārsniegÅ”ana neparādās klasiskajā debuggērÄ«, ir jāpalaidÄ«s aparatÅ«ras veiktspējas skaitÄ«tāju debuggers un jāpaskatās uz to tieÅ”i. Un, ja ne tieÅ”i, to var aizdomāties no Å”iem trim simptomiem. TreÅ”ais simptoms - tas ir, kad jÅ«s skatāties, ko sadalāt, prasāt profileram, un viņŔ atbild: "JÅ«s izdarÄ«jāt miljardu rindu, bet GC strādāja par brÄ«vu". TiklÄ«dz tas notiek, jÅ«s saprotat, ka esat radÄ«jis pārāk daudz objektu un iztērējis visu atmiņas joslu. Ir veids, kā to izprast, bet tas nav acÄ«mredzams.Ā 

Datu struktÅ«ras problēma: tukÅ”a struktÅ«ra, kas slēpjas aiz visa notiekoŔā, ir pārāk liela, tā aizņem 2.7G diskā, tādēļ Ŕīs lietas kopēŔana ir ļoti nevēlama – vēlētos to ielādēt tieÅ”i no tÄ«kla bajtu bufera reÄ£istros, lai nevajadzētu piecas reizes rakstÄ«t un lasÄ«t atpakaļ un uz priekÅ”u. Diemžēl Java pēc noklusējuma nesniedz tādu bibliotēku savā JDK komplektā. Bet tas taču ir trivāli, vai ne? PatiesÄ«bā tas ir 5-10 koda rindiņas, kas tiks izmantotas, lai Ä«stenotu savu buferizēto rindiņu ielādētāju, kas atkārto virkņu klases uzvedÄ«bu, bÅ«tÄ«bā kalpojot kā apvalks ap zemāk esoÅ”o bajtu buferi. Rezultātā izrādās, ka tu strādā gandrÄ«z it kā ar virknēm, bet patiesÄ«bā tur pārvietojas rādÄ«tāji uz buferi, un nevis neapstrādāti bajti tiek kopēti, un tādējādi tiek atkārtoti izmantoti tie paÅ”i buferi atkal un atkal, un operētājsistēma labprāt uzņemas lietas, kam tā ir paredzēta, piemēram, slēptu dubulto buferizēŔanu Å”iem bajtu buferiem, un tu vairs neiznÄ«cini bezgalÄ«gu nevajadzÄ«gu datu plÅ«smu. Starp citu, jÅ«s taču saprotat, ka strādājot ar GC garantēts, ka katra atmiņas pieŔķirÅ”ana nebÅ«s redzama procesoram pēc pēdējā GC cikla? Tādēļ, tas viss nekā nevar bÅ«t keŔā, un tālāk notiek 100%-Ä«gi garantēts nepareizais trieciens. Strādājot ar rādÄ«tāju, x86 piekļūŔana reÄ£istram no atmiņas aizņem 1-2 takts, un tiklÄ«dz tas notiek, tu maksā, maksā, maksā, jo visa atmiņa ir NINE keÅ”os – un tieÅ”i tas ir atmiņas pieŔķirÅ”anas izmaksas. Patiesās izmaksas.

Citiem vārdiem sakot, datu struktÅ«ras ir tās, ko visgrÅ«tāk mainÄ«t. Un tiklÄ«dz jÅ«s saprotat, ka esat izvēlējies nepareizu datu struktÅ«ru, kas vēlāk sabojās veiktspēju, parasti jāpavada bÅ«tiska darba apjoms, bet, ja to neizdarÄ«siet, tālāk bÅ«s vēl sliktāk. Pirmkārt, ir jādomā par datu struktÅ«rām, tas ir svarÄ«gi. Galvenās izmaksas Å”eit ir saistÄ«tas ar lielām datu struktÅ«rām, kuras sāk izmantot stilā "es kopēju datu struktÅ«ru X datu struktÅ«rā Y, jo Y man labāk patÄ«k pēc formas". Bet kopēŔanas operācija (kura Ŕķiet lēta) patiesÄ«bā patērē atmiņas joslu, un tieÅ”i Å”eit slēpjas viss zaudētais izpildes laiks. Ja man ir milzÄ«ga JSON rinda un es vēlos to pārvērst par strukturētu DOM koku no POJO vai kaut kā tamlÄ«dzÄ«ga, Ŕīs rindas parsēŔanas operācija un POJO veidoÅ”ana, un pēc tam jauna piekļuve POJO vēlākies radÄ«s nevajadzÄ«gas izmaksas – tas nav lēti. Ieskaitot gadÄ«jumus, ja jÅ«s darbosies ar POJO daudz biežāk nekā ar rindu. Pirmajā brÄ«dÄ« varētu izmēģināt izsist rindu un izvilkt tikai nepiecieÅ”amo, nepārvērÅ”ot to par nekādiem POJO. Ja viss tas notiek ceļā, kurā nepiecieÅ”ama maksimāla veiktspēja, nekādus POJO – ir vajaÄ£s tieÅ”i ņemties ar rindu.

Kāpēc radÄ«t savu programmēŔanas valodu

Andrej: Jūs teicāt, ka, lai apzinātos izmaksu modeli, jāpieraksta sava mazā valoda...

Kliffs: Nevis valoda, bet kompilators. Valoda un kompilators ir dažādas lietas. Galvenā atŔķirÄ«ba – jÅ«su prātā.Ā 

Andrej: Starp citu, cik zinu, jūs eksperimentējat ar savu valodu izveidi. Kāpēc?

Kliffs: Tāpēc, ka varu! Es esmu pussezonā, tāpēc tas ir mans hobijs. Visu mūžu es esmu Ä«stenojis sveÅ”as valodas. Un esmu arÄ« daudz strādājis pie koda stila. Un vēl tāpēc, ka redzu problēmas citās valodās. Redzu, ka ir labāki veidi, kā veikt ierastas lietas. Un es tos izmantotu. Man ir apnikusi redzēt problēmas sevÄ«, Java, Python, jebkurā citā valodā. Es Å”obrÄ«d rakstu uz React Native, JavaScript un Elm kā hobiju, kas nav par pensiju, bet par aktÄ«vu darbu. Un arÄ« uz Python rakstu un, visticamāk, turpināŔu strādāt pie maŔīnmācīŔanās Java aizmugurēm. Ir daudz populāru valodu, un katrai ir interesantas iezÄ«mes. Katrs ir labs kaut kādā veidā, un var izmēģināt apvienot visas Ŕīs funkcijas. Tāpēc es nodarbojos ar interesantu lietu izpēti, valodas uzvedÄ«bu, cenÅ”oties izdomāt saprātÄ«gu semantiku. Un pagaidām man tas padodas! Å obrÄ«d es cÄ«nos ar atmiņas semantiku, jo gribu, lai tā bÅ«tu kā C un Java, un iegÅ«t spēcÄ«gu atmiņas modeli un atmiņas semantiku ielādēm un veikalā. Tajā paŔā laikā iegÅ«t automātisku tipu izslēgÅ”anu kā Haskell. LÅ«k, es cenÅ”os sajaukt Haskell-lÄ«dzÄ«gu tipu izslēgÅ”anu ar atmiņu, kas darbojas kā C un Java. Ar to es esmu nodarbojies pēdējās 2-3 mēneÅ”us, piemēram.

Andrej: Ja jūs veidojat valodu, kas ņem labākās aspekts no citām valodām, vai esat domājis par to, ka kāds varētu izdarīt pretējo: ņemt jūsu idejas un izmantot tās pie sevis?

Kliffs: TieÅ”i tā rodas jaunas valodas! Kāpēc Java ir lÄ«dzÄ«ga C? Tāpēc, ka C bija laba sintakse, ko visi saprata, un Java iedvesmojās no Ŕīs sintakses, pievienojot tam veida droŔību, masÄ«vu robežu pārbaudes, GC, un vēl viņi uzlaboja dažas lietas no C. Viņi pievienoja savas. Bet viņi tika spēcÄ«gi iedvesmoti, vai ne? Visi stāv uz milžu pleciem, kas bija pirms tevis – tieÅ”i tā notiek progress.

Andrej: Kā es saprotu, jÅ«su valoda bÅ«s droÅ”a attiecÄ«bā uz atmiņas izmantoÅ”anu. Vai jÅ«s domājāt Ä«stenot kaut ko lÄ«dzÄ«gu borrow checker no Rust? Vai jÅ«s to skatÄ«jāties, kā jums patika?

Kliffs: Nu, es rakstu C jau veselu mūžību, ar visiem Å”iem malloc un free, un manuāli pārvaldu dzÄ«ves ciklu. Zināt, 90-95% manuāli pārvaldÄ«tā cikla ir ar vienādu struktÅ«ru. Un tas ir ļoti, ļoti sāpÄ«gi to darÄ«t manuāli. Es vēlētos, lai kompilators vienkārÅ”i pateiktu, kas tur notiek un ko tu esi sasniedzis ar savām darbÄ«bām. Dažām lietām borrow checker to dara automātiski. Vēl tam ir jāspēj automātiski izvest informāciju, visam jābÅ«t saprotamam un pat nevajag mani slodzÄ«t ar to sapratni. Tam bÅ«tu jāveic vismaz lokāla aizbēgÅ”anas analÄ«ze, un tikai tad, ja tas neizdodas, bÅ«tu jāpievieno tipa anotācijas, kas apraksta dzÄ«ves ciklu – un Ŕāda shēma ir daudz sarežģītāka nekā borrow checker vai jebkurÅ” cits esoÅ”ais atmiņas pārbaudÄ«tājs. Izvēle starp ā€žviss ir labiā€ un ā€žes neko nesaprotuā€ – nē, tam jābÅ«t kaut kam labākam.Ā 
Tātad, kā cilvēks, kurÅ” ir uzrakstÄ«jis daudz C koda, uzskatu, ka automātiskās dzÄ«ves cikla pārvaldÄ«bas atbalsts ir vissvarÄ«gākā lieta. Un mani arÄ« kaitina, cik daudz atmiņas izmanto Java, un galvenā sÅ«dzÄ«ba ir par GC. Pie atmiņas pieŔķirÅ”anas Java gadÄ«jumā tev atmiņa, kas bija lokāla pēdējā GC ciklā, netiks atgriezta. Valodās ar precÄ«zāku atmiņas pārvaldÄ«bu tas nav tā. Ja izsaucat malloc, tu tÅ«lÄ«tējo saņem atmiņu, kas parasti tikko tika izmantota. Parasti tu veic kādas pagaidu darbÄ«bas ar atmiņu un tÅ«lÄ«t to atgriez atpakaļ. Un tā uzreiz atgriežas malloc baseinā, un nākamajā malloc ciklā to atkal izvelk atpakaļ. Tāpēc reālais atmiņas patēriņŔ samazinās lÄ«dz dzÄ«vā objektu kopumam konkrētā brÄ«dÄ« plus kļūmēm. Un, ja tev nav tekŔķu nepiedienÄ«gos veidos, lielākā daļa atmiņas nosēdÄ«sies keÅ”os un procesorā, un tas darbosies ātri. Bet tas prasa daudz roku darba ar atmiņas pārvaldÄ«bu, izmantojot malloc un free, kas jāsauc pareizajā secÄ«bā, pareizā vietā. Rust spēj ar to tikt galā pareizi, un daudzos gadÄ«jumos pat nodroÅ”ina augstāku veiktspēju, jo atmiņas patēriņŔ tiek samazināts tikai lÄ«dz paÅ”reizējām aprēķiniem – atŔķirÄ«bā no gaidīŔanas nākamajam GC ciklam, kas atbrÄ«vos atmiņu. Beigās mēs ieguvām ļoti interesantu veidu, kā uzlabot veiktspēju. Un diezgan jaudÄ«gu – tā teikt, es nodarbojos ar Ŕādām lietām datu apstrādē finanÅ”u tehnoloÄ£iju jomā, un tas ļāva iegÅ«t paātrinājumu apmēram piecas reizes. Tas ir diezgan nozÄ«mÄ«gs paātrinājums, it Ä«paÅ”i pasaulē, kur procesori neveicas ātrāk, un mēs joprojām gaidām uzlabojumus.

Veiktspējas inženiera karjera

Andrej: Vēlētos arÄ« pajautāt par karjeru kopumā. JÅ«s kļuvāt slavens, strādājot pie JIT HotSpot, pēc tam pārcēlāties uz Azul – un tā ir arÄ« JVM kompānija. Bet jÅ«s jau vairāk nodarbojāties ar aparatÅ«ru, nevis programmatÅ«ru. Un pēc tam pēkŔņi pārgājāt uz Big Data un Machine Learning, pēc tam uz krāpÅ”anas atklāŔanu. Kā tas notika? Tas ir ļoti atŔķirÄ«gi izstrādes jomas.

Kliffs: Esmu jau diezgan ilgi nodarbojies ar programmēŔanu un esmu pārzÄ«mējis uz ļoti dažādām aktivitātēm. Un, kad cilvēki saka: Ā«O, tu taču esi tas, kurÅ” izstrādāja JIT Java!Ā», tas vienmēr ir smieklÄ«gi. Bet pirms tam es strādāju pie PostScript klona – valodas, kuru Apple kādreiz izmantoja saviem lāzerprinteriem. Un vēl pirms tam es veicu Forth valodas ievieÅ”anu. Domāju, kopējā tēma man ir rÄ«ku izstrāde. Visa dzÄ«ve esmu veidojis rÄ«kus, ar kuriem citi cilvēki raksta savas forŔās programmas. Bet es esmu strādājis arÄ« pie operētājsistēmām, vadÄ«tājiem, kodola lÄ«meņa atkļūdoÅ”anas rÄ«kiem, operētājsistēmu izstrādes valodām, kas sākās triviāli, bet laika gaitā kļuva arvien sarežģītākas. Bet galvenā tēma tomēr ir rÄ«ku izstrāde. Liela daļa manas dzÄ«ves ir pagājusi starp Azul un Sun, un tā bija par Java. Bet, kad es sāku nodarboties ar Big Data un MaŔīnmācīŔanos, es atkal uzlikÅ”u savu svinÄ«go cepuri un teicu: Ā«Ak, tagad mums ir netriviāla problēma, un Å”eit notiek vesela sērija interesantu lietu un cilvēku, kuri kaut ko daraĀ». Tas ir lielisks ceļŔ attÄ«stÄ«bai, pa kuru ir vērts iet.

Jā, es ļoti mÄ«lu izkliedētās aprēķināŔanas. Mana pirmā darba pieredze bija studentu gados, programmējot C valodā kādā reklāmas projektā. Tas bija izkliedēts aprēķins uz Zilog Z80 mikroshēmām, kuras vāc datus analogam optiskajam teksta atpazīŔanai, ko nodroÅ”ināja Ä«sts analoÄ£ais analizators. Tas bija aizraujoÅ”s un pavisam neparasts temats. Tomēr bija problēmas, kāda daļa netika pareizi atpazÄ«ta, tāpēc bija nepiecieÅ”ams izvilkt attēlu un parādÄ«t to cilvēkam, kurÅ” jau bija lasÄ«jis acÄ«m un paziņot, kas tur ir rakstÄ«ts, un tāpēc bija darbi ar datiem, un Å”iem darbiem bija sava valoda. Bija bāka, kas visu to apstrādāja – paralēli strādājoŔās Z80 mikroshēmas ar palaistiem vt100 termināliem – pa vienam uz cilvēku, un bija paralēlo programmēŔanas modelis uz Z80. Noteikta kopÄ«ga atmiņas daļa, ko dalÄ«ja visas Z80 Å”ajā ā€žzvaigznesā€ konfigurācijā; dalÄ«jās arÄ« bāka, un puse RAM tika dalÄ«ta iekŔējā tÄ«klā, un otra puse bija privāta vai devās uz kaut ko citu. CieÅ”i sarežģīta paralēla izkliedēta sistēma ar dalÄ«tu... daļēji dalÄ«tu atmiņu. Kad tas bija... Vairs neatceros, kaut kur astoņdesmito gadu vidÅ«. Pietiekami sen.Ā 
Jā, uzskatÄ«sim, ka 30 gadi ir pietiekami ilgi. Uzdevumi, kas saistÄ«ti ar izkliedētām aprēķināŔanām, ir pastāvējuÅ”i pietiekami ilgi, cilvēki jau sen cÄ«nās ar Beowulf-klasteriem. Šādi klasteri izskatās kā… Piemēram: ir Ethernet, un tavs ātrais x86 ir pievienots Å”im Ethernet, un tagad tu vēlies iegÅ«t viltoto kopÄ«go atmiņu, jo neviens todien nevarēja nodarboties ar sadalÄ«to aprēķinu kodēŔanu, tas bija pārāk sarežģīti, un tādēļ bija viltota kopÄ«gā atmiņa ar atmiņas lapu aizsardzÄ«bu uz x86, un ja tu rakstÄ«ji Å”ajā lapā, tad mēs paziņojām citiem procesoriem, ka, ja viņi piekļūst tai paÅ”ai kopÄ«gajai atmiņai, tā jāielādē no tevis, un tādējādi radās kaut kas lÄ«dzÄ«gs keÅ”u saskaņoÅ”anas protokolam un programmatÅ«rai tam. Interesanta koncepcija. Patiesā problēma, protams, bija citur. Viss tas darbojās, bet tu ātri saskārās ar veiktspējas problēmām, jo neviens nesaprata veiktspējas modeli pietiekami labi – kādi ir piekļuves modeļi uz atmiņu, kā panākt, lai mezgli nemitÄ«gi nesazinātos savā starpā, un tā tālāk.

H2O man ir radÄ«jusi Ŕādu ideju: paÅ”i izstrādātāji ir atbildÄ«gi par to, lai noteiktu, kur ir slēpÅ”anās paralēlisms un kur tā nav. Es izdomāju tādu kodēŔanas modeli, ka ir viegli un vienkārÅ”i rakstÄ«t augstas veiktspējas kodu. Taču uzrakstÄ«t lēni strādājoÅ”u kodu ir grÅ«ti, tas izskatÄ«sies slikti. Ir jāpieliek lielas pÅ«les, lai uzrakstÄ«tu lēnu kodu; bÅ«s jāpielieto nestandarta metodes. Lēns kods ir redzams jau pirmajā mirklÄ«. Tādēļ parasti tiek rakstÄ«ts kods, kas strādā ātri, bet jums jātiek galā ar to, kas jādara kopÄ«gas atmiņas gadÄ«jumā. Tas viss ir saistÄ«ts ar lieliem masÄ«viem, un uzvedÄ«ba tajos ir lÄ«dzÄ«ga nevolatÄ«viem lieliem masÄ«viem paralēlajā Java. Iedomājieties, ka divi pavedieni raksta paralēlajā masÄ«vā, viens no tiem uzvar, bet otrs, attiecÄ«gi, zaudē, un jÅ«s nezināt, kurÅ” no viņiem ir kurÅ”. Ja tie nav volatÄ«vi, secÄ«ba var bÅ«t jebkāda – un tas tieŔām darbojas labi. Cilvēkiem ir svarÄ«gi operāciju secÄ«ba, viņi pareizi izvieto volatÄ«vos un pareizajās vietās sagaida veiktspējas problēmas, kas saistÄ«tas ar atmiņu. Savādāk viņi vienkārÅ”i rakstÄ«tu kodu kā ciklus no 1 lÄ«dz N, kur N ir daži triljoni, cerot, ka visi sarežģītie gadÄ«jumi automātiski kļūs paralēli – un tur tas nedarbojas. Bet H2O nav ne Java, ne Scala, to var uzskatÄ«t par ā€žJava mÄ«nuss mÄ«nussā€, ja gribas. Tas ir ļoti saprotams programmēŔanas stils, un tas ir lÄ«dzÄ«gs vienkārÅ”a koda rakstīŔanai C vai Java ar cikliem un masÄ«viem. Bet tajā paŔā laikā atmiņu var apstrādāt terabaitiem. Es vēl joprojām izmantoju H2O. Laiku pa laikam to izmantoju dažādos projektos – un tas joprojām ir ātrākais risinājums, kas desmitiem reižu apsteidz konkurentus. Ja jÅ«s veicat Big Data ar kolonnveida datiem, ir ļoti grÅ«ti pārspēt H2O.

Tehniskie izaicinājumi

Andrej: Kāds ir bijis vislielākais izaicinājums jūsu karjerā?

Kliffs: Vai mēs apspriežam tehnisko vai ne tehnisko jautājumu? Es teiktu, ka lielākie izaicinājumi ir tehniskie.Ā 
Runājot par tehniskajiem izaicinājumiem. Es tos vienkārÅ”i pārvarēju. Pat nezinu, kurÅ” no tiem bija vislielākais, taču bija vairāki diezgan interesanti, kas prasÄ«ja diezgan daudz laika un mentālās cīņas. Kad es devos uz Sun, biju pārliecināts, ka izveidoÅ”u ātru kompilatoru, bet daudzie seniori, pieminot to, teica, ka man nekas nesanāks. Bet es izŔķīros doties Å”o ceļu, uzrakstÄ«ju kompilatoru lÄ«dz reÄ£istru pieŔķirÅ”anas lÄ«menim, un tas bija diezgan ātrs. Tas bija tikpat ātrs kā mÅ«sdienu C1, bet tajā laikā reÄ£istru pieŔķirÅ”ana bija daudz lēnāka, un, raugoties atpakaļ, tā bija liela datu struktÅ«ras problēma. Man vajadzēja to, lai uzrakstÄ«tu grafisko reÄ£istru pieŔķirÅ”anu, un es nesapratu dilemmu starp koda izteiksmÄ«bu un ātrumu, kas pastāvēja tajā laikā un bija ļoti svarÄ«ga. IzrādÄ«jās, ka datu struktÅ«ra parasti pārsniedz keÅ”atmiņas izmēru x86 procesoros tajā laikā, un tāpēc, ja es sākotnēji pieņēmēju, ka reÄ£istru pieŔķirÅ”ana prasÄ«s 5-10 procentus no visa jÄ«tēŔanas laika, tad realitātē tas izrādÄ«jās 50 procenti.

Laiks gāja, kompilators kļuva arvien skaidrāks un produktÄ«vāks, pārstāja Ä£enerēt Å”ausminoÅ”u kodu lielākajā daļā gadÄ«jumu, un veiktspēja arvien vairāk sāka lÄ«dzināties tam, ko sniedz C kompilators. Ja, protams, tu neraksti kaut kādu muļķību, ko pat C nevar paātrināt. Ja tu raksti kodu kā C, tu arÄ« saņem rezultātu, kas ir produktÄ«vs kā C lielākajā daļā gadÄ«jumu. Un jo tālāk, jo biežāk kods izrādÄ«jās asimptotiski atbilstoÅ”s C lÄ«menim, reÄ£istru pieŔķirÅ”ana sāka izskatÄ«ties kā kaut kas pabeigts... neatkarÄ«gi no tā, vai tavs kods darbojas ātri vai lēni. Es turpināju strādāt pie reÄ£istru pieŔķirÅ”anas, lai tā nodroÅ”inātu labākas pieŔķirÅ”anas. Tā kļuva lēnāka un lēnāka, bet sniedza arvien labākus rezultātus tajās situācijās, kur vairāk nevienam neizdevās. Es varēju ienirt reÄ£istru pieŔķirÅ”anā, pavadÄ«t tur mēnesi darbā, un pēkŔņi viss kods sāka darboties par 5% ātrāk. Tas notika vairākkārt, un reÄ£istru pieŔķirÅ”ana kļuva par kaut ko lÄ«dzÄ«gu mākslas darba statusam – visi to mÄ«lēja vai ienida, un akadēmiskās pasaules pārstāvji uzdeva jautājumus par to, Ā«kāpēc viss tiek darÄ«ts tieÅ”i tā». lināra skenēŔana, un kāda ir atŔķirÄ«ba. Atbilde ir vienāda: grafu krāsoÅ”anas bāzes alokators plus ļoti rÅ«pÄ«ga saskarne ar buferkodu ir uzvaras instruments, labākā kombinācija, ko neviens nevar sakaut. Un tas ir diezgan neuztverams moments. Viss pārējais, ko dara kompilators – ir diezgan pazÄ«stamas lietas, lai gan tās arÄ« ir attÄ«stÄ«tas lÄ«dz mākslas lÄ«menim. Es vienmēr veicu lietas, kas vajadzēja pārvērst kompilatoru par mākslas darbu. Bet nekas no tā nebija nekas ārkārtējs – izņemot reÄ£istru alokatoru. Triks ir tajā, ka ir jābÅ«t rÅ«pÄ«gam nogriezt zem slodzes, un, ja tas notiek (es varu izskaidrot detalizētāk, ja tas ir interesanti), tas nozÄ«mē, ka var izvietot agresÄ«vāk, neriskējot pārsniegt snieguma grafika salauÅ”anas punktu. Tajos laikos bija daudz pilna mēroga kompilatoru, kas bija pielāgoti ar visādiem uzlabojumiem un zvaniņiem, kuru sastāvā bija reÄ£istru alokatori, bet neviens tā vairs nespēja.

Problēma ir tāda, ka, ja tu pievieno metode, kas tiek inline'ota, palielinot un palielinot inline zonas, izmantoto vērtÄ«bu skaits momentāni pārsniedz reÄ£istru skaitu, un ir jānotiek spillam. Kritiskais lÄ«menis parasti rodas, kad alokators padodas, un viens labs kandidāts uz spilla maksā vairāk nekā cits, tu spilli kaut kādas pilnÄ«gi trakas lietas. Inline'ēŔanas vērtÄ«ba ir tāda, ka tu zaudē daļu overhead, overhead uz izsaukumu un saglabāŔanu, tu vari redzēt vērtÄ«bas iekŔā un vari tās tālāk optimizēt. Inline'ēŔanas cena ir tāda, ka rodas liels dzÄ«vās vērtÄ«bu skaits, un, ja tavs reÄ£istru alokators izspiež vairāk, nekā nepiecieÅ”ams, tu momentāni zaudē. Tādēļ lielākajai daļai alokatoru ir problēma: kad inline'ēŔana pārsniedz kādu robežu, sāk spilties viss, un veiktspēju var norakstÄ«t podā. Tie, kas realizē kompilatoru, pievieno kaut kādas heuristikas: piemēram, lai apturētu inline'ēŔanu, sākot no kāda pietiekami lielā izmēra, jo alokācijas visu sabojās. Tā rodas veiktspējas grafika salauzums – tu inline'ē, inline'ē, veiktspēja pamazām pieaug – un tad bum! – tā krÄ«t uz leju strauji, jo tu esi inline'ējis pārāk daudz. Tā viss arÄ« strādāja lÄ«dz Java parādīŔanai. Java prasa daudz vairāk inline'ēŔanas, tāpēc man bija jāpadara mans alokators daudz agresÄ«vāks, lai tas izlÄ«dzinātos, nevis kristu, un, ja tu esi pārāk daudz inline'ējis – tas sāk spilties, bet tad tāpat pienāk brÄ«dis "nav vairāk spilla". Tas ir interesants novērojums un tas man ienāca prātā tieÅ”i no nekurienes, neviennozÄ«mÄ«gs, bet labi atmaksājies. Es pievērsos agresÄ«vai inline'ēŔanai un tas noveda mani tādās vietās, kur Java un C veiktspēja ir blakus. TieŔām tuvu – es varu rakstÄ«t Java kodu, kas bÅ«s ievērojami ātrāks par C kodu un tamlÄ«dzÄ«gi, bet vidēji, lielajā attēlā, tie ir aptuveni salÄ«dzināmi. Domāju, daļa no Ŕī nopelna ir reÄ£istru alokators, kas ļauj man inline'ot maksimāli bezgaumÄ«gi. Es vienkārÅ”i inline'ēju visu, ko redzu. Jautājums ir, vai alokators darbojas labi, vai iznāk saprātÄ«gs kods. Tas bija liels izaicinājums: saprast visu to un likt tam strādāt.

Nedaudz par reģistru pieŔķirŔanu un daudzprocesoru skaitļoŔanu

Vladimirs: Problēmas, piemēram, reÄ£istru alokācija, Ŕķiet kā kāda mūžīga tēma. Interesanti, vai bija tā, ka kāda ideja izskatÄ«jās solÄ«ga, bet tad praktiski izgāzās?

Kliffs: Protams! ReÄ£istru alokācija ir joma, kurā, lai atrisinātu NP-grÅ«tÄ«bu uzdevumu, tu mēģini atrast kādas heuristikas. Un tu nekad nespēsi panākt ideālu risinājumu, vai ne? Tas vienkārÅ”i ir neiespējami. Skatieties, Ahead of Time kompilācija – tā arÄ« darbojās slikti. Å eit runa ir par kādiem vidējiem gadÄ«jumiem. Par tipisku veiktspēju, tāpēc var doties un izmērÄ«t kaut ko, ko tu uzskati par labu tipisku veiktspēju – galu galā tu taču strādā pie tās uzlaboÅ”anas! ReÄ£istru alokācija ir tēma, kas pilnÄ«bā veltÄ«ta veiktspējai. TiklÄ«dz tev ir pirmais prototips, kas strādā un nokrāso, kas nepiecieÅ”ams, sākas darbs pie veiktspējas. NepiecieÅ”ams iemācÄ«ties labi mērÄ«t. Kāpēc tas ir svarÄ«gi? Ja ir skaidri dati, var skatÄ«ties uz dažādām teritorijām un redzēt: ah, tas Å”eit palÄ«dzēja, bet tur viss sabojājās! Parādās dažas labas idejas, tu pievieno jaunu heuristiku un pēkŔņi viss vidēji sāk darboties nedaudz labāk. Vai arÄ« nesāk. Man bija daudz gadÄ«jumu, kad mēs cÄ«nÄ«jāmies par piecām procentēm veiktspējas, kas atŔķīra mÅ«su izstrādi no iepriekŔējā alokatora. Un katrā reizē tas izskatās tā: kaut kur vinnēja, kaut kur zaudēja. Ja tev ir labi veiktspējas analÄ«zes rÄ«ki, var atrast zaudējuŔās idejas un saprast, kāpēc tās zaudē. VarbÅ«t ir vērts atstāt visu pa vecam, bet varbÅ«t nopietni pievērsties smalkai regulēŔanai, vai doties un salabot kaut ko citu. Tas ir vesels komplekss! Es izdarÄ«ju Å”o forÅ”o haku, bet man nepiecieÅ”ams arÄ« Å”is, un Å”is, un Å”is – un to kopējā kombinācija nodroÅ”ina kādus uzlabojumus. Bet vienas idejas var neizdoties. Tā ir NP-grÅ«tÄ«bu uzdevumu veiktspējas darba daba.

Vladimirs: Veidojas sajÅ«ta, ka tādas lietas kā krāsoÅ”ana alokatoros – ir jau atrisināta problēma. Nu, jums tā Ŕķiet atrisināta, spriežot pēc tā, ko jÅ«s stāstāt, vai tad vispār vajag…

Kliffs: Tā nav atrisināta kā tāda. Tev tas jāpārvērÅ” par ā€žatrisinātuā€. Ir smagas problēmas, un tās ir jārisina. Kad tas ir izdarÄ«ts, pienāk laiks strādāt pie veiktspējas. Uz Å”o darbu jāattiecas atbilstoÅ”i - veikt testus, vākt metriku, izskaidrot situācijas, kad, atgriežoties pie iepriekŔējās versijas, tavs vecais hack atkal sāka darboties (vai otrādi, pārstāja). Un nevajag atkāpties, kamēr kaut ko nepanāk. Kā jau teicu, ja forÅ”as idejas, kas neizdevās, bet ideju reÄ£istra alokācijas jomā ir aptuveni bezgalÄ«gi. Var, piemēram, lasÄ«t zinātniskos izdevumus. Lai gan Å”obrÄ«d Ŕī joma virzās daudz lēnāk un ir kļuvusi skaidrāka nekā savās jaunÄ«bas dienās. Tomēr Å”ajā jomā strādā vesela bezgalÄ«ba cilvēku, un visas viņu idejas ir vērts izmēģināt, visas gaida savu brÄ«di. Un tu nevari pateikt, cik labi tās ir, ja nemēģini. Cik labi tās integrējas ar visu pārējo tavā alokatorā, jo alokators veic daudzas lietas, un dažas idejas tavā konkrētajā alokatorā var nedarboties, bet citā alokatorā – bez problēmām. Galvenais veids, kā uzvarēt alokatoram, ir izvilkt lēnos elementus ārpus galvenā ceļa un piespiedu kārtā sadalÄ«t pa lēno ceļu malām. Tāpēc, ja tu gribi palaist GC, iet pa lēno ceļu, deoptimizēt, izmest izņēmumu, viss Ŕādā garā - tu zini, ka Ŕīs lietas ir salÄ«dzinoÅ”i retas. Un tās tieŔām ir reti, esmu pārbaudÄ«jis. Tu veic papildu darbu, un pateicoties tam pazÅ«d daudz ierobežojumu uz Å”iem lēnajiem ceļiem, bet tas nav ļoti svarÄ«gi, jo tie ir lēni un pa tiem reti pārvietojas. Piemēram, nulles rādÄ«tājs - tas nekad nenotiek, vai ne? Ir jābÅ«t vairākām ceļa variantēm dažādām lietām, bet tām nevajadzētu sajaukties galvenajā.Ā 

Vladimirs: Ko tu domā par daudzkodolu, kad kodolu ir tūkstoŔiem? Vai tā ir noderīga lieta?

Kliffs: GPU panākumi parāda, ka tā ir diezgan noderīga!

Vladimirs: Tie ir diezgan specializēti. Bet ko par vispārÄ«gās lietoÅ”anas procesoriem?

Kliffs: Nu, tā bija Azul biznesa modelis. Atbilde nāca vēl tajā ēras laikā, kad cilvēki ļoti mÄ«lēja prognozējamu veiktspēju. Tajā laikā bija grÅ«ti rakstÄ«t paralēlo kodu. H2O kodēŔanas modelis labi pielāgojas, bet tas nav vispārējas lietoÅ”anas modelis. DrÄ«zāk tas ir nedaudz vispārÄ«gāks nekā, izmantojot GPU. Mēs runājam par Ŕāda veida izstrādes grÅ«tÄ«bām vai par tās izmantoÅ”anas sarežģītÄ«bu? Piemēram, interesants mācÄ«bu stunda, ko man deva Azul, diezgan neskaidrs: mazi keÅ”i - tas ir normāli.Ā 

Lielākais izaicinājums dzīvē

Vladimirs: Kā ir ar netehniskiem izaicinājumiem?

Kliffs: Lielākais izaicinājums bija nenotikties… laipni un labi pret cilvēkiem. Un kā sekas, es pastāvÄ«gi nonācu ļoti konfliktos situācijās. Tajās, kurās es zināju, ka viss norit nepareizi, bet nezināju, kā virzÄ«ties uz priekÅ”u Å”o problēmu risināŔanā un nespēju ar tām tikt galā. Daudzas ilgas problēmas, kas ilga desmitiem gadu, radās tieÅ”i Ŕādā veidā. Tas, ka Java ir kompilatori C1 un C2 – ir tieÅ”a Ŕī sekas. Tas, ka Java desmit gadu garumā nebija daudzlÄ«meņu kompilācijas – arÄ« ir tieÅ”a sekas. AcÄ«mredzami, ka mums Ŕāda sistēma bija nepiecieÅ”ama, bet nav skaidrs, kāpēc tās nebija. Man bija problēmas ar vienu inženieri… vai inženieru grupu. Pirms daudziem gadiem, kad es sāku strādāt Sun, es biju… Labi, ne tikai toreiz, man vispār vienmēr ir savs viedoklis. Un es uzskatÄ«ju, ka var vienkārÅ”i paņemt Å”o savu patiesÄ«bu un izteikt to tieÅ”i. Turklāt, es biju Å”okējoÅ”i pareizs lielāko daļu laika. Un, ja tev Ŕāds pieeja nepatÄ«k… jo Ä«paÅ”i, ja tu acÄ«mredzami kļūdies un dari muļķības… Vispār, nedaudzi cilvēki varēja paciest Ŕādu komunikācijas formu. Tomēr daži varēja, piemēram, es. Es visu savu dzÄ«vi esmu bÅ«vējis uz meritokrātiskiem principiem. Ja tu man parādÄ«si kaut ko nepareizu, es tÅ«lÄ«t pagriezīŔos un teikÅ”u: tu teici muļķības. To darot, protams, atvainojos un visu tādu, atzīŔu nopelnu, ja tādi vispār pastāv, un veicu citas pareizas darbÄ«bas. No otras puses, es esmu Å”okējoÅ”i pareizs Å”okējoÅ”i lielā procentā kopējā laika. Un tas nevis ir ļoti labi strādā attiecÄ«bās ar cilvēkiem. Es nemaz nemēģinu bÅ«t laipns, bet nostādÄ«ju jautājumu uz paplātes. ā€žÅ is nekad nestrādās, jo viens, divi un trÄ«s.ā€ Un viņi tādi: ā€žAk!ā€ Bija arÄ« citi sekas, kuras, iespējams, labāk bÅ«tu izlaižu: piemēram, tādi, kas noveda pie ŔķirÅ”anās ar sievu un desmit gadu depresijas pēc tam.

Izaicinājums ir cīņa ar cilvēkiem, ar viņu uztveri par to, ko tu vari vai nevari darÄ«t, kas ir svarÄ«gi un kas nav. Ir bijuÅ”i daudzi izaicinājumi par kodēŔanas stilu. Es joprojām rakstu daudz kodu, un tajos laikos man pat nācās palēnināt tempu, jo es veicu pārāk daudz paralēlu uzdevumu un neizdarÄ«ju tos labi, nevis koncentrējos uz vienu. Atgriežoties pagātnē, es uzrakstÄ«ju pusi no Java JIT komandas koda, C2 komandas. Nākamais ātrākais kodētājs rakstÄ«ja uz pusi lēnāk, nākamais – vēl uz pusi lēnāk, un tas bija eksponenciāls kritums. SeptÄ«tais cilvēks Å”ajā rindā bija ļoti, ļoti lēns – tā vienmēr ir! Es strādāju ar daudz kodu. Es skatÄ«jos, kurÅ” ko raksta, bez izņēmumiem, es skatÄ«jos viņu kodu, veicu pārskatus par katru no viņiem un joprojām turpināju rakstÄ«t vairāk nekā kāds no viņiem. Ar cilvēkiem Ŕāda pieeja ne vienmēr darbojās labi. Daži to nepatika. Un kad viņi ar to nespēja tikt galā, sākās visāda veida sÅ«dzÄ«bas. Piemēram, reiz man tika teikts pārtraukt rakstÄ«t kodu, jo es rakstu pārāk daudz kodu, un tas apdraud komandu, un viss tas man izklausÄ«jās kā joks: čalis, ja visa pārējā komanda izzudÄ«s un es turpināŔu rakstÄ«t kodu, tu zaudēsi tikai pusi komandas. No otras puses, ja es turpināŔu rakstÄ«t kodu un tu zaudēsi pusi komandas – tas izklausās pēc ļoti sliktas pārvaldÄ«bas. Es nekad par to daudz nedomāju, nekad par to nerunāju, bet tas tomēr bija kaut kur manā prātā. Apziņā riņķoja doma: 'JÅ«s taču jokojat?'. LÄ«dz ar to, vislielākā problēma biju es un manas attiecÄ«bas ar cilvēkiem. Tagad es saprotu sevi daudz labāk, es ilgi biju komandas vadÄ«tājs programmētājiem, un tagad es taisni saku cilvēkiem: zini, es esmu tāds, kāds es esmu, un jums bÅ«s jāarÄ« ar mani – vai ir labi, ja es Å”eit pastāvu? Un kad viņi ar to sāka tikt galā, viss sāka darboties. Es nevienam neesmu ne slikts, ne labs, man nav nekādu sliktu nodomu vai egoistisku tieksmju, tas vienkārÅ”i ir mans bÅ«tÄ«ba, un ar to kaut kā jādzÄ«vo.

AndrejPavisam nesen visi sāka runāt par paÅ”apziņu introvertiem un vispār par mÄ«kstajām prasmēm. Ko par to var teikt?

Kliffs: Jā, tas bija izpratne un mācÄ«ba, ko es guvu no ŔķirÅ”anās ar sievu. Ko es guvu no ŔķirÅ”anās – tā ir izpratne par sevi. Tā es sāku saprast citus cilvēkus. Saprast, kā darbojas Ŕī mijiedarbÄ«ba. Tas izsauca atklājumus viens pēc otra. ParādÄ«jās apziņa, kas es esmu, un kas esmu. Ko es daru: vai es esmu noraizējies par uzdevumu, vai izvairos no konflikta, vai kaut kas cits – un Ŕāds paÅ”apziņas lÄ«menis tieŔām palÄ«dz turēt sevi rokās. Pēc tam viss iet daudz vieglāk. Viens punkts, ko es esmu atklājis ne tikai sev, bet arÄ« citiem programmētājiem – nespēja verbilizēt domas, kad esi emocionālā stresa stāvoklÄ«. Piemēram, tu sēdi un kodē, esi plÅ«smā, un tad pie tevis pienāk un sāk kliegt histērijā, ka kaut kas ir salÅ«zis, un tagad tiks pielietoti ekstrēmi pasākumi. Un tu nespēj neko pateikt, jo esi emocionālā stresa stāvoklÄ«. IegÅ«tā zināŔanās ļauj sagatavoties Å”im mirklim, izdzÄ«vot to un pāriet uz atkāpÅ”anās plānu, pēc kura var darÄ«t kaut ko. Tātad, jā, kad tu sāk saprast, kā viss tas darbojas – tas ir milzÄ«gs notikums, kas maina dzÄ«vi.Ā 
Es pats neizdevās atrast pareizos vārdus, bet atcerējos darbÄ«bu secÄ«bu. Jēga ir tāda, ka Ŕī reakcija ir tikpat fiziska, cik verbāla, un tev ir nepiecieÅ”ama telpa. Tādu telpu, zenā. TieÅ”i to ir nepiecieÅ”ams izskaidrot, un tad uzreiz atkāpties – tÄ«ri fiziski atkāpties. Kad es klusēju ar vārdiem, es varu apstrādāt situāciju emocionālajā daļā. Pēc tam, kad adrenalÄ«ns nonāk smadzenēs, tu pārej uz

Un tādēļ ir nepiecieÅ”ama verbālā telpa. VienkārÅ”i brÄ«va telpa. Ja vispār kaut ko teikt, tad var tieÅ”i to apgalvot, un pēc tam doties un patieŔām atrast sev "telpu": doties pastaigā pa parku, ieslēgties duŔā – nav nozÄ«mes. Galvenais ir uz laiku atslēgties no tās situācijas. Tikko tu pat uz dažām sekundēm atslēdzies, atgriežas kontrole, tu sāk domāt skaidri. "Labi, es neesmu idiots, es nedaru muļķīgas lietas, es esmu diezgan noderÄ«gs cilvēks." TiklÄ«dz tu spēj pārliecināt pats sevi, ir laiks pāriet uz nākamo posmu: saprast, kas noticis. Tevi uzbruka, uzbrukums nāca no negaidÄ«tas vietas, tā bija negodÄ«ga izsistÄ«ba. Tas ir slikti. Nākamais solis – saprast, kāpēc uzbrucējam tas bija nepiecieÅ”ams. PatiesÄ«bā, kāpēc? VarbÅ«t tāpēc, ka viņŔ pats ir dusmÄ«gs? Kāpēc viņŔ ir dusmÄ«gs? Piemēram, tāpēc, ka viņŔ pats ir kļūdÄ«jies un nevar uzņemties atbildÄ«bu? Šādā veidā ir jāpievērÅ”as visai situācijai. Bet tam ir nepiecieÅ”ama manevrēŔanas telpa, verbālā telpa. Pirmais solis – pārtraukt verbālo kontaktu. Aiziet no vārdu apsprieÅ”anas. Atcelt to, aiziet prom, cik ātri vien iespējams. Ja tas ir telefona saruna – vienkārÅ”i noliec klausuli – Ŕī ir prasme, kuru es guvu no sarunām ar bijuÅ”o sievu. Ja saruna nenoved pie nekas laba, vienkārÅ”i saki "uz redzēŔanos" un noliec klausuli. No otras puses: "bla-bla-bla", tu atbildi: "aha, uz redzēŔanos!" un noliec klausuli. VienkārÅ”i pārtrauc sarunu. Pēc piecām minÅ«tēm, kad tev atgriežas spēja skaidri domāt, tu esi nedaudz atdzisis, kļūst iespējams visu pārdomāt, kas vispār notika un kas notiks tālāk. Un sākt formulēt pārdomātu atbildi, nevis vienkārÅ”i reaģēt uz emocijām. Manā paÅ”a apziņas attÄ«stÄ«bā tieÅ”i tas, ka emocionālā stresa gadÄ«jumā es nevaru runāt, ir bijis atklājums. Iziet no Ŕīs stāvokļa, apdomāt un plānot, kā atbildēt un kompensēt problēmas – Å”ie ir pareizi soļi gadÄ«jumā, ja nevar runāt. VienkārŔākais veids – aizbēgt no situācijas, kurā izpaužas emocionālais stress un vienkārÅ”i pārtraukt piedalÄ«ties Å”ajā stresā. Pēc tam tu atgÅ«sti spēju domāt, kad tu vari domāt, parādās iespēja runāt, un tā tālāk.

Starp citu, pretējās puses advokāts tiesā mēģina darÄ«t to ar tevi – tagad jau ir skaidrs, kāpēc. Jo viņam ir iespēja tevi apspiest lÄ«dz tādai pakāpei, ka tu pat nevari izrunāt savu vārdu, piemēram. TieŔā nozÄ«mē, tu nespēji runāt. Ja tas notiek ar tevi un ja tu zini, ka nonāksi vietā, kur plosās verbālas cīņas, piemēram, tiesā, tu vari atnākt ar savu juristu. Jurists aizstāvēs tevi un pārtrauks verbālo uzbrukumu, un to izdarÄ«s pavisam likumÄ«gi, un tev atgriezÄ«sies zaudētais zen telpas sajÅ«ta. Piemēram, man dažreiz bija nepiecieÅ”ams piezvanÄ«t Ä£imenei, tiesnesis pret to attiecās diezgan draudzÄ«gi, bet pretējās puses advokāts kliedza un kliedza uz mani, es pat vārdu nevarēju ievietot. Šādās situācijās man vislabāk strādā starpnieka izmantoÅ”ana. Starpnieks pārtrauc visu Å”o spiedienu, kas tev nepārtraukti plÅ«st virsÅ«, tu atrod nepiecieÅ”amo zen telpas sajÅ«tu, kopā ar to atgriežas spēja runāt. Tas ir vesela zināŔanu joma, kurā jāapgÅ«st daudz, daudz jāatklāj sevÄ«, un viss tas pārvērÅ”as augsta lÄ«meņa stratēģiskos risinājumos, kas ir dažādi atkarÄ«bā no cilvēka. Dažiem cilvēkiem nav augstāk minēto problēmu, parasti tās nav cilvēkiem, kas profesionāli nodarbojas ar pārdoÅ”anu. Visi Å”ie cilvēki, kas pelna iztiku ar vārdiem – pazÄ«stami dziedātāji, dzejnieki, reliÄ£iskie darbinieki un politiÄ·i, viņiem vienmēr ir ko teikt. Viņiem nav Ŕādu problēmu, bet man tās ir.

Andrej: Tas bija… negaidÄ«ti. Lieliski, mēs jau esam diezgan daudz runājuÅ”i un ir laiks noslēgt Å”o interviju. Mēs noteikti tiksimies konferencē un varēsim turpināt Å”o dialogu. Tiksimies Hydra!

Turpināt sarunu ar Klifu varēs konferencē Hydra 2019, kas notiks 11.-12. jÅ«lijā 2019. gadā Sanktpēterburgā. ViņŔ ieradÄ«sies ar ziņojumu Ā«The Azul Hardware Transactional Memory experienceĀ». Biļetes var iegādāties . Tas ļauj izveidot savu personÄ«go pasauli, padarot to pieejamu savienojumam no jebkuras pasaules malas. Tiem, kas to dara pirmo reizi, ir lielisks.

Avots: habr.com

Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem šŸ”„ Iegādājieties uzticamu tÄ«mekļa hostingu ar DDoS aizsardzÄ«bu, VPS VDS serveriem | ProHoster