Cliff Click — CTO společnosti Cratus (IoT senzory pro zlepšení procesů), zakladatel a spoluzakladatel několika startupů (včetně Rocket Realtime School, Neurensic a H2O.ai) s několika úspěšnými výstupy. Cliff napsal svůj první kompilátor ve 15 letech (Pascal pro TRS Z-80)! Je nejznámější za práci na C2 v Java (the Sea of Nodes IR). Tento kompilátor ukázal světu, že JIT může produkovat kvalitní kód, což se stalo jedním z faktorů, proč se Java stala jednou z hlavních moderních softwarových platforem. Poté Cliff pomohl společnosti Azul Systems postavit 864jádrový mainframe se softwarem napsaným čistě v Javě, který podporoval GC pauzy na 500gigabajtovém haldě v rozmezí 10 milisekund. Obecně, Cliff měl možnost pracovat na všech aspektech JVM.
Tento habra-příspěvek je velký rozhovor s Cliffem. Budeme hovořit na následující témata:
- Přechod k nízkoúrovňovým optimalizacím
- Jak provést velkou refaktorizaci
- Model nákladů
- Výuka nízkoúrovňovým optimalizacím
- Praktické příklady zlepšení výkonu
- Proč vytvořit svůj vlastní programovací jazyk
- Kariéra inženýra výkonu
- Technické výzvy
- Trochu o alokaci registrů a vícejádrovosti
- Největší výzva v životě
Rozhovor vede:
- Andrej Satarin z Amazon Web Services. Ve své kariéře měl možnost pracovat na zcela různých projektech: testoval distribuovanou databázi NewSQL v Yandexu, systém cloudového detekování v laboratoři Kaspersky, multiplayerovou hru v Mail.ru a službu pro výpočet měnových cen v Deutsche Bank. Zajímá se o testování velkorozměrových backendových a distribuovaných systémů.
- Vladimir Sitnikov z Netcracker. Deset let pracuje na výkonu a škálovatelnosti NetCracker OS — software používaného provozovateli sítí pro automatizaci procesů správy sítě a síťového vybavení. Zajímá se o otázky výkonu Java a Oracle Database. Autor více než deseti zlepšení výkonu v oficiálním PostgreSQL JDBC ovladači.
Přechod k nízkoúrovňovým optimalizacím
Andrej: Jste známý v oblasti JIT kompilace, v Javě a práci na výkonu obecně, že?
Cliff: Přesně tak!
Andrej: Pojďme začít s obecnými otázkami o práci na výkonu. Co si myslíte o volbě mezi vysoceúrovňovými a nízkoúrovňovými optimalizacemi, jako je práce na úrovni CPU?
Cliff: Tady je všechno jednoduché. Nejrychlejší kód je ten, který se nikdy nespouští. Proto je vždy důležité začít na vysoké úrovni a pracovat na algoritmech. Lepší O-notation přebije horší O-notation, jen pokud se neobjeví některé dostatečně velké konstanty. Práce na nízké úrovni přichází až na samém konci. Obvykle, pokud jste optimalizovali celý zbývající stack dostatečně dobře a stále zbývá něco zajímavého – to už je nízká úroveň. Ale jak začít na vysoké úrovni? Jak zjistit, že bylo uděláno dostatečné množství práce na vysoké úrovni? Nu… nijak. Neexistují žádné hotové recepty. Musíte se vyznat v problému, rozhodnout se, co chcete udělat (abyste se vyhnuli zbytečným krokům) a teprve pak můžete vytáhnout profiler, který může říct něco užitečného. V určitém okamžiku si sami uvědomíte, že jste se zbavili zbytečných věcí a nastal čas věnovat se jemnému ladění na nízké úrovni. To je rozhodně specifický druh umění. Spousta lidí dělá zbytečné věci, ale pohybuje se tak rychle, že se o výkonu nemají čas starat. Ale to platí do té doby, než se problém stane naléhavým. Obvykle 99 % času nikoho nezajímá, co dělám, až do momentu, kdy se na kritické cestě objeví důležitá věc, o kterou má někdo zájem. A pak se všichni začnou ptát na téma „proč to od začátku nefungovalo dokonale“. V podstatě vždy existuje něco, co lze zlepšit v oblasti výkonu. Ale 99 % času nemáte vodítka! Jen se snažíte, aby něco fungovalo, a při tom zjistíte, co je důležité. Nikdy nemůžete vědět předem, že tento kousek by měl být dokonalý, takže v podstatě musíte být dokonalí ve všem. A to je nemožné a tak se to nedělá. Vždy je spousta věcí k opravě – a to je naprosto v pořádku.
Jak provést velkou refaktorizaci
Andrej: Jak pracujete na výkonu? Je to přece nadřazený problém. Například, měli jste někdy na starosti problémy, které vznikly v důsledku interakce velkého množství již existující funkčnosti?
Cliff: Snažím se tomu vyhýbat. Jestliže vím, že výkon bude problém, přemýšlím o tom, než začnu kódovat, zejména o datových strukturách. Ale často to zjistíš až mnohem později. A pak musíš přistoupit na krajní opatření a udělat to, co nazývám „přepracuj a vládni“: musíš se chopit dostatečně velké části. Část kódu bude stejně nutné přepracovat kvůli problémům s výkonem nebo z jiného důvodu. Ať už je důvod k přepracování kódu jakýkoli, skoro vždy je lepší přepracovat větší kus než menší. V tu chvíli se všichni začnou třást strachy: „ó bože, nesmíš se dotýkat tolik kódu!“. Ale ve skutečnosti tento přístup většinou funguje mnohem lépe. Je třeba se hned uchopit většího problému, obkreslit kolem něj velký kruh a říct: všechno, co je uvnitř kruhu, přepracovávám. Hranice je přece mnohem menší než ten obsah uvnitř, který podléhá výměně. A pokud takové obkreslení hranic umožní perfektně zvládnout práci uvnitř – máš volné ruce, dělej si, co chceš. Jakmile porozumíš problému, proces přepracování jde mnohem hladčeji, tak si ujídej velký kus!
Zároveň, když přepracováváš velký kus a chápeš, že výkon se stane problémem, můžeš se začít o něj hned obávat. Obvykle se to promění v jednoduché věci jako „nekopíruj data, spravuj data co nejjednodušeji, dělej je menšími“. U velkých přepracování jsou standardní způsoby, jak zlepšit výkon. A ty se téměř vždy točí kolem dat.
Model nákladů
Andrej: V jednom z podcastů jste mluvili o modelech nákladů v kontextu výkonu. Můžete vysvětlit, co tím bylo myšleno?
Cliff: Samozřejmě. Narodil jsem se v době, kdy byla výkon procesoru nesmírně důležitý. A tato éra se opět vrací – osud není bez ironie. Začínal jsem žít ve světě osmibitových strojů, můj první počítač měl 256 byte. Skutečně byte. Všechno bylo velmi malé. Bylo třeba vyhodnocovat instrukce a jakmile jsme začali postupovat nahoru v rámci jazyků programování, jazyky přebíraly stále více a více. Bylo to Assembler, pak Basic, pak C, a C přebíral práci s mnoha detaily, jako je distribuce registrů a výběr instrukcí. Ale tam bylo všechno docela jasné a pokud jsem udělal ukazatel na instanci proměnné, dostal jsem load, a náklady na tuto instrukci jsou známy. Hardware poskytuje známý počet strojových cyklů, takže rychlost provedení různých akcí lze spočítat pouhým sečtením všech instrukcí, které plánuješ spustit. Každý compare/test/branch/call/load/store se dal sečíst a říct: máte tu čas provedení. Při optimalizaci výkonu si určitě všimneš, jaká čísla odpovídají malým horkým cyklům.
Ale jakmile přepneš na Javu, Python a podobné věci, velmi rychle se vzdaluješ od nízkoúrovňového hardwaru. Jaká je cena volání getteru v Javě? Pokud JIT v HotSpotu vše správně , bude to load, ale pokud to neudělal – bude to volání funkce. Vzhledem k tomu, že volání leží na horkém cyklu, zruší všechny ostatní optimalizace v tomto cyklu. Proto bude skutečná cena mnohem vyšší. A hned ztrácíš schopnost podívat se na kus kódu a rozumět tomu, co bychom měli vykonat v termínech taktovací frekvence procesoru, použité paměti a cache. To všechno se stává zajímavé pouze tehdy, pokud se skutečně ponoříš do výkonu.
Nyní jsme se ocitli v situaci, kdy rychlosti procesorů už více než deset let téměř nerostou. Staré časy se vracejí! Už nemůžete počítat s dobrou jednovláknovou výkonností. Ale pokud se náhodou pustíte do paralelního výpočtu – to je šíleně složité, všichni se na vás dívají jako na Jamese Bonda. Desetinásobná zrychlení se zde obvykle vyskytují tam, kde si někdo něčeho nevšiml. Paralelnost vyžaduje spoustu práce. Aby bylo dosaženo oněch desetinásobných zrychlení, je třeba pochopit model nákladů. Co a kolik to stojí. A k tomu je třeba porozumět, jak jazyk odpovídá základnímu hardwaru.
Martin Thompson našel skvělé slovo pro svůj blog ! Je nezbytné pochopit, co se hardware chystá udělat, jak přesně to bude dělat a proč vlastně dělá to, co dělá. Díky tomu je docela snadné začít počítat instrukce a zjistit, kde čas běhu uniká. Pokud však nemáte odpovídající přípravu, jen hledáte černou kočku v temné místnosti. Stále vidím lidi, kteří optimalizují výkon, a nemají ani tušení, co vlastně dělají. Trpí a moc se neposunují. A když vezmu ten samý kód, přidám tam pár drobných hacků a dosáhnu pěti nebo desetinásobného zrychlení, říkají: no, to není fér, my jsme už věděli, že jsi lepší. Ohromující. O čem to mluvím… model nákladů – to je o tom, za jaký kód píšete a jak rychle v průměru funguje v rámci celkového obrazu.
Andrej: A jak takový objem udržet v hlavě? To se dosahuje velkým množstvím zkušeností, nebo? Kde se takové zkušenosti získávají?
Cliff: No, svou zkušenost jsem získal ne zrovna nejjednodušší cestou. Programoval jsem v Assembleru ještě v dobách, kdy jste mohli rozumět každé jednotlivé instrukci. Zní to hloupě, ale od té doby mi zůstala v hlavě, v paměti, navždy sada instrukcí Z80. Nepamatuji si jména lidí ani minutu po rozhovoru, ale pamatuji si kód napsaný před 40 lety. Legrační, vypadá to jako syndrom „».
Výuka nízkoúrovňovým optimalizacím
Andrej: Existuje nějaký jednodušší způsob, jak se do toho ponořit?
Cliff: Ano i ne. Hardware, který všichni používáme, se za tuto dobu příliš nezměnil. Všichni používají x86, kromě chytrých telefonů na Arm. Pokud se nevěnujete nějakému hardcore embedded programování, máte to samé. Dobře, pokračujme dál. Instrukce se také po staletí nezměnily. Je potřeba jít a napsat něco v assembleru. Trochu, ale dost, abyste začali chápat. Usmíváte se, ale já to myslím naprosto vážně. Je třeba porozumět vztahu mezi jazykem a hardwarem. Poté je třeba jít napsat trochu kódu a vytvořit malý experimentální kompilátor pro malý experimentální jazyk. „Experimentální“ znamená, že ho musíte udělat v rozumném čase. Může být super jednoduchý, ale musí generovat instrukce. Akt generování instrukcí umožní pochopit model nákladů pro most mezi vysokou úrovní kódu, na kterém všichni píší, a strojovým kódem, který se provádí na hardware. Tento vztah se vryje do paměti při psaní kompilátoru. I toho nejjednoduššího kompilátoru. Poté můžete začít zkoumat Javu a to, že její sémantická propast je mnohem hlubší, a budovat nad ní mosty, což je mnohem složitější. V Javě je mnohem složitější pochopit, zda je náš most dobrý nebo špatný, co ho přivede k pádu a co ne. Ale potřebujete nějaký výchozí bod, když se díváte na kód a chápete: „aha, tento getter by měl být inlinovaný pokaždé“. A pak se ukáže, že se to někdy opravdu děje, s výjimkou případů, kdy se metoda stane příliš velkou a JIT začne inlinovat všechno. Výkon těchto míst lze okamžitě předpovědět. Obvykle work with getters dobře, ale pak se podíváte na velké horké smyčky a zjistíte, že tam plavou nějaké volání funkcí, které nevím, co dělají. V tom je problém s masivním používáním getterů, důvod, proč se neinlinují - není jasné, zda je to getter. Pokud máte supermalou kódovou základnu, můžete si ji jednoduše zapamatovat a pak říct: to je getter a to je setter. V rozsáhlé kódové základně každá funkce prožívá svůj vlastní příběh, který nikomu, vlastně, není znám. Profiler říká, že jsme ztratili 24 % času na nějaké smyčce a abychom pochopili, co tato smyčka dělá, musíme se podívat na každou funkci uvnitř. Není možné to pochopit, aniž byste studovali funkci, a to vážně zpomaluje proces porozumění. Proto já nepoužívám gettery a settery, dostal jsem se na novou úroveň!
Kde najít model nákladů? No, můžete si něco přečíst, samozřejmě... Ale myslím, že nejlepší způsob je jednat. Vytvořit malý kompilátor a to bude nejlepší způsob, jak pochopit model nákladů a vměstnat si ho do vlastní hlavy. Malý kompilátor, který by se hodil pro programování mikrovlnné trouby – to je úkol pro začátečníka. No, myslím, že pokud už máte programovací dovednosti, mělo by to stačit. Všechny tyto věci, jako například parsování řetězce, který bude nějakým algemičtínovým výrazem, vytáhnout z něj instrukce matematických operací ve správném pořadí, vzít správné hodnoty z registrů – to vše jde snadno. A zatímco to děláte, bude se vám to otiskovat v mozku. Myslím, že všichni vědí, čím se zabývá kompilátor. A to vám poskytne pochopení modelu nákladů.
Praktické příklady zlepšení výkonu
Andrej: Na co dalšího byste měli dávat pozor při práci na výkonnosti?
Cliff: Datové struktury. Mimochodem, dlouho jsem tyto lekce nevedl… . Bylo to zábavné, ale vyžadovalo to tolik úsilí, a přitom mám ještě i život! Dobře. Takže během jednoho z velkých a zajímavých přednášek, „Kam jde váš výkon“, jsem studentům dával příklad: dva a půl gigabajtu fintech dat byla načtena z CSV souboru a dál bylo třeba spočítat počet prodávaných produktů. Běžná ticková tržní data. UDP pakety, převedené na textový formát, začínající od 70. let. Chicago Mercantile Exchange – různé věci jako olej, kukuřice, sójové boby a podobně. Bylo potřeba spočítat tyto produkty, počet obchodů, průměrný objem pohybu prostředků a zboží, atd. To je docela jednoduchá obchodní matematika: najít kód produktu (to jsou 1-2 znaky v hash tabulce), získat sumu, přidat ji do jednoho z obchodních sad, přidat objem, přidat náklady a pár dalších věcí. Velmi jednoduchá matematika. Hračková implementace byla velmi přímočará: všechno leží v souboru, čtu soubor a procházím jím, rozděluji jednotlivé záznamy na Java řetězce, hledám v nich potřebné věci a sčítám podle výše uvedené matematiky. A funguje to nějakou malou rychlostí.
S tímto přístupem je jasné, co se děje, a paralelní výpočty zde nepomohou, je to tak? Ukazuje se, že pětinásobného zvýšení výkonu lze dosáhnout pouze volbou správných datových struktur. A to překvapuje i zkušené programátory! V mém konkrétním případě bylo klíčové, že není dobré provádět alokace paměti v horké smyčce. No, to není celá pravda, ale celkově – není vhodné alokovat "jednou za X", když je X dostatečně velké. Když je X dva a půl gigabajtu, není dobré alokovat nic "jednou za znak", "jednou za řádek" nebo "jednou za pole", nic takového. Právě tohle zabírá čas. Jak to vůbec funguje? Představte si, že provádím volání String.split() nebo BufferedReader.readLine(). Readline vytváří řetězec z byteového půdorysu, který přišel přes síť, jednou pro každý řádek, pro každý z několika stovek milionů řádků. Vezmu ten řetězec, analyzuji ho a zahazuji. Proč ho zahazuji – no, už jsem ho zpracoval, hotovo. Takže, pro každý byte přečtený z těchto 2.7G bude uložena dvě písmena v řetězci, tedy už 5.4G, a ty mi pak k ničemu nejsou, proto jsou zahazovány. Když se podíváte na propustnost paměti, zatížení 2.7G prochází pamětí a paměťovou sběrnicí v procesoru a dále se dvojnásobné množství posílá do řetězce uloženého v paměti, a veškeré to se přepočítává při vytváření každého nového řetězce. Ale musím ho přečíst, hardware ho čte, i když nakonec bude všechno přepočítáno. A musím ho zapsat, protože jsem vytvořil řetězec a cache se přeplnily – cache nemůže obsahovat 2.7G. Takže, pro každý přečtený byte čtu ještě dva další byty a zapisuji dva další byty, což má poměr 4:1 – takto neúměrně plýtváme propustností paměti. A nakonec se ukazuje, že pokud dělám String.split() – dělám to rozhodně ne naposled, uvnitř může být dalších 6-7 polí. Proto klasický kód pro čtení CSV s následným parsováním řádků vede ke ztrátám propustnosti paměti kolem 14:1 ve srovnání s tím, co byste vlastně chtěli mít. Pokud tyto alokace vyhodíme, můžeme získat pětinásobné zrychlení.
Není to ani tak složité. Pokud se na kód podíváte pod správným úhlem, vše se stává docela jednoduchým, jakmile si uvědomíte jádro problému. Není třeba přestávat s alokací paměti: problém je v tom, že něco alokujete a to hned umírá, přičemž po cestě spaluje důležitý zdroj, kterým je v tomto případě propustnost paměti. A to všechno se promítá do poklesu výkonu. Na x86 se obvykle musí aktivně používat procesorové cykly, ale zde jste spálili celou paměť mnohem dřív. Řešení spočívá v tom, že je potřeba snižovat počet alokací.
Další část problému spočívá v tom, že když spuštíte profiler, když dojde k přeplnění paměťové linky, v okamžiku, kdy se to děje, obvykle čekáte na návrat cache, protože je plná nepořádku, který jste právě vytvořili se všemi těmi řetězci. Proto každá operace load nebo store se stává pomalou, protože vedou k cache miss – celá cache se stala pomalou, zatímco čekala, až z ní odjede nepořádek. Takže profiler jen ukáže teplý náhodný šum roztažený po celém cyklu – nebude tam žádná samostatná horká instrukce nebo místo v kódu. Jen šum. A pokud se podíváte na cykly GC, všechny budou v Young Generation a super rychlé – mikrosekundy nebo maximálně milisekundy. Vždyť celá ta paměť umírá okamžitě. Alokujete miliardy gigabajtů a on je ořezává, ořezává a znovu ořezává. Všechno se to děje velmi rychle. Takže tu máme levné cykly GC, teplý šum po celém cyklu, ale chceme dosažení pětinásobného zrychlení. V ten okamžik by se mělo v hlavě něco uzavřít a zaznít: „Proč to tak je?!“. Přetečení paměťové linky se nezobrazuje v klasickém debuggeru, je třeba spustit debugger hardwarových výkonových čidel a vidět to samostatně a přímo. A nepřímo to lze podezřívat z těchto tří symptomů. Třetí symptom – to je, když se podíváte na to, co alokujete, zeptáte se profilu a on odpoví: „Udělali jste miliardu řetězců, ale GC pracoval zdarma.“ Jakmile se to stalo, chápete, že jste vytvořili příliš mnoho objektů a spálili celou paměťovou linku. Existuje způsob, jak se v tom vyznat, ale není to zřejmé.
Problém ve struktuře dat: holá struktura, která leží za vším, co se děje, je příliš velká, má 2,7 GB na disku, takže je velmi nežádoucí dělat kopii této věci – chcete ji načíst přímo z ethernetového datového buffera do registrů, abyste to nemuseli číst a zapisovat tam a zpět pětkrát. Bohužel, Java ti ve výchozím nastavení nenabízí takovou knihovnu jako součást JDK. Ale to by mělo být banální, že? V podstatě se jedná o 5-10 řádků kódu, které potřebujete k tomu, abyste implementovali vlastní buffered loader řetězců, který napodobuje chování třídy řetězců, a přitom je obalovým wrapperem kolem podkladového datového buffera. Výsledkem je, že pracujete skoro jako byste pracovali se řetězci, ale ve skutečnosti se pohybují ukazatele na buffer, a syrová data se nikam nekopírují, a tímto způsobem se znovu použijí stejné buffery bez ustání, zatímco operační systém je spokojen s tím, že si vezme na starost věci, které mu jsou určeny, jako například skryté dvojité bufferování těchto datových bufferů, a už se nezabýváte nekonečným zpracováváním nepotřebných dat. Mimochodem, chápete, že při práci s GC je zaručeno, že každé přidělení paměti nebude viditelné pro CPU po posledním cyklu GC? Takže, to všechno prostě nemůže být v cache, a pak se stane 100% zaručený miss. Při práci s ukazatelem trvá na x86 přečíst registr z paměti 1-2 cykly, a jakmile k tomu dojde, platíte, platíte, platíte, protože celá paměť je – a to je cena přidělení paměti. Skutečná cena.
Jinými slovy, datové struktury jsou to, co se mění nejhůře. Jakmile si uvědomíte, že jste si vybrali špatnou datovou strukturu, která nakonec zabije výkon, obvykle je zapotřebí udělat zásadní práci, ale pokud to neuděláte, bude to ještě horší. Především je důležité přemýšlet o datových strukturách. Hlavní náklady zde leží na robustních datových strukturách, které začínáme používat stylem "zkopíroval jsem datovou strukturu X do datové struktury Y, protože se mi Y líbí víc". Ale operace kopírování (která se zdá být levná) ve skutečnosti spotřebovává paměťový pás a právě tam je zakopáno veškeré ztracené běhové času. Pokud mám obrovský řetězec s JSON a chci ho proměnit na strukturované DOM-struce z POJO nebo něco takového, operace parsování tohoto řetězce a sestavení POJO, a pak nový přístup k POJO později, se při obrací na dodatečné náklady – věc, která není levná. S výjimkou případu, kdy budete běhat po POJO mnohem častěji než po řetězci. Napadá mě, že místo toho byste mohli zkusit dekódovat řetězec a vytáhnout z něj jen to potřebné, aniž byste ho proměňovali na nějaká POJO. Pokud se to vše děje na cestě, kde je požadován maximální výkon, žádné POJO – musíte nějak přímo hrabat v řetězci.
Proč vytvořit svůj vlastní programovací jazyk
Andrej: Řekl jste, že pro pochopení modelu nákladů je třeba napsat svůj vlastní malý jazyk…
Cliff: Ne jazyk, ale kompilátor. Jazyk a kompilátor jsou různé věci. Největší rozdíl je ve vaší hlavě.
Andrej: Mimochodem, pokud vím, experimentujete s vytvořením vlastních jazyků. Proč?
Cliff: Protože mohu! Jsem napůl v důchodu, takže je to moje hobby. Celý život jsem realizoval cizí jazyky. Také jsem hodně pracoval na stylu kódování. A také proto, že vidím problémy v jiných jazycích. Vidím, že existují lepší způsoby, jak dělat běžné věci. A těch bych využil. Už mě omrzelo vidět problémy v sobě, v Javě, v Pythonu, v jakémkoli jiném jazyce. Teď píšu v React Native, JavaScriptu a Elm jako hobby, které není o důchodu, ale o aktivní práci. A také píši v Pythonu a pravděpodobně budu pokračovat s strojovým učením pro Java backendy. Existuje mnoho populárních jazyků a všechny mají zajímavé vlastnosti. Každý je dobrý něčím svým a lze zkusit zkombinovat všechny tyto vychytávky dohromady. Takže se věnuji studiu zajímavých věcí pro mě, chování jazyka, snažím se vymyslet rozumnou semantiku. A zatím se mi to daří! V současnosti bojuji s paměťovou semantikou, protože chci mít paměť jako v C a Javě, a získat silný model paměti a paměťovou semantiku pro načítání a ukládání. Zároveň mít automatické odvozování typů jako v Haskellu. Takhle se snažím zkombinovat Haskellovské odvozování typů s pamětí, která funguje jako v C a Javě. Tomu se věnuji poslední 2-3 měsíce, například.
Andrej: Pokud stavíte jazyk, který bere nejlepší aspekty z jiných jazyků, přemýšleli jste o tom, že by někdo udělal opak: vzal by vaše nápady a použil je u sebe?
Cliff: Přesně tak vznikají nové jazyky! Proč je Java podobná C? Protože C mělo dobrou syntaxi, kterou všichni chápali, a Java se inspirovala touto syntaxi, přidala do ní typovou bezpečnost, kontroly hranic polí, GC, a také zlepšili některé věci z C. Přidali své. Ale inspirovali se dost silně, že? Všichni stojí na ramenou obřích, kteří byli před tebou - takto se dělá pokrok.
Andrej: Jak chápu, váš jazyk bude bezpečný z hlediska používání paměti. Přemýšleli jste o implementaci něčeho jako borrow checker z Rustu? Dívali jste se na něj, co si o něm myslíte?
Cliff: No, už dlouho píšu na C, se všemi těmi malloc a free, a ručně spravuji životnost. Víte, 90-95% ručně řízené životnosti má stejnou strukturu. A je velmi, velmi bolestivé se tím zabývat ručně. Chtělo by to, aby překladač prostě říkal, co se děje a co jste svými činy dosáhli. U některých věcí to borrow checker dělá automaticky. A měl by také automaticky generovat informace, vše chápat a ani mě nezatěžovat tím, abych tu informaci sám formuloval. Měl by provádět minimálně lokální analýzu úniků, a teprve pokud se mu to nepodaří, měl bych přidávat typové anotace, které popisují životnost - a tento systém je mnohem složitější než borrow checker, nebo jakýkoli jiný stávající paměťový checker. Volba mezi „všechno je v pořádku“ a „nic jsem nepochopil“ - ne, mělo by tu být něco lepšího.
Jako někdo, kdo napsal hodně kódu v C, považuji za zásadní mít podporu automatického řízení životnosti. A také mě skutečně frustruje, jak moc Java využívá paměť; hlavní stížnost se týká garbage collectoru. Když alokujete paměť v Javě, nedostanete zpět paměť, která byla lokální v posledním kole garbage collectoru. V jazycích s přesnějším řízením paměti tomu tak není. Když zavoláte malloc, okamžitě získáte paměť, která byla obvykle právě použita. Obvykle paměti děláte nějaké dočasné věci a ihned ji vracíte zpět. A hned se vrací do poolu mallocu a při dalším volání mallocu ji můžete použít znovu. Proto skutečné využití paměti klesá na množství živých objektů v konkrétním časovém okamžiku, plus úniky. A pokud vám neuniká paměť v úplně nepřijatelném množství, většina paměti končí v cache a procesoru, a to funguje rychle. Ale vyžaduje to hodně manuálního řízení paměti pomocí malloc a free, volaných ve správném pořadí a na správných místech. Rust si s tím dokáže poradit sám a v mnoha případech dokonce nabídnout větší výkon, protože spotřeba paměti se zužuje pouze na aktuální výpočty – na rozdíl od čekání na další kolo garbage collectoru, které uvolní paměť. Nakonec jsme dostali velmi zajímavý způsob, jak zlepšit výkon. A docela silný – co se týče zpracování dat pro fintech, to umožňovalo zrychlení zhruba pětkrát. To je docela velké zrychlení, zejména v době, kdy procesory nezrychlují a my stále čekáme na zlepšení.
Kariéra inženýra výkonu
Andrej: Chtěl bych také položit pár otázek ohledně vaší kariéry. Stal jste se slavným díky práci na JIT v HotSpotu a poté jste se přesunuli do společnosti Azul – což je také společnost zabývající se JVM. Ale více jste se zaměřovali na hardware než na software. A pak jste se náhle přepnuli na Big Data a strojové učení, a následně na detekci podvodů. Jak k tomu došlo? To jsou velmi odlišné oblasti vývoje.
Cliff: Už se nějakou dobu zabývám programováním a stihl jsem se vyzkoušet na různých projektech. Když lidé říkají: „Oh, ty jsi ten, kdo udělal JIT pro Java!“, je to vždycky zábavné. A přitom jsem předtím dělal klon PostScriptu – toho jazyka, který Apple kdysi používala pro své laserové tiskárny. A předtím jsem realizoval jazyk Forth. Myslím, že společným tématem pro mě je vývoj nástrojů. Celý život vytvářím nástroje, pomocí kterých lidé píší své skvělé programy. Ale také jsem pracoval na vývoji operačních systémů, ovladačů, jádrových debuggerů a jazyků pro vývoj OS, které zpočátku vypadaly triviálně, ale časem se stále komplikovaly. Ale hlavní téma je přece jen vývoj nástrojů. Velká část mého života proběhla mezi Azul a Sun a byla zaměřena na Java. Ale když jsem se začal zabývat Big Data a strojovým učením, znovu jsem si nasadil svou slavnostní čepici a řekl: „Ach, nyní máme netriviální problém, a zde se děje spousta zajímavých věcí a lidí, kteří něco dělají.“ To je skvělá cesta pro rozvoj, kterou stojí za to projít.
Ano, opravdu mám rád distribuované výpočty. Moje první práce byla v době studia na C, na reklamním projektu. Byly to distribuované výpočty na čipech Zilog Z80, které shromažďovaly data pro analogové optické rozpoznávání textu, které prováděl skutečný analogový analyzátor. Bylo to úžasné a naprosto neobvyklé téma. Ale byly tam problémy, nějaká část nebyla správně rozpoznána, takže bylo potřeba dostat obrázek a ukázat ho člověku, který už to četl očima a řekl, co tam vlastně je, a proto tam byly úlohy s daty, a tyto úlohy měly svůj vlastní jazyk. Byl tam backend, který to vše zpracovával – paralelní Z80 s aktivními terminály vt100 – po jednom na člověka, a existoval model paralelního programování na Z80. Určitý společný kus paměti, který sdílely všechny Z80 uvnitř konfigurace typu „hvězda“; sdílel se i backplane, a polovina RAM byla sdílena v síti, zatímco druhá polovina byla soukromá nebo šla na něco jiného. Smysluplně složitý paralelní distribuovaný systém se sdílenou… polozdílenou pamětí. Kdy to bylo… Už si ani nevzpomenu, někde v polovině 80. let. Docela dávno.
Ano, budeme považovat, že 30 let je dost dávno. Úkoly spojené s distribuovanými výpočty existují už dlouhou dobu, lidé se s nimi odjakživa potýkali. -klastry. Takové klastry vypadají jako… Například: máte Ethernet a váš rychlý x86 je připojen k tomuto Ethernetu, a teď chcete získat fake shared memory, protože nikdo nemohl tehdy dělat kódování distribuovaných výpočtů, bylo to příliš složité a proto existovala fake shared memory s ochranou stránek paměti na x86, a pokud jste zapisovali na tuto stránku, říkali jsme ostatním procesorům, že pokud získají přístup k té samé shared memory, musí ji načíst od vás, a tak se objevil něco jako protokol pro podporu koherence cache a software pro to. Zajímavá koncepce. Skutečný problém, samozřejmě, byl jinde. To všechno fungovalo, ale rychle jste získali problémy s výkonem, protože nikdo nerozuměl modelu výkonu na dostatečně dobré úrovni – jaké tam byly vzory přístupu k paměti, jak udělat, aby uzly navzájem nepingovaly věčně, a tak dále.
V H2O jsem si vymyslel následující: sami vývojáři jsou odpovědní za určení, kde se skrývá paralelismus a kde není. Vymyslel jsem takový model kódování, že psaní vysoce výkonného kódu se stalo snadným a jednoduchým. Naproti tomu napsat pomalu běžící kód je obtížné, bude vypadat špatně. Je třeba se vážně snažit, abyste napsali pomalý kód, bude nutné použít nestandardní metody. Příliš pomalý kód je viditelný na první pohled. Jakmile toho dosáhnete, obvykle se píše kód, který funguje rychle, ale je třeba vyřešit, co dělat v případě sdílené paměti. Všechno to souvisí s velkými poli a chování je podobné nevolatilním velkým polím v paralelním Javě. Představte si, že dva vlákna zapisují do paralelního pole, jedno z nich vyhrává, a druhé tím pádem prohrává, a vy nevíte, kdo je kdo. Pokud nejsou volatilní, pořadí může být jakékoli – a to skutečně dobře funguje. Lidé skutečně dbají na pořádek operací, správně umísťují volatile a na správných místech očekávají problémy s výkonem spojené s pamětí. Jinak by prostě psali kód ve formě cyklů od 1 do N, kde N jsou nějaké triliony, v naději, že všechny složité případy se automaticky stanou paralelními – a tam to nefunguje. Ale v H2O to není ani Java, ani Scala, dá se to považovat za „Java minus minus“, pokud chcete. Je to velmi srozumitelný styl programování a připomíná psaní jednoduchého kódu v C nebo Javě s cykly a poli. Přitom však můžete zpracovávat terabajty paměti. Stále používám H2O. Občas jej používám v různých projektech – a to je stále nejrychlejší věc, která o desítky let předbíhá konkurenci. Pokud děláte Big Data s sloupcovými daty, je velmi obtížné překonat H2O.
Technické výzvy
Andrej: Jaký byl váš největší výzvou za celou vaši kariéru?
Cliff: Diskutujeme o technické nebo netechnické části otázky? Řekl bych, že největší výzvy nejsou technické.
Pokud jde o technické výzvy. Jednoduše jsem je překonal. Ani nevím, která byla ta největší, ale bylo jich několik docela zajímavých, na které mi zabralo hodně času a mentálního boje. Když jsem přišel do Sunu, byl jsem si jistý, že vytvořím rychlý kompilátor, ale spousta seniorů mi řekla, že nikdy nic nedokážu. Ale šel jsem tou cestou, napsal jsem kompilátor až po alokátor registrů a byl docela rychlý. Byl tak rychlý jako moderní C1, ale tehdy byl alokátor mnohem pomalejší, a když se dívám zpět, to byla otázka velké datové struktury. Potřeboval jsem ji k napsání grafického alokátoru registrů a nerozuměl jsem dilema mezi výstižností kódu a rychlostí, které existovalo v té době a bylo velmi důležité. Ukázalo se, že datová struktura obvykle překračuje velikost cache na x86 počítačích té doby, a proto, pokud jsem zpočátku předpokládal, že alokátor registrů bude pracovat 5-10 procent celého času jitování, v realitě to vyšlo na 50 procent.
Čas plynul, kompilátor se stal stále jasnějším a výkonnějším, přestal generovat odporný kód v větším počtu případů, a výkon čím dál tím víc začal připomínat to, co vydával kompilátor C. Pokud samozřejmě nepíšete nějakou sračku, kterou ani C nezrychlí. Pokud píšete kód jako v C, dostáváte výkon jako v C ve větším počtu případů. A čím dál častěji se vyskytoval kód, který asymptoticky odpovídal úrovni C, alokátor registrů začal připomínat něco hotového… nezávisle na tom, zda váš kód funguje rychle nebo pomalu. Pokračoval jsem v práci na alokátoru, aby prováděl lepší alokace. Stával se pomalejším a pomalejším, ale poskytoval stále lepší výkon v těch případech, kde si nikdo už nevedl. Mohu se ponořit do alokátoru registrů, bury tam měsíc práce, a najednou se celý kód začal vykonávat o 5% rychleji. To se dělo znovu a znovu a alokátor registrů se stal něco jako umělecké dílo – všichni ho milovali nebo nenáviděli, a lidé z akademie kladli otázky na téma „proč se vše dělá tímto způsobem“, proč ne. , a jaký je rozdíl. Odpověď je stále stejná: alokátor založený na barvení grafu plus velmi pečlivá práce s obslužným kódem rovná se nástroj k vítězství, nejlepší kombinace, kterou nikdo nemůže porazit. A to je docela nejasná záležitost. Všechno ostatní, co kompilátor dělá, jsou poměrně prozkoumané věci, i když i ty dosáhly úrovně umění. Vždycky jsem dělal věci, které měly proměnit kompilátor v umělecké dílo. Ale nic z toho nebylo něčím výjimečným – s výjimkou alokátoru registrů. Klíčové je to, že je potřeba pečlivě pod zátěží a pokud k tomu dojde (mohu podrobněji vysvětlit, pokud to zajímá), znamená to, že je možné inline více agresivně, bez rizika překonání ohybu výkonového grafu. V té době bylo spousta plnohodnotných kompilátorů, ozdobených vychytávkami, které měly alokátory registrů, ale nikdo to už nedokázal.
Problém je v tom, že pokud přidáš metody podléhající inliningování, a tím neustále zvyšuješ oblast inliningování, soubor používaných hodnot okamžitě překročí počet registrů a musíš začít spilit. Kritická úroveň obvykle nastává, když alokátor selže, a jeden dobrý kandidát na spilit stojí za druhým; spiliš nějaké opravdu nezvyklé věci. Hodnota inliningování spočívá v tom, že ztrácíš část overheadu, overheadu na volání a ukládání; můžeš vidět hodnoty uvnitř a dál je optimalizovat. Cena inliningování spočívá v tom, že vzniká velké množství živých hodnot, a pokud tvůj alokátor registrů spilitne víc, než je potřeba, okamžitě o to přicházíš. Proto mají většina alokátorů problém: když inlining překročí určitou mez, spilitne se vše, co se dá, a výkon může být spláchnut do záchodu. Ti, kdo implementují kompilátor, přidávají nějaké heuristiky; například aby zastavili inlining, když dosáhne určité dostatečně velké velikosti, protože alokace všechno pokazí. Tak vzniká zlomek na grafu výkonu – inlinuješ, inlinuješ, výkon pomalu roste – a pak bum! – klesne dolů jako blesk, protože jsi inlinoval příliš mnoho. Tak to šlo až do příchodu Javy. Java vyžaduje daleko více inliningování, takže jsem musel udělat svůj alokátor mnohem agresivnějším, aby se vyrovnal, a neklesnul, a když inlinuješ příliš mnoho – začne spilit, ale přesto nakonec nastane okamžik "už není více spilitování". To je zajímavé pozorování, které mi přišlo tak nějak z ničeho, není to očividné, ale dobře se to vyplatilo. Pustil jsem se do agresivního inliningování a to mě přivedlo na místa, kde je výkon Javy a C vedle sebe. Jsou opravdu blízko – mohu psát kód na Javě, který bude výrazně rychlejší než kód v C a podobně, ale v průměru, v širším kontextu věcí, jsou přibližně srovnatelné. Myslím, že část této zásluhy patří alokátoru registrů, který mi umožňuje inlinovat maximálně bez rozmyslu. Prostě inlinuji vše, co vidím. Otázka je, zda alokátor funguje dobře, zda vzniká výsledně rozumně pracující kód. Tady byla velká výzva: pochopit to vše a dostat to do chodu.
Trochu o alokaci registrů a vícejádrovosti
Vladimir: Problémy jako alokace registrů se zdají být nějakým věčným nepřetržitým tématem. Je zajímavé, zda někdy nějaký nápad vypadal slibně, ale pak neuspěl v praxi?
Cliff: Samozřejmě! Alokace registrů je oblast, v níž se snažíte najít nějaké heuristiky pro řešení NP-úplného problému. A nikdy nemůžete dosáhnout ideálního řešení, že? To je prostě nemožné. Podívejte se, Ahead of Time kompilace – ta také nefunguje dobře. Hovoříme zde o nějakých průměrných případech. O typickém výkonu, takže je možné jít a měřit něco, co považujete za dobrý typický výkon – nakonec, přece pracujete na tom, aby se zlepšil! Alokace registrů je téma, které se zcela věnuje výkonu. Jakmile máte první prototyp, který funguje a dělá, co má, začíná práce na výkonu. Musíte se naučit dobře měřit. Proč je to důležité? Pokud jsou k dispozici jasná data, můžete se podívat na různé části a vidět: aha, to tu pomohlo, ale tady se všechno rozpadlo! Objeví se nějaké dobré nápady, přidáte novou heuristiku a najednou všechno začne průměrně fungovat o něco lépe. Nebo nezačne. Měl jsem spoustu případů, kdy jsme se snažili o pět procent výkonu, které dělily naši implementaci od předchozího alokátoru. A pokaždé to vypadá takto: někde vyhrál, někde prohrál. Pokud máte dobré nástroje pro analýzu výkonu, můžete najít prohrávající nápady a pochopit, proč prohrávají. Možná stojí za to nechat všechno jak je, nebo se vážně pustit do ladění, nebo jít a opravit něco jiného. Je to celý soubor věcí! Udělal jsem tenhle skvělý hack, ale potřebujete i tenhle, a tenhle, a tenhle – a jejich celková kombinace přináší některá zlepšení. A ojedinělé nápady mohou selhat. Taková je povaha práce na výkonu NP-úplných úloh.
Vladimir: Mám pocit, že věci jako barvení v alokátorech jsou už vyřešené. No, pro vás vyřešené, soudě podle toho, co říkáte, tak má to vůbec smysl…
Cliff: Není to řešeno jako takové. To ty bys to měl proměnit na «řešené». Existují obtížné úkoly a je potřeba je řešit. Když je to hotovo, přichází čas na práci na výkonu. K této práci je třeba přistupovat odpovídajícím způsobem – dělat benchmarky, shromažďovat metriky, vysvětlovat situace, kdy při návratu k předchozí verzi tvůj starý hack opět začal fungovat (nebo naopak, přestal). A neustupovat, dokud něčeho nedosáhneš. Jak už jsem říkal, pokud jde o skvělé nápady, které nevyšly, ale v oblasti alokace registrů je jich zhruba nekonečné množství. Můžeš například číst vědecké publikace. Ačkoli se tato oblast nyní vyvíjí mnohem pomaleji a stává se jasnější než v době svoje mladosti. Nicméně, v této oblasti pracuje celá nekonečnost lidí a všechny jejich nápady stojí za zkoušku, všechny čekají na svůj čas. A nemůžeš říci, jak dobré jsou, pokud je nevyzkoušíš. Jak dobře se integrují se vším ostatním ve tvém alokátoru, protože alokátor dělá spoustu věcí, a nějaké nápady ve tvém konkrétním alokátoru nebudou fungovat, zatímco v jiném alokátoru – klidně. Hlavní způsob, jak vyhrát pro alokátor – je vytáhnout pomalé věci za hranice hlavní cesty a násilně je rozdělit na hranicích pomalých cest. Takže, pokud chceš spustit GC, jít po pomalé cestě, deoptimalizovat se, vyhodit výjimku, všechno v tomto duchu – víš, že tyto věci jsou relativně vzácné. A skutečně vzácné, zkontroloval jsem to. Děláš další práci a díky tomu zmizí spousta omezení na těch pomalých cestách, ale to není příliš důležité, protože jsou pomalé a málokdy se po nich chodí. Například nulový ukazatel – nikdy se to nestane, že? Je potřeba mít několik cest pro různé věci, ale neměly by se mísit na hlavní.
Vladimir: Co si myslíte o vícejádrovosti, když je jader hned tisíce? Je to užitečná věc?
Cliff: Úspěch GPU ukazuje, že je to docela užitečné!
Vladimir: Jsou celkem specializované. A co procesory všeobecného určení?
Cliff: To byla obchodní model Azul. Odpověď přišla ještě v éře, kdy lidé velmi oceňovali předvídatelný výkon. Tehdy bylo obtížné psát paralelní kód. Model kódování H2O se dobře škáluje, ale není to model pro obecné použití. Snad jen mírně obecnější ve srovnání s používáním GPU. Mluvíme o složitosti vývoje takové věci nebo o složitosti jejího používání? Například, zajímavou lekci mi dala Azul, docela nepatrnou: malé cache jsou v pořádku.
Největší výzva v životě
Vladimir: Co říkáte na netechnické výzvy?
Cliff: Největší výzvou bylo být… nezdvořilý a nesympatický k lidem. A jako důsledek jsem se neustále ocital v extrémně konfliktčních situacích. V těch, kde jsem věděl, že všechno jde špatně, ale nevěděl jsem, jak dál postupovat při řešení těchto problémů a nedokázal jsem si s nimi poradit. Mnoho dlouhotrvajících problémů, které trvaly desítky let, vzniklo právě takto. To, že v Javě existují kompilátory C1 a C2 – je přímým důsledkem toho. To, že v Javě po dobu deseti let nebylo vícestupňové kompilace – také přímý důsledek. Je zřejmé, že takový systém jsme potřebovali, ale není zřejmé – proč tady nebyl. Měl jsem problémy s jedním inženýrem… nebo skupinou inženýrů. Před mnoha lety, když jsem začal pracovat ve Sunu, jsem byl… No, ne jen tehdy, já měl vlastně vždycky svůj názor na všechno. A považoval jsem za pravdu, že stačí svou pravdu prostě říct na plnou hubu. Zvlášť když jsem byl šokujícím způsobem pravdivý většinu času. A pokud se ti takový přístup nelíbí… zejména pokud jsi zjevně na omylu a děláš hlouposti… Obecně, málo lidí dokázalo snášet takovou formu komunikace. I když někteří ano, například já. Celý svůj život jsem postavil na meritokratických principech. Pokud mi ukážete něco špatného, okamžitě se otočím a řeknu: řekl jsi nesmysl. Přitom se samozřejmě omlouvám, a tak dále, vyzdvihuji zásluhy, pokud nějaké jsou, a dělám další správné kroky. Na druhou stranu, jsem šokujícím způsobem pravdivý šokujícím velkým procentem celkového času. A to ve vztazích s lidmi nefunguje moc dobře. Nesnažím se být milý, ale kladu problém na stůl. „Tohle nikdy nebude fungovat, protože jedni, dva a tři.“ A oni: „Och!“. Byly i jiné důsledky, které by asi bylo lepší přeskočit: například vedly k rozvodu s manželkou a desítkovému období deprese po tom.
Výzva je boj s lidmi a jejich vnímáním toho, co můžete nebo nemůžete dělat, co je důležité a co není. Bylo mnoho výzev ohledně stylu kódování. Stále píšu hodně kódu, a tehdy jsem se musel dokonce zpomalit, protože jsem dělal příliš mnoho paralelních úkolů a dělal je špatně, místo abych se soustředil na jeden. Když se ohlédnu zpět, napsal jsem polovinu kódu týmu Java JIT, týmu C2. Následující nejrychlejší kodér psal polovinu tak rychle, další ještě pomaleji, a to bylo exponenciální pokles. Sedmý člověk v tomto řadu byl velmi, velmi pomalý – tak to vždycky bývá! Prošel jsem spoustou kódu. Sledoval jsem, kdo co píše, bez výjimek, zíral jsem na jejich kód, kontroloval každého z nich, a přesto jsem stále psal víc než kdokoliv z nich. S lidmi tento přístup nefunguje moc dobře. Někteří to nemají rádi. A když to nezvládají, začínají se objevovat všechny možné stížnosti. Například mi jednou řekli, abych přestal psát kód, protože píšu příliš mnoho kódu, a to ohrožuje tým, a mně to všechno znělo jako vtip: chlape, pokud celá zbývající parta zmizí a já budu pokračovat v psaní kódu, ztratíš jen polovinu týmu. Na druhou stranu, pokud budu pokračovat v psaní kódu a ty ztratíš polovinu týmu – to zní jako velmi špatný management. Nikdy jsem o tom moc nepřemýšlel, nikdy jsem to nezmiňoval, ale přesto to bylo někde v mé hlavě. V pozadí vědomí se mihotala myšlenka: „Co si děláte srandu?“. Tedy, největším problémem jsem byl já a mé vztahy s lidmi. Teď se lépe chápu, dlouho jsem byl team leader mezi programátory, a teď lidem říkám přímo: víš, jsem takový, jaký jsem, a budete si muset se mnou poradit – nevadí, když tu chvíli zůstanu? A když se s tím srovnali, všechno začalo fungovat. Ve skutečnosti nejsem ani špatný, ani dobrý, nemám žádné špatné úmysly nebo sobecké cíle, to je prostě moje podstata, a je třeba s tím nějak žít.
AndrejNedávno se všichni začali bavit o sebereflexi pro introverty a vůbec o soft skills. Co k tomu říct?
Cliff: Ano, to byla porozumění a lekce, kterou jsem si odnesl z rozvodu s manželkou. Co jsem si z rozvodu vzal – je to porozumění sobě samému. Tak jsem začal chápat ostatní lidi. Chápat, jak toto interakce funguje. To vedlo k objevům jeden za druhým. Objevilo se vědomí, kdo jsem a co ze sebe představují. Co dělám: buď jsem zaneprázdněn úkoly, nebo se vyhýbám konfliktu, nebo něco jiného – a taková úroveň sebereflexe opravdu pomáhá udržet se na uzdě. Poté vše jde mnohem snáze. Jedna věc, kterou jsem zjistil nejen u sebe, ale i u ostatních programátorů – neschopnost verbalizovat myšlenky, když jsi ve stavu emocionálního stresu. Například sedíš a kóduješ, jsi v módu flow a najednou k tobě někdo přiběhne a začne v hysterii křičet, že se něco pokazilo, a teď ti hrozí extrémní opatření. A ty nemůžeš říct ani slovo, protože jsi v emocionálním stresu. Získané znalosti ti umožňují se na tento moment připravit, prožít ho a přejít k plánu ústupu, po kterém můžeš něco udělat. Takže ano, když začneš chápat, jak to celé funguje – je to obrovská životní událost.
Sám jsem nedokázal najít ta správná slova, ale zapamatoval jsem si sekvenci akcí. Podstatou je, že tato reakce je stejně fyzická, jako je verbální, a potřebuješ prostor. Takový prostor, v zenovém smyslu. To je právě to, co je třeba vysvětlit, a pak se hned stáhnout – čistě fyzicky ustoupit. Když mlčím, mohu zpracovat situaci v emocích. Jakmile adrenalin dorazí do mozku a přepne tě do módu „boj nebo útěk“, už nemůžeš nic říct, ne – teď jsi idiot, inženýr pro bití, neschopný adekvátní reakce nebo alespoň zastavit útok, a útočník může znovu a znovu útočit. Nejdříve se musíš znovu stát sám sebou, získat kontrolu, vystoupit z módu „boj nebo útěk“.
A právě k tomu je potřeba verbální prostor. Prostě volný prostor. Pokud už vůbec něco říkat, můžete říct přesně tohle, a pak si jít skutečně najít "prostor": jít se projít po parku, zamknout se v koupelně – to je jedno. Hlavní je dočasně se odpojit od té situace. Jakmile se na několik sekund odpojíš, kontrola se vrací, začínáš myslet střízlivě. "Dobře, já přece nejsem nějaký idiot, nedělám hloupé věci, jsem docela užitečný člověk." Jakmile se ti podaří přesvědčit sám sebe, přichází čas na další krok: pochopit, co se stalo. Byl jsi napaden, útok přišel odnikud, to byla nečestná, podlá past. To je špatně. Další krok – pochopit, proč to útočníkovi bylo potřeba. Opravdu, proč? Možná proto, že je sám v zuřivosti? Proč je v zuřivosti? Například proto, že sám selhal a nemůže přijmout zodpovědnost? Takto bys měl pečlivě zpracovat celou situaci. Ale k tomu potřebuješ prostor pro manévrování, verbální prostor. První krok – přerušit verbální kontakt. Ujít od slovního projednávání. Zrušit ho, odejít co nejrychleji. Pokud je to telefonní hovor – prostě polož sluchátko – to je dovednost, kterou jsem získal z komunikace s bývalou manželkou. Pokud rozhovor nikam nevede, prostě řekni "sbohem" a zavěs. Na druhé straně sluchátka: "bla-bla-bla", ty odpovídáš: "aha, čau!" a pokládáš sluchátko. Prostě přerušíš rozhovor. O pět minut později, když se ti vrací schopnost racionálně myslet, trošku jsi se uklidnil, stává se možné promyslet, co se vlastně stalo a co bude dál. A začít formulovat promyšlenou odpověď, místo abych jen reagoval na emoce. Pro mě bylo prozření v sebereflexi právě to, že v případě emocionálního stresu nemohu mluvit. Vystoupit z tohoto stavu, promyslet a naplánovat, jak odpovědět a kompenzovat problémy – to jsou správné kroky, když nemůžeš mluvit. Nejjednodušší způsob je utéct ze situace, ve které se projevuje emocionální stres, a jednoduše přestat se na tomto stresu podílet. Poté získáš schopnost myslet, když můžeš myslet, objeví se možnost mluvit, a tak dále.
Mimochodem, advokát protistrany se pokouší to s tebou dělat – nyní už je jasné, proč. Protože má možnost tě potlačit do takového stavu, že nebudeš moci ani vyslovit své jméno, například. V doslovném smyslu, nebudeš moci mluvit. Pokud se ti to děje a pokud víš, že se ocitneš na místě, kde se vedou slovní bitvy, na místě jako je soud, můžeš přijít se svým právníkem. Právník tě zastoupí a ukončí slovní útok, a udělá to zcela legálním způsobem, a k tobě se vrátí ztracené zenové prostor. Například, párkrát jsem musel zavolat rodině, soudce k tomu přistupoval celkem přátelsky, ale advokát protistrany na mě pořád křičel a křičel, ani jsem nemohl promluvit. V takových případech pro mě nejlépe funguje využití zprostředkovatele. Zprostředkovatel ukončuje veškerý ten tlak, který na tebe neustále působí, znovu objevuješ potřebné zenové prostor, spolu s ním se vrací schopnost mluvit. Je to celá oblast znalostí, ve které je třeba se hodně učit, hodně objevovat uvnitř sebe, a to se promění v vysokou úroveň strategických rozhodnutí, která jsou různá pro různé lidi. Někdo tyto výše popsané problémy nemá, obvykle je nemají lidé, kteří se profesionálně zabývají prodejem. Všichni ti, kteří si vydělávají na živobytí slovy – známí zpěváci, básníci, náboženští vůdci a politici, ti mají vždy co říct. Ti nemají takové problémy, ale já je mám.
Andrej: To bylo… nečekané. Skvělé, už jsme toho dost probrali a je čas tohle interview ukončit. Určitě se setkáme na konferenci a budeme moci tento dialog pokračovat. Uvidíme se na Hydra!
Komunikaci s Cliffem bude možné pokračovat na konferenci Hydra 2019, která se uskuteční 11.-12. července 2019 v Petrohradě. Přijede s prezentací . Vstupenky si můžete zakoupit .
Zdroj: habr.com
