Cliff Click — CTO of Cratus (IoT sensors for process improvement), founder and co-founder of several startups (including Rocket Realtime School, Neurensic, and H2O.ai) with multiple successful exits. Cliff wrote his first compiler at the age of 15 (Pascal for TRS Z-80)! He is best known for his work on C2 in Java (the Sea of Nodes IR). This compiler showed the world that JIT can produce high-quality code, which became one of the factors in Java's emergence as one of the main contemporary programming platforms. Later, Cliff helped Azul Systems build an 864-core mainframe with software written entirely in Java that supported GC pauses on a 500-gigabyte heap within 10 milliseconds. Generally, Cliff has managed to work on all aspects of JVM.
This Hub post is a large interview with Cliff. We'll discuss the following topics:
- Transitioning to low-level optimizations
- How to perform a large refactoring
- Cost model
- Training in low-level optimizations
- Practical examples of performance improvements
- Why create your own programming language
- Career of a performance engineer
- Technical challenges
- A bit about register allocation and multithreading
- The biggest challenge of your life
The interview is conducted by:
- Andrey Satarin from Amazon Web Services. In his career, he has managed to work on completely different projects: testing a distributed NewSQL database at Yandex, a cloud detection system at Kaspersky Lab, a multiplayer game at Mail.ru, and a currency pricing service at Deutsche Bank. He is interested in testing large-scale backend and distributed systems.
- Vladimir Sitnikov from Netcracker. He has been working for ten years on the performance and scalability of NetCracker OS — software used by telecom operators for automating network management and equipment processes. He is passionate about Java performance and Oracle Database. He is the author of more than a dozen performance enhancements in the official PostgreSQL JDBC driver.
Transitioning to low-level optimizations
Andrey: You are a well-known figure in the world of JIT compilation, Java, and performance optimization in general, aren't you?
Cliff: That's right!
Andrey: Let's start with some general questions about performance work. What do you think about the choice between high-level and low-level optimizations like working at the CPU level?
Cliff: Áno, tu je všetko jednoduché. Najrýchlejší kód je ten, ktorý sa nikdy nespúšťa. Preto je dôležité začať na vyššej úrovni a pracovať na algoritmoch. Lepšia O-označenie prekoná horšie O-označenie, pokiaľ do toho nevstúpia nejaké dostatočne veľké konštanty. Nízkoúrovňové záležitosti prichádzajú na rad ako posledné. Zvyčajne, ak ste optimalizovali zvyšok stacku dostatočne dobre a stále zostáva niečo zaujímavé, to je nízka úroveň. Ale ako začať na vyššej úrovni? Ako zistiť, že ste na vyššej úrovni urobili dostatok práce? Nuž... nijako. Neexistujú žiadne hotové recepty. Musíte sa podrobne zaoberať problémom, rozhodnúť, čo plánujete urobiť (aby ste sa nestratili v zbytočných krokoch) a až potom môžete siahnuť po profileri, ktorý môže povedať niečo užitočné. V určitom momente si uvedomíte, že ste sa zbavili zbytočností a nastal čas zamerať sa na detailné ladienie na nízkej úrovni. To je určite špeciálny druh umenia. Mnoho ľudí robí zbytočné veci, ale pohybuje sa tak rýchlo, že na výkon nemajú čas. Ale to trvá, kým sa otázka nevytvorí ako urgentná. Zvyčajne 99% času nikoho nezaujíma, čím sa zaoberám, až do okamihu, keď sa na kritickej ceste objaví niečo dôležité, na čom niekomu záleží. A v tej chvíli vás všetci začnú tlačiť s otázkou „prečo to od začiatku nefungovalo dokonale“. Celkovo, vždy je čo zlepšiť na výkone. Ale 99% času nemáte body, na ktorých by ste mohli pracovať! Snažíte sa len niečo rozbehnúť a počas toho si uvedomujete, čo je dôležité. Nikdy nemožno vedieť dopredu, že tento kúsok treba robiť dokonale, preto v podstate musíte byť dokonalí vo všetkom. A to je nemožné a takto to nerobíte. Vždy je množstvo vecí, ktoré treba opraviť – a to je úplne normálne.
How to perform a large refactoring
Andrey: Ako pracujete na výkone? Je to predsa problém, ktorý zasahuje do všetkého. Napríklad, museli ste niekedy pracovať na problémoch, ktoré vznikli v dôsledku prekrytia veľkého množstva existujúcej funkcionality?
Cliff: Snažím sa tomu vyhnúť. Ak viem, že výkon bude problém, premýšľam o tom ešte predtým, ako začnem kódovať, najmä nad dátovými štruktúrami. Ale často všetko objavíte až neskôr. A potom musíte siahnuť na extrémne opatrenia a robiť to, čo nazývam „prepisuj a vládni“: musíte sa chopit dostatočne veľkého kusu. Niektoré časti kódu budete musieť prepisovať kvôli výkonnostným problémom alebo niečomu inému. Akýkoľvek dôvod na prepisovanie kódu sa vyskytne, takmer vždy je lepšie prepisovať väčší kus, než menší kus. V tomto okamihu všetci začnú triasť od strachu: „och nie, nemôžeme sa dotknúť toľko kódu!“. Ale v skutočnosti taký prístup takmer vždy funguje oveľa lepšie. Musíte sa hneď vrhnúť na veľký problém, obkresliť okolo neho veľký kruh a povedať: všetko, čo je v kruhu, prepisujem. Hranica je predsa oveľa menšia ako ten obsah, ktorý je vnútri, ktorý má byť nahradený. A ak takéto obkreslenie hraníc umožní dokončiť prácu vo vnútri dokonale – máte voľné ruky, robte si, čo chcete. Akonáhle rozumiete problému, proces prepisovania ide oveľa hladšie, preto si ukousnite veľký kus!
Zároveň, keď robíte prepis veľkým kusom a uvedomujete si, že výkon bude problém, môžete sa oň začať obávať hneď. Zvyčajne sa to prejaví v jednoduchých veciach ako „nekopírujte dáta, spravujte dáta čo najjednoduchšie, robte ich menšie“. Pri veľkých prepisovaniach existujú štandardné spôsoby zlepšenia výkonu. A takmer vždy sa to točí okolo dát.
Cost model
Andrey: V jednom z podcastov ste hovorili o modeloch nákladov v kontexte výkonu. Môžete vysvetliť, čo ste tým mali na mysli?
Cliff: Samozrejme. Narodil som sa v ére, keď bola výkon procesora mimoriadne dôležitý. A táto éra sa opäť vracia - osud nie je bez irónie. Žil som v časoch osembitových strojov, môj prvý počítač pracoval s 256 bajtmi. Presne s bajtmi. Všetko bolo veľmi malé. Musel som počítať inštrukcie a keď sme začali postupovať vyššie v zásobníku programovacích jazykov, jazyky preberali na seba čoraz viac a viac. Bolo tu Assembler, potom Basic, potom C, a C preberal prácu s množstvom detailov, ako je rozdeľovanie registrov a výber inštrukcií. Ale tam to bolo celkom jasné a ak som urobil ukazovateľ na inštanciu premennej, dostal som load a táto inštrukcia mala známy náklad. Hardvér poskytuje známe množstvo strojových cyklov, takže rýchlosť vykonávania rôznych vecí sa dá jednoducho vypočítať sčítaním všetkých inštrukcií, ktoré plánujete spustiť. Každé compare/test/branch/call/load/store sa dalo sčítať a povedať: tu máš a to je doba vykonávania. Pri zlepšovaní výkonu si určite všimneš, aké čísla sa vzťahujú na malé horúce cykly.
Ale len čo prejdeš na Java, Python a podobné veci, veľmi rýchlo sa vzďaľuješ od nízkoúrovňového hardvéru. Akú cenu má volanie gettera v Java? Ak JIT v HotSpot všetko správne , bude to load, ale ak to neurobil – to bude volanie funkcie. Pretože volanie leží na horúcom cykle, zruší všetky ostatné optimalizácie v tomto cykle. Preto skutočné náklady budú oveľa vyššie. A hneď strácaš schopnosť pozerať sa na kus kódu a rozumieť tomu, aké náklady na vykonanie z pohľadu frekvencie procesora, používaní pamäte a cache. Všetko toto sa stáva zaujímavé len vtedy, ak sa skutočne ponoríš do výkonu.
Práve sme sa ocitli v situácii, keď rýchlosti procesorov už viac než desaťročie takmer nerastú. Staré časy sa vracajú! Už nemôžete počítať s dobrou jednoprocesorovou výkonnosťou. Ale ak sa náhodou pustíte do paralelných výpočtov – je to šialene zložité, všetci sa na vás pozerajú ako na Jamesa Bonda. Desiatky násobných urýchlení sa obvykle objavujú tam, kde niekto niečo prehliadol. Paralelnosť si vyžaduje veľa práce. Aby ste dosiahli ono desiatkové urýchlenie, musíte pochopiť model nákladov. Čo a koľko stojí. A na to treba pochopiť, ako jazyk zapadá do podložného hardvéru.
Martin Thompson našiel skvelé slovo pre svoj blog ! Je potrebné pochopiť, čo má hardvér robiť, ako to bude robiť a prečo to vôbec robí. Využitím tohto je dosť jednoduché začať počítať inštrukcie a zistiť, kde uniká čas vykonania. Ak však nemáte príslušné školenie, jednoducho hľadáte čiernu mačku v tmavej miestnosti. Neustále vidím ľudí, ktorí optimalizujú výkonnosť, a ktorí nemajú najmenšiu predstavu o tom, čo vlastne robia. Títo sa veľmi trápi a veľmi sa nikam neposúvajú. A keď vezmem ten istý kód, pridám pár drobných trikov a dosiahnem päťnásobné alebo desiatkové urýchlenie, hovoria: takto je to nefér, vedeli sme, že si lepší. Úžasné. O čom to hovorím… model nákladov – to je o tom, aký kód píšeš a ako rýchlo v priemere funguje v celkovom obraze.
Andrey: A ako si udržovať taký objem v hlave? To sa dosahuje veľkým množstvom skúseností, alebo? Kde sa tieto skúsenosti získavajú?
Cliff: No, svoju skúsenosť som získal nie najjednoduchšou cestou. Programoval som v Assembleri ešte v časoch, keď sa dalo zorientovať v každej jednotlivé inštrukcii. Znie to hlúpo, ale odvtedy mám v hlave, v pamäti, navždy zostavený súbor inštrukcií Z80. Nepamätám si mená ľudí už minútu po rozhovore, ale pamätám si kód napísaný pred 40 rokmi. Zábavné, vyzerá to ako syndróm „».
Training in low-level optimizations
Andrey: Existuje nejaký jednoduchší spôsob, ako sa do toho zapojiť?
Cliff: Áno aj nie. Hardvér, ktorý všetci používame, sa za tú dobu veľmi nezmenil. Všetci používajú x86, s výnimkou smartfónov na Arm. Ak sa nezaoberáš nejakým hardcore embeddingom, máš všetko rovnaké. Dobre, pokračujme. Inštrukcie sa tiež po stáročia nemenili. Je potrebné ísť a napísať niečo v assembleri. Trochu, ale dosť na to, aby si začal rozumieť. Usmievate sa, ale ja hovorím úplne vážne. Musíte pochopiť zhodu medzi jazykom a hardvérom. Potom by si mal ísť, niečo napísať a urobiť malý hračkársky kompilátor pre malý hračkársky jazyk. „Hračkársky“ znamená, že by sa mal dať urobiť v rozumnom čase. Môže byť super jednoduchý, ale musí generovať inštrukcie. Akt generovania inštrukcií umožní pochopiť model nákladov pre most medzi vysokými jazykmi, na ktorých všetci píšu, a strojovým kódom, ktorý sa vykonáva na hardvéri. Táto zhoda sa vryje do mozgu v momente písania kompilátora. Aj toho najjednoduchšieho kompilátora. Potom sa môžeš začať pozerať na Java a na to, že má semantickú priepasť oveľa hlbšiu, a budovať na nej mosty omnoho zložitejšie. V Jave je oveľa ťažšie pochopiť, či bol náš most dobrý alebo zlý, čo ho spôsobuje, aby sa rozpadol a čo nie. Ale potrebuješ nejaký východiskový bod, keď sa pozeráš na kód a rozumieš: „aha, tento getter by sa mal inlinovať zakaždým“. A potom zistíš, že sa to občas deje, s výnimkou situácie, keď sa metóda stane príliš veľkou a JIT začína inlinovať všetko možné. Výkon týchto miest môžeš predpovedať okamžite. Zvyčajne gettery fungujú dobre, ale potom sa pozrieš na veľké horúce slučky a zistíš, že tam plávajú nejaké volania funkcií, ktorých účel nie je jasný. A v tom spočíva problém s masovým používaním getterov, dôvod, prečo sa neinlinujú – nie je jasné, či ide o getter. Ak máš super malú kódovú základňu, dá sa to jednoducho zapamätať a potom povedať: toto je getter, a toto je setter. V veľkej kódovej základni každá funkcia žije svoj vlastný príbeh, ktorý nikomu nie je známy. Profilér hovorí, že sme stratili 24% času na nejakej slučke a aby sme pochopili, čo táto slučka robí, musíme sa pozrieť na každú funkciu vo vnútri. Nie je možné to pochopiť bez štúdia funkcie, a to vážne spomaľuje proces porozumenia. Preto nepoužívam gettery a settery, prešiel som na novú úroveň!
Kde získať model nákladov? No, je možné čítať rôzne materiály, ale myslím, že najlepší spôsob je konať. Urobiť si malý kompilátor a to bude najlepší spôsob, ako si osvojiť model nákladov a usadiť ho vo svojej hlave. Malý kompilátor, ktorý by mohol fungovať na programovanie mikrovlnky – to je úloha pre začiatočníka. Myslím, že ak už máš programátorské zručnosti, malo by to postačiť. Všetky tie veci, ako napríklad parsovanie reťazca, ktorý bude nejakým algebraickým výrazom, vytiahnuť odtiaľ inštrukcie matematických operácií v správnom poradí, zobrať správne hodnoty z registrov – to všetko sa robí na jedno zamávanie. A pokiaľ to robíš, to sa ti zapíše do hlavy. Myslím, že všetci vedia, čím sa zaoberá kompilátor. A toto ti dá pochopenie modelu nákladov.
Practical examples of performance improvements
Andrey: Na čo ďalšie si treba dávať pozor pri práci na výkonnosti?
Cliff: Dátové štruktúry. Mimochodom, už dávno som nemal tieto lekcie... . Bolo to zábavné, ale vyžadovalo to toľko energie, a predsa mám aj svoj život! Dobre. Takže, na jednej z veľkých a zaujímavých lekcií, „Kam odchádza váš výkon“, som študentom dal príklad: dva a pol gigabajtu fintech dát sa čítalo z CSV súboru a potom bolo potrebné spočítať počet predávaných produktov. Bežné tikové trhové dáta. UDP pakety premenené na textový formát, začínajúce od 70. rokov. Chicago Mercantile Exchange – rôzne štandardy ako ropa, kukurica, sójové bôby a podobne. Bolo potrebné spočítať tieto produkty, počet obchodov, priemerný objem pohybu peňazí a tovaru, atď. Je to celkom jednoduchá obchodná matematika: nájsť kód produktu (to sú 1-2 symboly v hash tabuľke), získať súčet, pridať ho do jedného z obchodných setov, pridať objem, pridať hodnotu a pár ďalších vecí. Veľmi jednoduchá matematika. Hračkárska implementácia bola veľmi priamočiara: všetko ležalo v súbore, čítal som súbor a posúval sa po ňom, deliac jednotlivé záznamy na Java riadky, hľadal som v nich potrebné veci a sčítaval podľa vyššie opísanej matematiky. A to funguje s nejakou malou rýchlosťou.
S týmto prístupom je všetko jasné, čo sa deje, a paralelné výpočty tu nepomôžu, že? Ukazuje sa, že päťnásobné zvýšenie výkonu sa dá dosiahnuť len výberom správnych dátových štruktúr. A to prekvapuje aj skúsených programátorov! V mojom konkrétnom prípade bolo dôležité, že netreba vykonávať alokáciu pamäte v horúcej slučke. No, to nie je celá pravda, ale v zásade – netreba alokovať „raz za X“, keď je X dostatočne veľké. Keď X predstavuje dve a pol gigabajtu, netreba alokovať nič „raz za písmeno“, alebo „raz za riadok“, alebo „raz za pole“, nič takého. Presne na to ide čas. Ako to vlastne funguje? Predstavte si, že vykonávam volanie String.split() alebo BufferedReader.readLine(). Readline robí reťazec zo súboru bajtov, ktoré prišli cez sieť, raz pre každý riadok, pre každý z hundreds of millions riadkov. Beriem tento riadok, analyzujem ho a vyhadzujem. Prečo vyhadzujem – no, už som ho spracoval, hotovo. Takže, pre každý bajt, ktorý sa prečítal z týchto 2.7G, sa zapíše dva symboly do reťazca, teda už 5.4G, a na nič iné ich nepotrebujem, preto sa vyhadzujú. Ak sa pozrieme na priepustnosť pamäte, načítavame 2.7G, ktoré prechádzajú pamäťou a pamäťovou zbernicou do procesora, a potom sa dvakrát viac posiela do reťazca v pamäti, a všetko sa to spracováva pri vytváraní každého nového reťazca. Ale musím ho prečítať, železo ho číta, aj keď sa všetko neskôr spracuje. A musím ho zapísať, pretože som vytvoril reťazec a cache sa preplnili – cache nemôže pojať 2.7G. Celkovo, pre každý prečítaný bajt prečítam ďalšie dva dodatočné bajty a zapíšem dva dodatočné bajty, a v konečnom dôsledku máme pomer 4:1 – v takom pomere bezradne míňame priepustnosť pamäte. A potom sa ukazuje, že ak urobím String.split() – robím to ďaleko nie naposledy, tam môže byť ešte 6-7 polí. Preto klasický kód na čítanie CSV s následným parsovaním riadkov vedie k stratám priepustnosti pamäte v oblasti 14:1 v porovnaní s tým, čo by ste naozaj chceli mať. Ak sa zbavíte týchto alokácií, môžete dosiahnuť päťnásobné zrýchlenie.
A to nie je niečo, čo by bolo veľmi ťažké. Ak sa pozriete na kód z správneho uhla, všetko sa stáva celkom jednoduchým hneď, ako si uvedomíte podstatu problému. Naozaj by ste nemali prestávať alokovať pamäť: problém je len v tom, že niečo alokujete a hneď to zomrie, a pritom to spáli dôležitý zdroj, ktorým v tomto prípade je šírka pásma pamäte. A všetko toto vedie k poklesu výkonu. Na x86 zvyčajne potrebujete aktívne spaľovať cykly procesora, ale tu ste spálili celú pamäť oveľa skôr. Riešením je znižovať počet alokácií.
Iná časť problému spočíva v tom, že ak spustíte profiler, keď sa vyčerpá pamäť, presne v momente, keď sa to stane, zvyčajne očakávate návrat cache, pretože je plná odpadu, ktorý ste práve vygenerovali s týmito riadkami. Preto každá operácia načítania alebo uloženia sa stáva pomalou, pretože vedú k chybám v cache – celá cache sa stala pomalou, čakajúc, kým z nej odíde odpad. Preto profilovač iba zobrazí teplý náhodný šum, rozmazaný pozdĺž celého cyklu – nebude žiadna osobitná horúca inštrukcia alebo miesto v kóde. Iba šum. A ak sa pozriete na cykly GC, všetky budú v Young Generation a super rýchle – mikrosekundy alebo milisekundy maximálne. Pretože celá táto pamäť umiera okamžite. Vytvárate miliardy gigabajtov, a on ich strihá, a strihá, a znova strihá. Všetko sa to deje veľmi rýchlo. Takže máme lacné cykly GC, teplý šum pozdĺž celého cyklu, ale chceme dosiahnuť 5-násobné zrýchlenie. V tento moment by sa v hlave malo niečo uzavrieť a zaznieť: „prečo tak?!“. Prekročenie pásma pamäte sa nezobrazuje v klasickom debuggeri, je potrebné spustiť debugger hardvérových počítačových výkonových čítačov a vidieť to samostatne a priamo. A nepriamo to možno podozrievať z týchto troch symptómov. Tretí symptóm – to je, keď sa pozriete na to, čo alokujete, pýtate sa profilu a on odpovedá: „Urobil si miliardu riadkov, ale GC to zvládol bezplatne“. Akonáhle sa to stane, uvedomíte si, že ste vygenerovali príliš veľa objektov a spálili celú šírku pásma pamäte. Spôsob, ako sa s tým vyrovnať, existuje, ale nie je očividný.
Problém v dátovej štruktúre: surová štruktúra za všetkým, čo sa deje, je príliš veľká, má 2.7G na disku, a preto je veľmi neželané robiť kópiu tejto veci – chceli by ste ju načítať priamo z sieťového byte bufferu do registrov, aby ste nemuseli čítať a zapisovať ju tam a späť päťkrát. Bohužiaľ, Java štandardne neposkytuje takú knižnicu v rámci JDK. Ale toto je predsa triviálne, nie? V podstate ide o 5-10 riadkov kódu, ktoré implementujú vlastného bufferovaného načítavača riadkov, ktorý imituje správanie triedy reťazcov a zároveň je obalom okolo podkladového byte bufferu. Výsledkom je, že pracujete takmer ako so reťazcami, ale v skutočnosti sa pohybujú ukazovatele na buffer a surové bajty sa nikam nekopírujú, a tak sa znovu využívajú tie isté buffery, znova a znova, a operačný systém je šťastný prevziať veci, na ktoré je určený, ako napríklad skrytú dvojitú bufferáciu týchto byte bufferov, a vy už viac nerecyklujete nekonečný prúd nepotrebných dát. Mimochodom, vy si predsa uvedomujete, že pri práci s GC je zaručené, že každé pridelenie pamäte nebude viditeľné pre procesor po poslednom cykle GC? Preto toto nemôže byť v cache, a potom sa deje 100%-ne zaručený zásah. Pri práci s ukazovateľom, na x86 získať register z pamäte trvá 1-2 cykly, a akonáhle sa to stane, platíte, platíte, platíte, pretože celá pamäť je na – a toto je náklad na pridelenie pamäte. Skutočný náklad.
Inými slovami, dátové štruktúry sú to, čo sa ťažko mení. A akonáhle si uvedomíte, že ste si vybrali nesprávnu dátovú štruktúru, ktorá v budúcnosti zabije výkon, zvyčajne si vyžaduje značné úsilie to napraviť, a ak to neurobíte, bude to len horšie. Predovšetkým je potrebné premýšľať o dátových štruktúrach, to je dôležité. Hlavné náklady spočívajú v zložitých dátových štruktúrach, ktoré sa začnú používať v štýle „skopíroval som dátovú štruktúru X do dátovej štruktúry Y, pretože sa mi Y viac páči“. Ale operácia kopírovania (ktorá sa javí ako lacná) v skutočnosti utráca pásmo pamäte a tam je zakopané všetko stratené vykonávanie. Ak mám obrovský reťazec s JSON a chcem ho premeniť na štruktúrované DOM-strom z POJO alebo niečoho takého, operácia parsovania tohto reťazca a zostavenia POJO, a potom nová interakcia s POJO sa nielen premení na zbytočné náklady – je to niečo, čo nie je lacné. Okrem prípadov, ak budete čítať z POJO oveľa častejšie ako z reťazca. Takto možno vyskúšať rozlúštiť reťazec a vybrať z neho len potrebné, bez prevodu do POJO. Ak sa to všetko deje na ceste, kde je potrebný maximálny výkon, žiadne POJO – musíte sa nejako priamo hrabať v reťazci.
Why create your own programming language
Andrey: Povedali ste, že na pochopenie modelu nákladov, musíte napísať svoj vlastný malý jazyk…
Cliff: Nie jazyk, ale kompilátor. Jazyk a kompilátor sú rôzne veci. Najdôležitejšie rozlíšenie je vo vašich hlavách.
Andrey: Mimochodom, ak sa nemýlim, experimentujete so zakladaním vlastných jazykov. Prečo?
Cliff: Pretože môžem! Som napoly na dôchodku, takže je to moje hobby. Po celý život som realizoval cudzie jazyky. Taktiež som veľa pracoval na programovacom štýle. A tiež preto, že vidím problémy v iných jazykoch. Vidím, že existujú lepšie spôsoby, ako robiť bežné veci. A tými by som sa rád riadil. Už ma nebaví vidieť problémy v sebe, v Jave, v Pythone, v akomkoľvek inom jazyku. Momentálne píšem v React Native, JavaScripte a Elme ako hobby, ktoré nie je o dôchodku, ale o aktívnej práci. A tiež píšem v Pythone a pravdepodobne budem pokračovať v práci na strojovom učení pre Java backendy. Existuje množstvo populárnych jazykov a každý z nich má zaujímavé vlastnosti. Každý je v niečom dobrý a dá sa skúsiť spojiť všetky tieto funkcie do jedného. Takže sa zaoberám štúdiom pre mňa zaujímavých vecí, správaním jazyka, snažím sa vymyslieť rozumnú semantiku. A zatiaľ sa mi to darí! V súčasnosti bojujem so semantikou pamäte, pretože chcem mať pamäť ako v C a Jave a získať silný model pamäte a semantiku pamäte pre načítavanie a ukladanie. Pritom mať automatické odvodenie typov ako v Haskelli. Týmto sa zaoberám posledné 2-3 mesiace, napríklad.
Andrey: Ak vytvárate jazyk, ktorý berie lepšie aspekty z iných jazykov, premýšľali ste o tom, že niekto urobí opak: vezme vaše nápady a použije ich vo svojom?
Cliff: Presne tak vznikajú nové jazyky! Prečo je Java podobná C? Pretože C malo dobrú syntaxi, ktorú všetci chápali, a Java sa inšpirovala touto syntaxi, pridala typovú bezpečnosť, kontroly hraníc polí, GC a zlepšila niektoré veci z C. Pridali svoje. Ale silno sa inšpirovali, že? Všetci stojí na pleciach gigantov, ktorí boli pred vami – takto sa robí pokrok.
Andrey: Ako chápem, váš jazyk bude bezpečný pokiaľ ide o používanie pamäte. Premýšľali ste o implementácii niečoho ako borrow checker z Rust? Pozerali ste sa na to, aký je váš názor?
Cliff: Už dlhý čas programujem v C, so všetkými týmito malloc a free príkazmi, a ručne spravujem dĺžku životnosti. Viete, 90-95% ručne spravovanej životnosti má rovnakú štruktúru. A je veľmi, veľmi bolestivé sa tým zaoberať ručne. Chcel by som, aby kompilátor jednoducho povedal, čo sa deje a čo ste dosiahli svojimi akciami. Pre niektoré veci to borrow checker robí automaticky. Mal by tiež automaticky vyvodzovať informácie, všetko rozumieť a ani ma nezaťažovať potrebou toto pochopenie vysvetliť. Mal by vykonávať minimálne lokálnu analýzu únikou, a ak sa mu to nepodarí, až vtedy by sa mali pridávať anotácie typov, ktoré by opisovali dĺžku životnosti – a takáto schéma je oveľa zložitejšia ako borrow checker, alebo akýkoľvek existujúci kontrolór pamäte. Voľba medzi „všetko je v poriadku“ a „nič som nepochopil“ — nie, malo by existovať niečo lepšie.
Takže ako človek, ktorý napísal veľa kódu v C, považujem podporu automatického riadenia životnosti za veľmi dôležitú vec. Tiež ma irituje, ako veľmi Java využíva pamäť, a hlavná sťažnosť je na GC. Pri alokácii pamäte v Jave ti nevráti pamäť, ktorá bola lokálna počas posledného cyklu GC. V jazykoch s presnejším riadením pamäte je to inak. Ak zavoláš malloc, okamžite dostaneš pamäť, ktorá sa zvyčajne práve používala. Zvyčajne robíš s pamäťou nejaké dočasné veci a okamžite ju vraciaš. A tá sa okamžite vracia do poolu malloc-u, a ďalší cyklus malloc-u ju znovu ťahá von. Preto sa skutočné využitie pamäte znižuje na množstvo živých objektov v danom okamihu, plus úniky. A ak ti neuniká všetko úplne nepríjemným spôsobom, väčšina pamäte je uložená v kešoch a procesore, a to funguje rýchlo. Ale vyžaduje množstvo ručného riadenia pamäte s malloc a free, vyvolaných na správnom mieste a v správnom poradí. Rust to dokáže správne spravovať sám a v mnohých prípadoch ponúka dokonca väčší výkon, pretože spotreba pamäte sa zúži iba na aktuálne výpočty – na rozdiel od čakania na ďalší cyklus GC, ktorý uvoľní pamäť. Výsledkom je, že sme dostali veľmi zaujímavý spôsob, ako zlepšiť výkon. A dosť silný – myslím, že som sa zaoberal takýmito vecami pri spracovaní údajov pre fintech, a to umožnilo dosahovať zrýchlenie až päťkrát. To je dosť veľké zrýchlenie, najmä vo svete, kde procesory už nie sú rýchlejšie, a my stále čakáme na zlepšenia.
Career of a performance engineer
Andrey: Ešte by som sa rád opýtal na vašu kariéru ako celok. Stali ste sa známym vďaka práci na JIT v HotSpot, a potom ste prešli do Azul – čo je tiež JVM spoločnosť. Ale už ste sa zaoberali viac hardvérom než softvérom. A potom ste zrazu prešli na Big Data a strojové učenie, a potom na detekciu podvodov. Ako sa to stalo? To sú veľmi odlišné oblasti vývoja.
Cliff: Už dlhší čas sa zaoberám programovaním a podarilo sa mi zapísať sa do veľmi rôznych aktivít. A keď ľudia hovoria: „och, ty si ten, kto robil JIT pre Java!“, je to vždy zábavné. Predtým som sa zaoberal klonom PostScript – toho jazyka, ktorý Apple kedysi používala pre svoje laserové tlačiarne. A pred tým som robil implementáciu jazyka Forth. Myslím, že tému, ktorá ma spojila, je vývoj nástrojov. Celý život vytváram nástroje, pomocou ktorých iní ľudia píšu svoje úžasné programy. No venoval som sa aj vývoju operačných systémov, ovládačov, jadrových debuggers, jazykov pre vývoj OS, ktoré začínali triviálne, ale časom sa stále zložitejšie a zložitejšie. No hlavnú tému, predsa len, tvorí vývoj nástrojov. Veľká časť môjho života prešla medzi Azul a Sun a bola o Jave. Ale keď som sa začal zaoberať Big Data a strojovým učením, znova som si obliekol svoj slávnostný klobúk a povedal: „Och, teraz máme netriviálny problém a tu sa dejú naozaj zaujímavé veci a sú tu ľudia, ktorí niečo robia.“ Je to skvelá cesta na rozvoj, ktorou stojí za to prejsť.
Áno, veľmi mám rád distribuované výpočty. Moja prvá práca bola v študentských rokoch v C, na reklamnom projekte. Boli to distribuované výpočty na čipoch Zilog Z80, ktoré zbierali dáta pre analógové optické rozpoznávanie textu, ktoré vykonával skutočný analógový analyzátor. Bola to skvelá a úplne nezvyčajná téma. Ale boli tam problémy, nejaká časť nebola správne rozpoznaná, a preto bolo potrebné vytiahnuť obrázok a ukázať ho človeku, ktorý už čítal a oznámil, čo je na ňom napísané, a preto tam boli úlohy s dátami, pričom tieto úlohy mali svoj jazyk. Bol tam backend, ktorý to všetko spracovával – súbežne pracujúce Z80 s behajúcimi terminálmi vt100 – jeden na človeka, a bola tam model paralelného programovania na Z80. Istý spoločný kus pamäte, ktorý zdieľali všetky Z80 v konfigurácii typu „hviezda”; zdieľal sa aj backplane, a polovica RAM sa zdieľala v sieti, a ďalšia polovica bola súkromná alebo išla na niečo iné. Zmysluplne zložitý paralelný distribuovaný systém so zdieľanou… polozdieľanou pamäťou. Kedy to bolo… Už si ani nepamätám, niekde v polovici 80-tych rokov. Pekne dávno.
Áno, považujme, že 30 rokov je dosť dávno. Úlohy spojené s distribuovanými výpočtami existujú už pomerne dlho, ľudia sa odjakživa snažili s nimi bojovať. -klastry. Tieto klastry vyzerajú ako… Napríklad: existuje Ethernet a váš rýchly x86 je pripojený k tomuto Ethernetu, a teraz by ste chceli získať fake shared memory, pretože nikto sa vtedy nemohol venovať kódovaniu distribuovaných výpočtov, bolo to príliš zložité, a preto bola fake shared memory s ochranou strán pamäte na x86, a ak ste písali na túto stránku, hovorili sme ostatným procesorom, že ak sa dostanú k tej istej shared memory, bude ju potrebné načítať od vás. Tak sa objavil akýsi protokol podpory koherencie cache a softvér na to. Zaujímavá koncepcia. Skutočný problém bol samozrejme v niečom inom. Všetko to fungovalo, ale rýchlo ste narazili na problémy s výkonnosťou, pretože nikto úplne nechápal model výkonnosti na dostatočne dobrej úrovni – aké sú vzory prístupu k pamäti, ako zabezpečiť, aby sa uzly navzájom nepingovali donekonečna, a tak ďalej.
V H2O som prišiel na to, že samotní vývojári sú zodpovední za určenie, kde sa skrýva paralelizmus a kde nie. Vyvinul som taký kódovací model, že písanie vysokovýkonného kódu sa stalo jednoduchým. Naopak, napísať kód, ktorý beží pomaly, je náročné a bude vyzerať zle. Musíte sa naozaj snažiť, aby ste napísali pomalý kód, musíte používať neštandardné metódy. Spomalený kód je viditeľný na prvý pohľad. Ako dôsledok spravidla píšeme kód, ktorý funguje rýchlo, ale musíte sa zaoberať tým, čo robiť v prípade zdieľanej pamäte. Celé to súvisí s veľkými maticami a správanie tam je podobné nevaultilným veľkým maticiam v paralelnom Jave. Predstavte si, že dva vlákna zapisujú do paralelnej matice, jedno vyhrá a druhé, podľa toho, prehrá, a vy neviete, kto je kto. Ak nie sú volatílne, poradie môže byť akékoľvek – a skutočne to dobre funguje. Ľudia sa naozaj starajú o poradie operácií, správne rozmiestňujú volatílne a na správnych miestach očakávajú problémy s výkonom súvisiace s pamäťou. Inak by jednoducho písali kód vo forme cyklov od 1 do N, kde N sú nejaké trilióny, s nádejou, že všetky komplikované prípady sa automaticky stanú paralelnými – a tam to nefunguje. Ale v H2O to nie je ani Java, ani Scala, môžete to považovať za „Java minus minus“, ak chcete. Je to veľmi intuitívny spôsob programovania a je podobný písaniu jednoduchého kódu v C alebo Jave s cyklami a maticami. A pritom môžete spracovávať pamäť v terabajtoch. Stále používam H2O. Občas ho používam v rôznych projektoch – a to je stále najrýchlejšia vec, ktorá násobne prekonáva konkurenciu. Ak robíte Big Data s kolónovými údajmi, je veľmi ťažké prekonať H2O.
Technical challenges
Andrey: Aký bol váš najväčší problém v celej kariére?
Cliff: Diskutujeme o technickej alebo netechnickej stránke otázky? Povedal by som, že najväčšie výzvy nie sú technické.
Pokiaľ ide o technické výzvy, jednoducho som ich prekonal. Ani neviem, ktorá bola najväčšia, ale bolo ich niekoľko dosť zaujímavých, na ktoré som strávil množstvo času a mentálnych bojov. Keď som nastúpil do Sunu, bol som presvedčený, že vytvorím rýchly kompilátor, ale mnoho seniorov mi na to povedalo, že sa mi to nikdy nepodarí. Ale ja som sa tohto cítila, napísal som kompilátor až po alokátor registrov a bol dosť rýchly. Bol rovnako rýchly ako moderný C1, ale vtedy bol alokátor oveľa pomalší a pri spätnom pohľade – bol to problém veľkej dátovej štruktúry. Potreboval som ju, aby som napísal grafický alokátor registrov, a nerozumel som dileme medzi vyjadrovacou schopnosťou kódu a rýchlosťou, ktorá vtedy existovala a bola veľmi dôležitá. Ukázalo sa, že dátová štruktúra zvyčajne presahuje veľkosť cache na x86 procesoroch tej doby, a preto ak som pôvodne predpokladal, že alokátor registrov bude pracovať 5-10 percent celkového času JIT, v skutočnosti to vyšlo na 50 percent.
Čas plynul, kompilátor sa stal stále jasnejším a výkonným, prestal generovať hrozný kód vo viacerých prípadoch a výkon sa čoraz častejšie podobal tomu, čo poskytuje C kompilátor. Ak samozrejme nepíšeš nejaké zlátaniny, ktoré ani C nepoprave. Ak píšeš kód ako v C, dostávaš výkon ako v C vo väčšine prípadov. A čím ďalej, tým častejšie sa vyskytol kód, ktorý asymptoticky zodpovedal úrovni C, alokátor registrov sa začal podobať na niečo dokončené… bez ohľadu na to, či tvoj kód beží rýchlo alebo pomaly. Pokračoval som v práci na alokátore, aby robil lepšie alokácie. Stával sa čoraz pomalším, ale poskytoval stále lepší výkon v tých prípadoch, kde si nikto iný neporadil. Mohol som sa ponoriť do alokátora registrov, investovať tam mesiac práce a náhle sa všetok kód začal vykonávať o 5% rýchlejšie. To sa dialo znova a znova a alokátor registrov sa stal akýmsi umeleckým dielom – všetci ho milovali alebo nenávideli a ľudia z akademickej sféry sa pýtali „prečo sa všetko robí práve takto“, prečo nie , a čo je na tom iné. Odpoveď je rovnaká: alokátor založený na farebnom grafu plus veľmi precízna práca s medzibufferovým kódom rovná sa nástroj víťazstva, najlepšia kombinácia, ktorú nikto nemôže prekonať. A je to dosť nejednoznačná vec. Všetko ostatné, čo robí kompilátor – sú to celkom známe veci, aj keď sú tiež priviedli na úroveň umenia. Vždy som robil veci, ktoré mali premeniť kompilátor na dielo umenia. Ale nič z toho nebolo niečím mimoriadnym – s výnimkou alokátora registrov. Základ je v tom, že je potrebné opatrne pod zaťažením a, ak sa to stane (môžem to vysvetliť podrobnejšie, ak je to zaujímavé), to znamená, že je možné inlineovať agresívnejšie, bez rizika prekonania ohybu grafu výkonu. V tých časoch existovalo množstvo plnohodnotných kompilátorov, ozdobených haraburdím a zvukovými efektmi, v ktorých boli alokátory registrov, ale nikto iný s tým už viac neuspel.
Problém je v tom, že ak pridáš metódy vhodné na inlining a zvyšuješ oblasť inlining, súbor používaných hodnôt okamžite prekročí počet registrov a musíš ich vylúčiť. Kritická úroveň obvykle nastáva, keď alokátor kapituloval, a jeden dobrý kandidát na vylúčenie znamená, že vylúčiš niektoré naozaj extrémne hodnoty. Hodnota inliningu spočíva v tom, že prichádzaš o časť režijných nákladov, nákladov na volanie a ukladanie, môžeš vidieť hodnoty vo vnútri a môžeš ich ďalej optimalizovať. Náklady na inlining sú v tom, že sa vytvorí veľké množstvo živých hodnôt, a ak tvoj alokátor registrov vylúči viac, než je potrebné, okamžite strácaš. Preto väčšina alokátorov má problém: keď inlining prekročí určitú hranicu, začnú sa vylučovať všetky možné hodnoty a výkon sa dá vyhodiť do záchodu. Tí, ktorí implementujú kompilátor, pridávajú nejaké heuristiky: napríklad, aby zastavili inlining, keď dosiahne nejakú dostatočne veľkú veľkosť, pretože alokácie všetko pokazia. Tak vzniká zlomyšlenie výkonu – inliníš, inliníš, výkon pomaly rastie – a potom bum! – spadne rýchlym tempom, pretože si inlinoval príliš veľa. Takto to fungovalo až do príchodu Javy. Java vyžaduje omnoho viac inliningu, a preto som musel urobiť svoj alokátor oveľa agresívnejším, aby sa vyrovnával a nespadol, a ak si inlinoval príliš veľa – začne vylúčiť, ale následne aj tak nastáva okamih „už žiadne vylúčenie“. To je zaujímavé pozorovanie a prišlo ku mne akosi z ničoho, nie je to zrejmé, ale dobre sa to vyplatilo. Začal som sa zameriavať na agresívny inlining a to ma priviedlo na miesta, kde sú výkony Javy a C vedľa seba. Skutočne sú blízko – môžem písať kód v Jave, ktorý je výrazne rýchlejší ako kód v C a podobne, ale v priemere, v širšom pohľade, sú si približne rovné. Myslím, že časť tejto zásluhy patrí alokátorovi registrov, ktorý mi umožňuje inlinovať maximálne jednoducho. Jednoducho inlinujem všetko, čo vidím. Otázka tu je, či alokátor funguje dobre, a či výsledkom je rozumne fungujúci kód. To bol veľký problém: pochopiť toto všetko a prinútiť to fungovať.
A bit about register allocation and multithreading
Vladimír: Problémy ako alokácia registrov sa zdajú byť akousi večnou nekonečnou témou. Zaujímavé je, či niektorá idea vyzerala sľubne, ale potom zlyhala v praxi?
Cliff: Samozrejme! Alokácia registrov je oblasť, kde sa snažíš prispôsobiť nejaké heuristiky na riešenie NP-úplnej úlohy. A nikdy nedosiahneš dokonalé riešenie, že? Je to jednoducho nemožné. Pozrite, Ahead of Time kompilácia – tá tiež nefunguje dobre. Hovoríme tu o niektorých priemerných prípadoch. O typickej výkonnosti, takže môžeš ísť a zmerať niečo, čo považuješ za dobrú typickú výkonnosť – veď nakoniec pracuješ na jej zlepšení! Alokácia registrov je téma, ktorá sa úplne venuje výkonnosti. Akonáhle máš prvý prototyp, ktorý funguje a robí to, čo má, prichádza práca na výkonnosti. Musíš sa naučiť dobre merať. Prečo je to dôležité? Ak existujú jasné dáta, môžeš sa pozrieť na rôzne oblasti a vidieť: aha, toto pomohlo tu, ale tu to úplne zlyhalo! Objavujú sa nejaké dobré nápady, pridáš novú heuristiku a zrazu všetko začína fungovať priemerne o niečo lepšie. Alebo nezačne. Mal som veľa prípadov, keď sme sa snažili o päť percent výkonnosti, ktoré rozdeľovali našu úpravu od predchádzajúceho alokátora. A zakaždým to vyzerá takto: niekde sme vyhrali, niekde prehrali. Ak máš dobré nástroje na analýzu výkonnosti, môžeš nájsť zlé nápady a pochopiť, prečo zlyhávajú. Možno by stálo za to nechať všetko tak, alebo sa vážnejšie zaoberať jemným doladením, alebo ísť a opraviť niečo iné. To je celá škála vecí! Urobil som tento skvelý hack, ale potrebujem ešte tento, a tento, a tento – a táto celková kombinácia prináša určité zlepšenia. A jednotlivé nápady môžu zlyhávať. Taká je prirodzenosť práce na výkonnosti NP-úplných úloh.
Vladimír: Zdá sa, že veci ako farbenie v alokátoroch sú už vyriešenou úlohou. Nuž, pre vás vyriešenou, podľa toho, čo rozprávate, tak má to vôbec zmysel…
Cliff: Nie je to vyriešené ako také. Ty to musíš premeniť na „vyriešené“. Existujú ťažké úlohy a tie je potrebné riešiť. Keď je to hotové, prichádza čas na prácu na výkonnosti. K tejto práci treba pristupovať zodpovedne – robiť benchmarky, zbierať metriky, vysvetľovať situácie, keď sa pri návrate k predchádzajúcej verzii tvoj starý hack opäť rozbehol (respektíve naopak, prestal fungovať). A neustupovať, pokiaľ niečo nedosiahneš. Ako som už povedal, ak sa zázračné nápady, ktoré nefungovali, ale v oblasti alokácie registrov sú nápady prakticky nekonečné. Je možné napríklad čítať vedecké publikácie. Hoci sa táto oblasť teraz pohybuje pomalšie a je jasnejšia než v časoch svojej mladosti. Napriek tomu, v tejto oblasti pracuje nekonečné množstvo ľudí a všetky ich nápady treba vyskúšať, všetky čakajú na svoj okamih. A nemôžeš povedať, aké sú dobré, ak ich neskúsiš. Ako dobre sa integrujú so všetkým ostatným vo tvojej alokátore, veď alokátor robí množstvo vecí, a niektoré nápady v tvojom konkrétnom alokátore nemusia fungovať, ale v inom alokátore – hravo. Základný spôsob, ako zvíťaziť pre alokátor – vytiahnuť pomalé veci za hranice hlavnej cesty a nútiť, aby sa rozdelili na hranicách pomalých ciest. Preto, ak chceš spustiť GC, ísť po pomalej ceste, deaktivovať optimalizáciu, vyhodiť výnimku, všetko v tomto duchu – vieš, že tieto veci sú relatívne zriedkavé. A naozaj sú zriedkavé, kontroloval som. Robíš dodatočnú prácu a vďaka tomu zmizne množstvo obmedzení na týchto pomalých cestách, ale to nie je veľmi dôležité, pretože sú pomalé a málokedy sa po nich chodí. Napríklad nulový ukazovateľ – on sa nikdy nestane, že? Je potrebné mať niekoľko ciest pre rôzne veci, ale nemali by si navzájom prekážať na hlavnej ceste.
Vladimír: Čo si myslíte o multi-jadrovosti, keď jadrá sú okamžite tisíce? Je to užitočná vec?
Cliff: Úspech GPU ukazuje, že je to dosť užitočné!
Vladimír: Sú dosť špecifické. A čo procesory všeobecného určenia?
Cliff: Takže, to bola obchodná model Azul. Odpoveď prišla ešte v ére, keď ľudia veľmi milovali predvídateľný výkon. Vtedy bolo náročné písať paralelný kód. Model kódovania H2O sa dobre škáluje, ale nie je to model pre všeobecné použitie. Snáď len o niečo univerzálnejší ako pri používaní GPU. Hovoríme o zložitosti vývoja takej veci alebo o zložitosti jej používania? Napríklad, zaujímavú lekciu mi dala Azul, dosť nejasnú: malé kešovanie je v poriadku.
The biggest challenge of your life
Vladimír: Čo tak netechnické výzvy?
Cliff: Najväčšou výzvou bolo neprejavovať... dobrotu a láskavosť voči ľuďom. A ako následok som sa neustále ocital v extrémne konfliktne ladených situáciách. V tých, v ktorých som vedel, že všetko ide z kopca, ale nevedel som, ako pokračovať v riešení týchto problémov a nezvládol som ich. Množstvo dlhodobých problémov trvajúcich desiatky rokov sa objavilo práve týmto spôsobom. To, že v Jave existujú kompilátory C1 a C2, je priamym dôsledkom tohto. To, že v Jave počas desiatich rokov neexistovala viacúrovňová kompilácia, je tiež priamym dôsledkom. Je jasné, že takýto systém sme potrebovali, ale nie je jasné, prečo tu nebol. Mal som problémy s jedným inžinierom... alebo skupinou inžinierov. Kedysi dávno, keď som začal pracovať v Sun, bol som... Dobre, nielen vtedy, vždy som mal svoj názor na všetko. A považoval som za pravdu, že môžete jednoducho vziať tú svoju pravdu a povedať ju priamo. Najmä preto, že som bol šokujúco správny väčšinu času. A ak sa ti taký prístup nepáči... najmä ak si zjavne na omyle a robíš nezmysly... V podstate, len málo ľudí dokázalo znášať takú formu komunikácie. Hoci niektorí mohli, napríklad ja. Celý svoj život som postavil na meritokratických princípoch. Ak mi ukážete niečo nesprávne, okamžite sa otočím a poviem: povedal si nezmysel. Pri tom sa samozrejme ospravedlním a tak ďalej, uznám zásluhy, ak nejaké sú, a vykonám ďalšie správne kroky. Na druhej strane, som šokujúco správny šokujúco veľké percento celkového času. A to veľmi dobre nefunguje vo vzťahoch s ľuďmi. Ani sa nesnažím byť milý, ale stavím otázku na ostrie. „Toto nikdy nefunguje, pretože raz, dva a tri.“ A oni: „Ach!“. Boli aj iné následky, ktoré je asi lepšie vynechať: napríklad tie, ktoré viedli k rozvodu so ženou a desaťročnej depresii po tom.
Challenging oneself means confronting others and their perceptions of what you can or cannot do, what is important and what is not. There have been many challenges regarding coding style. I still write a lot of code, and back then I even had to slow down because I was doing too many tasks at once poorly, instead of focusing on just one. Looking back, I wrote half of the Java JIT team's code, the C2 team. The next fastest coder wrote at half my speed, the next even slower, and it was an exponential decline. The seventh person in line was very, very slow — that's how it always is! I touched a lot of code. I watched what everyone wrote, without exception, I scrutinized their code, reviewed each one, and still managed to write more than any of them. This approach doesn't work very well with people. Some don’t like it. And when they can’t handle it, all sorts of complaints start. For example, once I was told to stop writing code because I was writing too much, and it was jeopardizing the team. To me, it all sounded like a joke: dude, if the entire remaining team disappears, and I keep writing code, you'll only lose half the team. On the other hand, if I keep writing code and you lose half the team — that sounds like really poor management. I never thought much about it, I never talked about it, but it was still somewhere in my mind. The thought that spun in the back of my consciousness was: "Is everyone just joking?" So, the biggest problem was me and my relationship with people. Now I understand myself much better, I led programmers for a long time, and now I directly tell people: you know, I am who I am, and you’ll have to deal with me — is it okay if I stay here? And when they started to handle it, everything worked out. I’m neither bad nor good, I have no bad intentions or selfish aspirations, it’s just my nature, and one has to live with that.
Andrey: Recently, everyone has started talking about self-awareness for introverts, and in general about soft skills. What can be said about this?
Cliff: Áno, to bolo pochopenie a lekcia, ktorú som si vzal z rozvodu so svojou manželkou. Čo som si z tohto rozvodu odniesol – je to poznanie seba samého. Tak som začal chápať iných ľudí. Chápať, ako toto interakcia funguje. To malo za následok odkrytia jedno za druhým. Objavilo sa uvedomenie, kto som a čo predstavujem. Čo robím: buď sa zameriavam na úlohu, alebo sa vyhýbam konfliktu, alebo niečo iné – a podobná úroveň sebapoznania naozaj pomáha udržať sa pohromade. Po tom všetkom ide všetko oveľa jednoduchšie. Jedna vec, ktorú som objavil nielen u seba, ale aj u ostatných programátorov – je neschopnosť verbalizovať myšlienky, keď si v stave emocionálneho stresu. Napríklad, sedíš a programuješ, si v stave toku a zrazu k tebe pribehuje niekto a začne hystericky kričať, že niečo je rozbité, a teraz sa voči tebe budú uplatňovať extrémne opatrenia. A nemôžeš ani slovo povedať, pretože si v stave emocionálneho stresu. Nadobudnuté vedomosti ti umožňujú pripraviť sa na túto chvíľu, prežiť ju a prejsť na plán úniku, po ktorom už môžeš niečo urobiť. Takže áno, keď začneš chápať, ako toto všetko funguje – je to obrovské život meníce udalosti.
Sám som nedokázal nájsť správne slová, ale zapamätal som si sekvenciu krokov. Podstata je taká, že táto reakcia je rovnakou mierou fyzická, ako aj verbálna, a potrebuješ priestor. Taký priestor, v zenovskom zmysle. Práve toto je potrebné vysvetliť a potom sa okamžite odsunúť na stranu – čisto fyzicky sa odsunúť. Keď mlčím, dokážem spracovať situáciu z hľadiska emócií. Ako sa adrenalín dostáva do mozgu, prepína ťa do režimu „biješ alebo bežíš“, už nemôžeš nič hovoriť, nie – teraz si idiot, inžinier na bitie, neschopný dôstojnej odpovede alebo aspoň zastaviť útok, a útočník môže útočiť znovu a znovu. Najprv sa musíš znova stať sebou, získať kontrolu, vyjsť z režimu „biješ alebo bežíš“.
A na to je potrebný verbálny priestor. Proste voľný priestor. Ak vôbec niečo povedať, môžeme povedať práve toto a potom ísť a naozaj nájsť si "priestor": ísť sa prejsť po parku, zamknúť sa v sprche – je to jedno. Hlavné je dočasne sa odpojiť od tej situácie. Hneď ako sa aspoň na niekoľko sekúnd odpojíš, kontrola sa vráti, začneš myslieť triezvo. "Dobré, nie som predsa nejaký idiot, nerobím hlúpe veci, som celkom užitočný človek". Hneď ako si sa dokázal presvedčiť, že je to pravda, je čas prejsť na ďalší krok: pochopiť, čo sa stalo. Boli ste napadnutí, útok prišiel z nečakaného smeru, bola to nespravodlivá podlá pasca. To je zlé. Ďalší krok – pochopiť, prečo bolo útokujúcemu to potrebné. Naozaj, prečo? Možno preto, že je sám v besnení? Prečo je v besnení? Napríklad preto, že sa sám prepadol a nemôže prijať zodpovednosť? Takýmto spôsobom je potrebné opatrne spracovať celú situáciu. Ale na to potrebujeme priestor na manévrovanie, verbálny priestor. Najprv je potrebné prerušiť verbálny kontakt. Odísť od diskusie slovami. Zrušiť to, odísť čo najrýchlejšie. Ak ide o telefonát – proste zložte telefón – to je zručnosť, ktorú som získal počas komunikácie s bývalou manželkou. Ak diskusia nevedie nikam dobrému, proste povedz "zbohom" a zlož slúchadlo. Z druhej strany slúchadla: "bla-bla-bla", ty odpovedáš: "aha, zbohom!" a zložíš. Proste prerušíš rozhovor. O päť minút neskôr, keď sa ti vráti schopnosť myslieť jasne, trošku si sa ochladil, stáva sa možné všetko premyslieť, čo sa vlastne stalo a čo bude ďalej. A začať formulovať premyslenú odpoveď, nie len reagovať na emócie. Pre mňa bolo prelomom v sebaúzmore to, že v prípade emocionálneho stresu nemôžem rozprávať. Vystúpiť z tohto stavu, premyslieť a naplánovať, ako odpovedať a kompenzovať problémy – to sú správne kroky, keď nemôžeš hovoriť. Najjednoduchší spôsob – ujsť od situácie, v ktorej sa prejavuje emocionálny stres a proste prestať sa na tomto strese podieľať. Potom nadobúdaš schopnosť myslieť, keď môžeš myslieť, objaví sa možnosť hovoriť, a tak ďalej.
Mimochodom, v súdnej sieni sa advokát protistrany snaží urobiť to isté s tebou – teraz je jasné prečo. Pretože má možnosť ťa potlačiť do takého stavu, že nebudeš schopný ani vysloviť svoje meno, napríklad. V najdoslovnejšom zmysle, nebudeš schopný hovoriť. Ak sa to s tebou deje a ak vieš, že sa ocitneš na mieste, kde prebiehajú verbálne bitky, na mieste ako súd, môžeš prísť so svojím právnikom. Právnik ťa zastúpi a ukončí verbálny útok, a urobí to právoplatným spôsobom, a k tebe sa vráti strata zenového priestoru. Napríklad, niekoľkokrát som musel zavolať rodine, sudca k tomu pristupoval celkom priateľsky, ale advokát protistrany na mňa kričal a kričal, dokonca som nemal šancu zasiahnuť. V takýchto prípadoch pre mňa najlepšie funguje použitie prostredníka. Prostredník ukončuje všetok ten tlak, ktorý na teba prichádza nepretržitým tokom, získaš potrebný zenový priestor a spolu s ním sa vráti schopnosť hovoriť. Je to celá oblasť poznania, v ktorej je potrebné sa veľa učiť, veľa objavovať vo vnútri seba, a toto všetko sa mení na vysokúúrovňové strategické rozhodnutia, rôzne pre rôznych ľudí. Niektorí ľudia nemajú vyššie uvedené problémy, zvyčajne ich nemajú tí, ktorí sa profesionálne zaoberajú predajom. Títo ľudia, ktorí si zarábajú na živobytie slovami – známi speváci, básnici, náboženskí vodcovia a politici, majú vždy čo povedať. Nemajú také problémy, ale ja ich mám.
Andrey: Bolo to… nečakané. Skvele, už sme toho dosť prebrali a je čas ukončiť tento rozhovor. Určite sa stretneme na konferencii a môžeme pokračovať v tomto dialógu. Uvidíme sa na Hydre!
Pokračovať v komunikácii s Cliffom bude možné na konferencii Hydra 2019, ktorá sa uskutoční 11.-12. júla 2019 v Petrohrade. Príde s prednáškou . Lístky si môžete zakúpiť .
Zdroj: habr.com
