Veliki intervju s Cliffom Clickom – ocem JIT kompajliranja u Javi

Veliki intervju s Cliffom Clickom – ocem JIT kompajliranja u JaviCliff Click — CTO of Cratus (IoT sensors for process enhancement), 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 age 15 (Pascal for the 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 could produce quality code, which was one of the factors in establishing Java as one of the major modern software platforms. Later, Cliff helped Azul Systems build an 864-core mainframe with software entirely in Java, which managed GC pauses on a 500-gigabyte heap within 10 milliseconds. Overall, Cliff has worked on all aspects of the JVM.

 
This HubPost is a large interview with Cliff. We will discuss the following topics:

  • Transitioning to low-level optimizations
  • How to perform large refactoring
  • Cost model
  • Learning about low-level optimizations
  • Practical examples of performance improvement
  • Why create your own programming language
  • Career of a performance engineer
  • Technical challenges
  • A bit about register allocation and multithreading
  • The biggest challenge in life

The interviewers are:

  • Andrey Sataryn from Amazon Web Services. In his career, he has worked on a variety of projects: testing a NewSQL distributed 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 worked for ten years on the performance and scalability of NetCracker OS — software used by telecommunications operators to automate network management processes and network equipment. He is passionate about Java and Oracle Database performance. Author of over a dozen performance improvements in the official PostgreSQL JDBC driver.

Transitioning to low-level optimizations

Andrey: You are a well-known figure in the world of JIT compilation, in Java, and in performance work overall, right? 

Cliff: That's right!

Andrey: Let's start with general questions about performance work. What do you think about choosing between high-level and low-level optimizations like working at the CPU level?

Cliff: Ovdje je sve jednostavno. Najbrži kod je onaj koji se nikada ne pokreće. Zato treba uvijek početi s visoke razine, raditi na algoritmima. Bolja O-oznaka pobijedit će lošiju O-oznakui, osim ako se ne uplete neka dovoljno velika konstanta. Niskonivojski aspekti dolaze tek na kraju. Obično, ako si dovoljno dobro optimizirao ostatak stoga, a još uvijek ostane nešto zanimljivo – to je niska razina. No, kako početi s visoke razine? Kako znati da je učinjeno dovoljno posla na visokoj razini? Pa... nikako. Nema gotovih recepata. Trebaš razumjeti problem, odlučiti što planiraš napraviti (da ne poduzimaš nepotrebne korake), i tek tada možeš izvaditi profajler koji može reći nešto korisno. U nekom trenutku sam shvatiš da si se oslobodio nepotrebnog i da je došlo vrijeme za finu prilagodbu niske razine. To je definitivno posebna vrsta umjetnosti. Mnogi ljudi rade nepotrebne stvari, ali se kreću tako brzo da nemaju vremena brinuti se o performansama. No to je sve dok pitanje ne postane hitno. Obično 99% vremena nikome nije zanimljivo čime se bavim, sve do trenutka kada se na kritičnom putu ne pojavi važna stvar koja nekome postane bitna. I tada svi počinju pitati zašto to od početka nije radilo savršeno. Ukratko, uvijek postoji nešto što se može poboljšati u performansama. No 99% vremena nemaš tragove! Samo pokušavaš natjerati nešto da radi i tijekom toga shvatiš što je važno. Nikada ne možeš unaprijed znati da taj komadić treba biti savršen, stoga, u suštini, moraš biti savršen u svemu. A to je nemoguće i ti to ne radiš. Uvijek ima puno stvari koje treba popraviti – i to je sasvim normalno.

How to perform large refactoring

Andrey: Kako radite na performansama? To je usputna problematika. Na primjer, jeste li ikada radili na problemima koji nastaju zbog preklapanja velikog broja već postojećih funkcionalnosti?

Cliff: Pokušavam to izbjeći. Ako znam da će performanse postati problem, razmišljam o tome prije nego što počnem kodirati, posebno o strukturama podataka. No, često otkrijete sve to vrlo kasno. Tada se morate poslužiti drastičnim mjerama i raditi ono što ja nazivam «prepisuj i vladaj»: trebate obuhvatiti dovoljno veliki dio. Dio koda ćete ionako morati prepisati zbog problema s performansama ili nečeg drugog. Koji god razlog za prepisivanje koda bio, gotovo uvijek je bolje prepisati veći dio nego manji. U tom trenutku svi počnu drhtati od straha: «o bože, ne možemo dirati toliko koda!». No, zapravo, takav pristup gotovo uvijek bolje funkcionira. Treba odmah uhvatiti veliki problem, okružiti ga velikim krugom i reći: sve unutar kruga, ja ću prepisati. Granica je svakako mnogo manja od sadržaja unutar nje koji podliježe promjeni. I ako takvo ocrtavanje granica omogućava da se rad unutar njega odradi savršeno – imate slobodne ruke, radite što god želite. Čim shvatite problem, proces prepisivanja ide mnogo lakše, stoga grizite veći komad!
U isto vrijeme, kada radite prepisivanje velikog dijela i shvatite da će performanse postati problem, možete odmah početi brinuti o tome. Obično to postaje jednostavne stvari poput «ne kopirajte podatke, upravljajte podacima što je jednostavnije moguće, učinite ih manjima». U velikim prepisivanjima, postoje standardni načini za poboljšanje performansi. I oni se gotovo uvijek vrte oko podataka.

Cost model

Andrey: U jednom od podkasta govorili ste o modelima cijena u kontekstu performansi. Možete li objasniti što ste time mislili?

Cliff: Naravno. Rođen sam u doba kada je performansa procesora bila izuzetno važna. I ta era se ponovo vraća – sudbina nije lišena ironije. Počeo sam živjeti u vremenu osmobitnih mašina, moj prvi računar radio je s 256 bajtova. Baš bajtova. Sve je bilo jako malo. Trebalo je brojati instrukcije i čim smo počeli napredovati kroz stek programskih jezika, jezici su preuzimali sve više. Bio je Assembler, zatim Basic, pa C, a C je preuzimao rad s mnogim detaljima, poput raspodele registara i pronalaska instrukcija. Ali sve je tu bilo prilično jasno i ako sam napravio pokazivač na instancu promenljive, dobijam load, a trošak te instrukcije je poznat. Hardver izbacuje poznat broj mašinskih ciklusa, tako da se brzina izvršavanja različitih stvari može izračunati jednostavnim sabiranjem svih instrukcija koje planiraš pokrenuti. Svaki compare/test/branch/call/load/store mogao se sabrati i reći: evo ga vreme izvršavanja. Kada se baviš poboljšanjem učinka, sigurno ćeš obratiti pažnju na to koji brojevi odgovaraju sitnim vrućim ciklusima. 
Ali čim se prebaciš na Java, Python i slične stvari, jako brzo se udaljavaš od niskonivojskog hardvera. Koliki je trošak pozivanja gettera u Javi? Ako JIT u HotSpot sve ispravno ubacilo, to će biti load, ali ako to nije učinio – biće to poziv funkcije. Budući da poziv leži u vrućem ciklusu, on će poništiti sve druge optimizacije u tom ciklusu. Stoga će stvarni trošak biti mnogo veći. I odmah gubiš sposobnost gledanja na deo koda i razumevanja šta nam se isplati izvršiti u terminima taktne frekvencije procesora, korišćene memorije i keša. Sve to postaje zanimljivo samo kada se zaista uđe u performanse.
Trenutno smo u situaciji u kojoj brzine procesora gotovo već desetljećima ne rastu. Stari dani se vraćaju! Ne možete više računati na dobru jednonitnu izvedbu. No, ako se odlučite za paralelno računanje – to je nevjerojatno složeno, svi vas gledaju kao da ste James Bond. Desetostruka ubrzanja javljaju se tamo gdje je netko nešto propustio. Paralelizam zahtijeva puno posla. Da biste dobili to desetostruko ubrzanje, morate razumjeti model troškova. Što i koliko košta. A za to morate shvatiti kako se jezik uklapa u donji hardver.
Martin Thompson pronašao je odličnu riječ za svoj blog Mehanička simpatija! Potrebno je razumjeti što hardver pokušava napraviti, kako točno to radi i zašto uopće radi ono što radi. Iskorištavajući to, prilično je lako početi brojati instrukcije i otkrivati gdje vrijeme izvođenja odlazi. Ako nemate odgovarajuću pripremu, samo tražite crnu mačku u mračnoj sobi. Neprestano viđam ljude koji optimiziraju performanse, a nemaju pojma što uopće rade. Veoma se muče i ne napreduju. A kada uzmem isti komadić koda, dodam nekoliko sitnih trikova i dobijem petostruko ili desetostruko ubrzanje, oni kažu: pa, to nije pošteno, znali smo da si bolji. Fascinantno. O čemu sam to… model troškova je o tome kakav kod pišete i koliko brzo prosječno radi u cjelokupnoj slici.

Andrey: I kako zadržati takav obujam u glavi? To se postiže velikim iskustvom, zar ne? Gdje se stječe takvo iskustvo?

Cliff: Pa, svoje iskustvo stekao sam na način koji nije bio baš jednostavan. Programirao sam u Assembleru u vrijeme kada se moglo razumjeti svaka pojedinačna instrukcija. Zvuči glupo, ali od tada mi u glavi, u pamćenju, zauvijek ostao niz instrukcija Z80. Ne sjećam se imena ljudi već minutu nakon razgovora, ali se sjećam koda napisanog prije 40 godina. Smiješno, izgleda kao sindrom „naučnika idiota».

Learning about low-level optimizations

Andrey: Postoji li jednostavniji način da se uđe u to?

Cliff: Da i ne. Hardver koji svi koristimo se u to vrijeme nije mnogo promijenio. Svi koriste x86, osim pametnih telefona koji koriste Arm. Ako se ne baviš nekim hardcore ugrađenim sistemima, imaš sve isto. Dobro, idemo dalje. Upute se također već dugo nisu mijenjale. Potrebno je otići i napisati nešto na Assembleru. Malo, ali dovoljno da počneš razumjeti. Smiješite se, ali ja govorim sasvim ozbiljno. Treba razumjeti odnos jezika i hardvera. Nakon ovoga trebaš otići, napisati malo i napraviti mali igraći kompajler za mali igraći jezik.
Odakle uzeti model cijene? Pa, možeš pročitati nešto, naravno... Ali mislim da je najbolji način djelovati. Napraviti mali kompajler i to će biti najbolji način da razumiješ model cijene i smjestiš ga u vlastitu glavu. Mali kompajler koji bi bio koristan za programiranje mikrovalne pećnice – to je zadatak za početnike. Mislim da ako već imaš programerske vještine, trebao bi biti sposoban. Sve te stvari poput parsiranja stringa koji će biti nekakva algebarska izraza, izvući upute matematičkih operacija u pravom redoslijedu, uzeti točne vrijednosti iz registara – sve to se radi lako. I dok to radiš, odjavit će se u mozgu. Mislim da svi znaju čime se bavi kompajler. I to će dati razumijevanje modela cijene.

Practical examples of performance improvement

Andrey: Na što još treba obratiti pažnju pri radu na performansama?

Cliff: Strukture podataka. Usput, dugo nisam vodio te lekcije… Rocket School. Bilo je zabavno, ali zahtijevalo je mnogo truda, a imam i svoj život! U redu. Dakle, na jednom od velikih i zanimljivih predavanja, „Gdje odlazi vaš performans“, dao sam studentima primjer: dva i pol gigabajta fintech podataka čitana iz CSV datoteke, a onda je trebalo izračunati broj prodanih proizvoda. Obični tiker tržišni podaci. UDP paketi, pretvoreni u tekstualni format, od 70-ih. Chicago Mercantile Exchange – razne stvari poput nafte, kukuruza, soje, itd. Trebalo je prebrojati te proizvode, broj transakcija, prosječni iznos kretanja sredstava i roba, itd. To je prilično jednostavna trgovačka matematika: pronaći kod proizvoda (to je 1-2 znaka u hash tablici), dobiti iznos, dodati ga u jedan od setova transakcija, dodati volumen, dodati cijenu, i nekoliko drugih stvari. Vrlo jednostavna matematika. Igračka implementacija bila je vrlo jednostavna: sve je bilo u datoteci, čitam datoteku i krećem se kroz nju, dijeleći pojedinačne zapise na Java stringove, tražim potrebne stvari i zbrajam prema gore opisanoj matematici. I to radi s nekakvom malom brzinom.

S ovim pristupom sve je jasno što se događa, a paralelno računarstvo ovdje ne pomaže, zar ne? Ispostavilo se da se petostruko povećanje performansi može postići samo odabirom pravih struktura podataka. To čak iznenađuje i iskusne programere! U mom konkretnom slučaju, fokus je bio na tome da ne trebamo alocirati memoriju unutar vruće petlje. Pa, to nije cijela istina, ali u suštini - ne treba alocirati "jednom u X", kada je X dovoljno velik. Kada je X dva i pol gigabajta, ne treba alocirati ništa "jednom za slovo", "jednom za redak", ili "jednom za polje", ništa slično. Na to se troši vrijeme. Kako to uopće funkcionira? Zamislite da radim poziv String.split() ili BufferedReader.readLine(). Readline stvara niz od niza bajtova koji dolaze putem mreže, jednom za svaki redak, za svaki od stotina milijuna redaka. Uzimam ovaj redak, analiziram ga i odbacujem. Zašto ga odbacujem - pa, već sam ga obradio, to je to. Tako da, za svaki bajt pročitanih iz tih 2.7G, zabilježit će se dva znaka u nizu, što znači već 5.4G, i više mi nisu potrebni, pa se odbacuju. Ako pogledamo propusnost memorije, učitavamo 2.7G, koje prolaze kroz memoriju i memorijsku sabirnicu u procesoru, a zatim se dvostruko više šalje u niz koji leži u memoriji, i sve se to obrađuje pri kreiranju svake nove niza. Ali moram ga pročitati, hardver ga čita, čak i ako će kasnije sve biti obrađeno. I moram ga zapisati jer sam stvorio niz, a cache-vi su se napunili - cache ne može primiti 2.7G. U konačnici, za svaki pročitani bajt, čitam još dva dodatna bajta i zapisujem dva dodatna bajta, i na kraju to ima omjer 4:1 - u takvom omjeru bespotrebno trošimo propusnost memorije. A onda se ispostavi da ako radim String.split() – to radim daleko od posljednjeg puta, unutra može biti još 6-7 polja. Stoga, klasični kod za čitanje CSV s kasnijim analizom redaka dovodi do gubitaka propusnosti memorije oko 14:1 u odnosu na ono što zapravo želite imati. Ako se odbace te alokacije, može se postići petostruko ubrzanje.

I to nije nešto što je posebno teško. Ako pogledate kod iz pravog ugla, sve postaje prilično jednostavno, čim shvatite suštinu problema. Ne treba prestati s alokacijom memorije: problem je u tome što nešto alocirate, a ono odmah umire, pri čemu sagorijeva važan resurs, a to je u ovom slučaju propusnost memorije. Sve to rezultira padom performansi. Na x86 obično je potrebno aktivno trošiti takte procesora, dok ste ovdje sagorjeli svu memoriju mnogo ranije. Rješenje je smanjiti broj alokacija. 
Drugi deo problema je u tome što, kada pokrenete profajler, kada se ispuni memorijska linija, u trenutku kada se to događa, obično čekate povratak keša, jer je pun smeća koje ste upravo stvorili, svim tim redovima. Stoga svaka operacija učitavanja ili spremanja postaje spora, jer dovodi do promašaja u kešu – ceo keš postaje spor, čekajući da smeće nestane. Zato profajler obično samo pokazuje topli slučajni šum, razvučen duž celog ciklusa – neće biti nikakve posebno vruće instrukcije ili mesta u kodu. Samo šum. I ako pogledate GC petlje, sve će biti iz Young Generation i super brze – mikrosekunde ili milisekunde maksimalno. Jer cela ta memorija umire trenutno. Alocirate milijarde gigabajta, i on ih seče, i seče, i ponovo seče. Sve se to događa vrlo brzo. Dakle, imamo jeftine GC petlje, topli šum duž celog ciklusa, ali želimo dobiti petostruko ubrzanje. U tom trenutku nešto u glavi treba da se zatvori i zazvoni: „zašto je to tako?!“. Preopterećenje memorijske linije ne prikazuje se u klasičnom debageru, potrebno je pokrenuti debager hardverskih brojača performansi i videti to sami i direktno. A indirektno se to može posumnjati iz ova tri simptoma. Treći simptom je kada gledate šta alocirate, pitate profajlera, a on odgovara: „Napravio si milijardu redova, ali GC je radio besplatno“. Kada se to desi, shvatite da ste stvorili previše objekata i sagoreli celu memorijsku liniju. Postoji način da se to razjasni, ali nije očigledan. 

Problem u strukturi podataka: gole strukture koje leže iza svega što se događa, prevelike su, zauzimaju 2.7G na disku, pa je izuzetno nepoželjno praviti kopiju ovog sistema – želimo ga učitati iz mrežnog bajtnog bafera odmah u registre, kako ne bismo čitali i pisali u red bezbroj puta. Nažalost, Java prema zadanim postavkama ne nudi takvu biblioteku unutar JDK-a. Ali to je trivijalno, zar ne? U osnovi, to su 5-10 redaka koda potrebnih za implementaciju vlastitog baferiranog učitača linija, koji oponaša ponašanje klase String, dok je zapravo omotač oko donjeg bajtnog bafera. Kao rezultat, radiš gotovo kao da radiš sa stringovima, ali u stvarnosti se pomiču pokazivači na bafer, a sirovi bajtovi se nigdje ne kopiraju, čime se ponovo koriste isti baferi, iznova i iznova, a operacijski sustav je sretan da preuzme stvari za koje je namijenjen, poput skrivene dvostruke baferizacije tih bajtnih bafera, dok više ne mlatiš beskrajan tok nepotrebnih podataka. Usput, shvaćate li, pri radu s GC-om, da je zajamčeno da svaka alokacija memorije neće biti vidljiva procesoru nakon posljednjeg ciklusa GC-a? Stoga, ništa od ovoga ne može biti u cache-u, i desi se 100%-tno zajamčen promašaj. Pri radu s pokazivačem, na x86 čitanje registra iz memorije traje 1-2 ciklusa, i čim se to dogodi, plaćaš, plaćaš, plaćaš, jer je cijela memorija na NINE cache-ima – i to je pravi trošak alokacije memorije. Pravi trošak.

Drugim riječima, strukture podataka su ono što se najteže mijenja. I čim shvatite da ste odabrali pogrešnu strukturu podataka koja će kasnije uništiti performanse, obično je potrebno uložiti značajan trud, no ako to ne učinite, stvari će postati još gore. Prije svega, treba razmišljati o strukturama podataka, to je važno. Glavni trošak dolazi od masivnih struktura podataka koje počinjemo koristiti na način 'kopirao sam strukturu podataka X u strukturu podataka Y, jer mi se Y više sviđa vizualno.' No operacija kopiranja (koja se čini jeftinom) zapravo troši memorijsku propusnost i to je mjesto gdje se skriva sve izgubljeno vrijeme izvođenja. Ako imam ogromnu JSON string i želim je pretvoriti u strukturirano DOM stablo iz POJO ili nečega sličnog, operacija parsiranja te string i izgradnje POJO, a zatim novo pozivanje POJO kasnije pretvorit će se u dodatne troškove - to nije jeftino. Osim ako ne planirate koristiti POJO mnogo češće nego string. U prvo vrijeme, umjesto toga, možete pokušati dešifrirati string i izvući iz njega samo ono što vam treba, ne pretvarajući ga u POJO. Ako se sve to događa na putu gdje su potrebne maksimalne performanse, zaboravite na POJO - trebate se direktno baviti stringom.

Why create your own programming language

Andrey: Rekli ste da za razumijevanje modela troškova, trebate napisati svoj mali jezik…

Cliff: Ne jezik, već kompajler. Jezik i kompajler su različite stvari. Najveća razlika je u vašoj glavi. 

Andrey: Usput, koliko znam, eksperimentirate s kreiranjem vlastitih jezika. Zašto?

Cliff: Zato što mogu! Polu sam u penziji, tako da je ovo moj hobi. Celu sam život realizovao tuđe jezike. Takođe sam puno radio na stilu kodiranja. A i zato što vidim probleme u drugim jezicima. Vidim da postoje bolje načine da radite uobičajene stvari. I želeo bih ih iskoristiti. Samo sam se umorio od gledanja problema u sebi, u Javi, u Pythonu, u bilo kom drugom jeziku. Trenutno pišem na React Native, JavaScript i Elm kao hobi koji nije o penziji, već o aktivnom radu. I pišem i na Pythonu, i verovatno ću nastaviti da radim na mašinskom učenju za Java backendove. Postoji mnogo popularnih jezika i svi imaju zanimljive osobine. Svaki je dobar na svoj način i možete pokušati da spojite sve te karakteristike. Tako da se bavim istraživanjem zanimljivih stvari za mene, ponašanjem jezika, pokušavam da smišljam razumnu semantiku. I za sada ide! Trenutno se borim sa semantikom memorije, jer želim da je imam kao u C i Javi, i da dobijem snažan model memorije i semantiku za učitavanja i skladištenja. U isto vreme da imam automatsko određivanje tipova kao u Haskelu. Tako da pokušavam da pomešam Haskell-sličnu automatsku dedukciju tipova sa memorijom koja funkcioniše kao u C i Javi. Time sam se bavio poslednja 2-3 meseca, na primer.

Andrey: Ako gradite jezik koji uzima najbolje aspekte iz drugih jezika, da li ste razmišljali da bi neko mogao da uradi obrnuto: uzme vaše ideje i koristi ih kod sebe?

Cliff: Upravo tako se pojavljuju novi jezici! Zašto je Java slična C-u? Zato što je C imao dobru sintaksu koju su svi razumeli, a Java se inspirisala tom sintaksom, dodavši bezbednost tipova, provere granica nizova, GC, a takođe su poboljšali neke stvari iz C-a. Dodali su svoje. Ali su se prilično snažno inspirisali, zar ne? Svi stojimo na plećima divova koji su bili pre nas – tako se ostvaruje napredak.

Andrey: Kako razumem, vaš jezik će biti bezbedan kada je u pitanju korišćenje memorije. Da li ste razmišljali o implementaciji nečega poput borrow checkera iz Rusta? Da li ste ga gledali, kako vam se dopada?

Cliff: Pa, pišem u C-u već cijelu vječnost, s tim svim malloc i free, i ručno upravljam vremenom života. Znate, 90-95% ručno upravljanog životnog ciklusa ima istu strukturu. I jako, jako je bolno baviti se tim ručno. Volio bih da kompajler jednostavno kaže što se događa i što ste postigli sa svojim radnjama. Za neke stvari, borrow checker to radi automatski. Također bi trebao automatski pružati informacije, sve razumjeti i čak me ne opterećivati time da to razumijevanje izložim. Trebao bi raditi barem lokalnu analizu eskapiranja, i samo ako to ne uspije, tada bi trebalo dodavati anotacije tipova koje će opisivati životni ciklus – a takav sustav je daleko složeniji od borrow checkera, ili uopće bilo kojeg postojećeg provjeravača memorije. Izbor između „sve je u redu“ i „nisam ništa razumio“ — ne, trebao bi postojati nešto bolje. 
Kao netko tko je napisao puno koda na C-u, vjerujem da je podrška za automatsko upravljanje vremenom trajanja (lifetime) najvažnija stvar. Također, umoran sam od ogromne potrošnje memorije u Javi, a glavni problem je u GC-u. Kada alociraš memoriju u Javi, ne vraćaš memoriju koja je bila lokalna u posljednjem ciklusu GC-a. U jezicima s točnijim upravljanjem memorijom to nije slučaj. Ako pozoveš malloc, odmah dobijaš memoriju koja je obično upravo korištena. Obično radiš neke privremene stvari s memorijom i odmah je vraćaš. I ona se odmah vraća u malloc pool, a sljedeći malloc ciklus ponovno je izvlači. Dakle, stvarna upotreba memorije smanjuje se na skup aktivnih objekata u određenom trenutku, plus curenja. Ako ti ne curi sve na posve neprimjeren način, većina memorije završi u cache-evima i procesoru, i to radi brzo. No, zahtijeva puno ručnog upravljanja memorijom putem malloc i free, pozvanih u pravom redu, na pravom mjestu. Rust može sam ispravno upravljati s tim, a u mnogim slučajevima dati čak i bolju izvedbu, jer se potrošnja memorije sužava samo na trenutne izračune – nasuprot čekanju sljedećeg GC ciklusa koji će osloboditi memoriju. Na kraju, dobili smo vrlo zanimljiv način za poboljšanje izvedbe. I prilično moćan – mislim, bavio sam se takvim stvarima prilikom obrade podataka za finteh, i to je omogućilo ubrzanje od otprilike pet puta. To je prilično veliko ubrzanje, posebno u svijetu gdje procesori ne postaju brži, a mi i dalje čekamo poboljšanja.

Career of a performance engineer

Andrey: Također bih želio malo pitati o karijeri u cjelini. Postali ste poznati zahvaljujući radu na JIT-u u HotSpot-u, a zatim ste prešli u Azul – koji je također JVM tvrtka. No, više ste se bavili hardverom nego softverom. I onda ste iznenada prešli na Big Data i Machine Learning, a potom na otkrivanje prijevara. Kako se to dogodilo? To su vrlo različita područja razvoja.

Cliff: Dugo se bavim programiranjem i imao sam prilike raditi na raznim projektima. Kada ljudi kažu: „Oh, ti si onaj koji je radio JIT za Java!”, uvijek mi je smiješno. Prije toga bavim se klonom PostScript-a – jezika koji je Apple nekada koristio za svoje laserske pisače. A prije toga sam implementirao jezik Forth. Mislim da je zajednička tema za mene – razvoj alata. Cijeli život radim alate pomoću kojih drugi ljudi pišu svoje fantastične programe. Također sam se bavio razvojem operativnih sustava, drajvera na razini jezgre, jezika za razvoj OS-a, koji su započeli trivijalno, ali su s vremenom postajali sve složeniji. No, osnovna tema ostaje – razvoj alata. Velik dio mog života prošao je između Azula i Suna, i taj dio bio je vezan uz Java. Kada sam se upustio u Big Data i Strojno Učenje, ponovno sam stavio svoj elegantni šešir i rekao: „Oh, sada imamo netrivijalni problem, a ovdje se događa mnogo zanimljivih stvari i ljudi koji nešto rade”. To je odličan put za razvoj kroz koji vrijedi proći.

Da, vrlo volim distribuirano računarstvo. Moj prvi posao bio je tijekom studija na C-u, na reklamnom projektu. To je bilo distribuirano računarstvo na Zilog Z80 čipovima, koji su prikupljali podatke za analogno optičko prepoznavanje teksta, koje je provodio pravi analogni analizator. Bila je to nevjerojatna i potpuno neobična tema. No, bilo je poteškoća, neki dijelovi nisu se ispravno prepoznavali, pa je bilo potrebno izvući sliku i pokazati je osobi koja je već pročitala očima i javila što se tamo nalazi, i zbog toga su postojali poslovi s podacima, a ti su poslovi imali svoj vlastiti jezik. Postojala je pozadina koja je sve to obrađivala - paralelno radili su Z80 s pokrenutim vt100 terminalima - jedan za svaku osobu, a bila je i paralelna programska paradigma na Z80. Postojao je neki zajednički komad memorije koji su dijelili svi Z80 unutar konfiguracije tipa 'zvijezda'; dijelili su se i backplane, a polovica RAM-a dijelila se unutar mreže, dok je druga polovica bila privatna ili odlazila na nešto drugo. Smisleno složen paralelni distribuirani sustav s dijeljenom... polu-dijeljenom memorijom. Kada se to dogodilo... Već ni ne pamtim, negdje sredinom 80-ih. Prilično davno. 
Da, možemo smatrati da je 30 godina dovoljno dugo. Zadatci povezani s distribuiranim računarstvom postoje već dovoljno dugo, ljudi su odavno ratovali s Beowulf-klasterima. Takvi klasteri izgledaju kao… Na primjer: postoji Ethernet i vaš brzi x86 je povezan na taj Ethernet, i sada želite dobiti lažnu dijeljenu memoriju, jer nitko tada nije mogao raditi na kodiranju distribuiranih izračuna, to je bilo prekomplicirano, i zato je postojala lažna dijeljena memorija sa zaštitom stranica memorije na x86, i ako ste pisali u tu stranicu, govorili smo ostalim procesorima da ako dobiju pristup istoj dijeljenoj memoriji, trebaju je učitati od vas, i tako se pojavila neka vrsta protokola podrške za koherentnost predmemorija i softvera za to. Zanimljiva koncept. Pravi problem, naravno, bio je u nečemu drugom. Sve je to radilo, ali ste brzo počeli dobivati probleme s performansama, jer nitko nije razumio model performansi na dovoljno dobrom nivou – koji su obrasci pristupa memoriji, kako učiniti da čvorovi beskonačno ne pingaju jedni druge, i tako dalje.

U H2O sam smislio ovo: sami developeri su odgovorni za to da odrede gde postoji paralelizam, a gde ne. Stvorio sam model kodiranja koji čini pisanje visokoperformantnog koda jednostavnim. Međutim, napisati spor kod je otežano; izgledat će loše. Potrebno je dosta truda da se napiše spor kod, koristiće se nekonvencionalne metode. Sporan kod može se uočiti već na prvi pogled. Kao posledica, obično se piše kod koji radi brzo, ali morate se nositi s pitanjem kako upravljati deljenom memorijom. Sve se to vrti oko velikih nizova, a ponašanje je slično nevolatilnim velikim nizovima u paralelnoj Javi. Zamislite da dva niti pišu u paralelni niz, jedan od njih dobija, a drugi gubi, i ne znate ko je ko. Ako nisu volatilni, redosled može biti bilo kakav - i to zaista dobro funkcioniše. Ljudi se zaista brinu o redosledu operacija, pravilno postavljaju volatile i očekuju probleme s performansama povezane s memorijom na pravim mestima. U suprotnom, jednostavno bi pisali kod u obliku petlji od 1 do N, gde je N neki trilijun, nadajući se da će svi složeni slučajevi automatski postati paralelni - a to tamo ne funkcioniše. Ali u H2O to nije ni Java ni Scala, može se smatrati 'Java minus minus', ako želite. To je veoma razumljiv stil programiranja i podseća na pisanje jednostavnog koda u C ili Javi sa petljama i nizovima. Ali pri tome se memorija može obrađivati terabajtima. I dalje koristim H2O. Povremeno ga koristim u različitim projektima - i to je i dalje najbrža stvar, deset puta brža od konkurenata. Ako radite s velikim podacima u kolumnarnim formatima, vrlo je teško nadmašiti H2O.

Technical challenges

Andrey: Koji je bio vaš najveći izazov tokom karijere?

Cliff: Da li razgovaramo o tehničkom ili ne tehničkom delu pitanja? Rekao bih da su najveći izazovi - ne tehnički. 
Što se tiče tehničkih izazova. Jednostavno sam ih prevladao. Čak ni ne znam koji je bio najveći, ali bilo je nekoliko prilično zanimljivih, koje su zauzele dosta vremena i mentalne borbe. Kada sam otišao u Sun, bio sam siguran da ću napraviti brzi kompajler, dok su mnogi seniori govorili da mi nikada neće uspjeti. Ali krenuo sam tim putem, napisao kompajler do alokatora registara, i bio je prilično brz. Bio je jednako brz kao moderni C1, ali tada je alokator bio mnogo sporiji, a gledajući unatrag – to je bio problem velike strukture podataka. Trebala mi je da napišem grafički alokator registara i nisam razumio dilemu između izražajnosti koda i brzine, koja je postojala u to doba i bila vrlo važna. Ispostavilo se da struktura podataka obično premašuje veličinu keša na x86 tadašnjeg vremena i stoga, ako sam prvotno pretpostavio da će alokator registara odraditi 5-10 posto cijelog vremena JIT-ovanja, u stvarnosti je to prerastalo u 50 posto.

Vrijeme je prolazilo, kompajler je postajao sve jasniji i učinkovitiji, prestao je generirati odvratan kod u većem broju slučajeva, i performanse su sve više počele ličiti na ono što daje C kompajler. Ako, naravno, ne pišeš neku glupost koju čak ni C ne može ubrzati. Ako pišeš kod kao u C-u, dobivaš performanse poput C-a u većem broju slučajeva. I što je dalje išlo, kod je sve više asimptotski odgovarao razini C, alokator registara je počeo ličiti na nešto završeno… bez obzira na to radi li tvoj kod brzo ili sporo. Nastavio sam raditi na alokatoru, kako bi obavljao bolje alokacije. Postajao je sve sporiji, ali je davavao sve bolje performanse u slučajevima kada nitko nije mogao uspjeti. Mogao sam zaroniti u alokator registara, potrošiti mjesec rada, i odjednom bi sav kod počeo raditi 5% brže. To se događalo iznova i iznova i alokator registara postao je nešto poput umjetničkog djela – svi su ga voleli ili mrzeli, a ljudi iz akademske zajednice postavljali su pitanja o tome "zašto se sve radi upravo ovako", zašto ne linearna skeniranja, i koja je razlika. Odgovor je uvijek isti: alokator temeljen na boji grafa plus vrlo pažljivo upravljanje predmemoriranim kodom jednako je oružje pobjede, najbolja kombinacija koju nitko ne može nadmašiti. I to je prilično nevidljiva stvar. Sve ostalo što compiler radi - to su prilično proučene stvari, iako su također dovedene do razine umjetnosti. Uvijek sam radio stvari koje su trebale pretvoriti compiler u umjetničko djelo. No, ništa od toga nije bilo izvanredno - osim alokatora registara. Caka je u tome što treba pažljivo odrezati pod opterećenjem i, ako se to dogodi ( mogu detaljnije objasniti ako je to zanimljivo), to znači da se može agresivnije inline-ati, bez rizika od prekoračenja zakrivljenosti grafikon performansi. U to vrijeme bilo je gomila sveobuhvatnih compilera, prepunih dodataka i zvučnih efekata, koji su imali alokatore registara, ali nitko to više nije mogao.

Problem je u tome što, ako dodaješ metode koje podliježu inliningu, povećavajući i povećavajući područje inlininga, skup korištenih vrijednosti odmah nadmašuje broj registara, što dovodi do spillanja. Kritična razina obično nastaje kada alokator popusti, a jedan dobar kandidat za spillanje vrijedi više od drugoga, pa spillas neke zaista divlje stvari. Vrijednost inlininga ovdje je u tome što gubiš dio overheada, overheada na poziv i očuvanje, možeš vidjeti vrijednosti iznutra i možeš ih dalje optimizirati. Trošak inlininga je u tome što se stvara veliki broj živih vrijednosti, i ako tvoj alokator registara spili više nego što je potrebno, odmah gubiš. Zato, većina alokatora ima problem: kada inlining prelazi neku granicu, počinju se spillati svi na svijetu i performanse mogu otići niz odvod. Oni koji implementiraju kompajler dodaju neke heuristike: na primjer, da zaustave inlining kada dostigne neku dovoljnu veličinu, jer će alokacije sve pokvariti. Tako se formira slom grafika performansi – inliniraš, inliniraš, performanse polako rastu – a onda bum! – pada naglo, jer si preinlinirao previše. Tako je sve funkcioniralo prije pojave Jave. Java zahtijeva mnogo više inlininga, pa sam morao učiniti svoj alokator mnogo agresivnijim, kako bi se izjednačio, a ne srušio, i ako si previše inlinirao – počinje spillati, ali onda opet dolazi trenutak „nema više spillanja“. To je zanimljivo zapažanje i došlo mi je jednostavno iz ničega, ne evidentno, ali se dobro isplatilo. Započeo sam s agresivnim inliningom i to me odvelo na mjesta gdje se performanse Jave i C-a nalaze jedne pored drugih. Stvarno su blizu – mogu pisati kod na Javi koji će biti znatno brži od C koda i slično, ali u prosjeku, u velikoj slici stvari, oni su otprilike usporedivi. Čini mi se da je dio ove zasluge – alokator registara, koji mi omogućuje da inliniram maksimalno jednostavno. Jednostavno inliniram sve što vidim. Pitanje je ovdje radi li alokator dobro, proizlazi li kao rezultat razuman radni kod. Ovo je bio veliki izazov: razumjeti sve to i natjerati ga da radi.

A bit about register allocation and multithreading

Vladimir: Problemi poput alokacije registara čine se kao neka vječna, beskonačna tema. Zanimljivo je, je li neka ideja izgledala obećavajuće, a onda zakazala u praksi?

Cliff: Naravno! Alokacija registara je područje u kojem za rješavanje NP-potpunog problema pokušavaš pronaći neke heuristike. I nikada ne možeš postići idealno rješenje, zar ne? To je jednostavno nemoguće. Pogledajte, Ahead of Time kompilacija – ona također loše radi. O čemu se ovdje radi? O nekim prosječnim slučajevima. O tipičnoj izvedbi, tako da možeš otići i izmjeriti nešto za što smatraš da je dobra tipična izvedba – na kraju krajeva, ti doista radiš na njenom poboljšanju! Alokacija registara je tema posvećena izvedbi. Čim imaš prvi prototip, radi i oboja što treba, započinje rad na performansama. Trebaš naučiti dobro mjeriti. Zašto je to važno? Ako postoje jasni podaci, možeš gledati na različitim dijelovima i vidjeti: aha, ovo je pomoglo ovdje, ali ovdje je sve propalo! Pojavljuju se neke dobre ideje, dodaješ novu heuristiku i odjednom sve počinje raditi prosječno malo bolje. Ili ne počinje. Imao sam puno slučajeva kada smo se borili za pet posto izvedbe koja je razlikovala naš razvoj od prethodnog alokatora. I svaki put to izgleda ovako: negdje si pobijedio, negdje si izgubio. Ako imaš dobre alate za analizu performansi, možeš pronaći gubitničke ideje i shvatiti zašto gube. Možda bi bilo bolje ostaviti sve kako je, a možda se ozbiljnije posvetiti finom podešavanju, ili otići i popraviti nešto drugo. To je cijeli set stvari! Napravio sam ovaj cool hax, ali treba i ovaj, i ovaj, i ovaj – i njihova ukupna kombinacija daje neka poboljšanja. A pojedinci mogu propasti. Takva je priroda rada na performansama NP-potpunih zadataka.

Vladimir: Čini se da su stvari poput obojanja u alokatorima već riješene. Pa, za vas su riješene, sudeći prema tome što pričate, pa vrijedi li uopće...

Cliff: Nije rešena sama po sebi. Ti je moraš pretvoriti u 'rešenu'. Postoje teški zadaci koje treba rešiti. Kada to uradiš, dolazi vreme za rad na performansama. Na taj posao treba pristupiti odgovarajuće - raditi benchmarking, prikupljati metrike, objašnjavati situacije kada je, vraćanjem na prethodnu verziju, tvoja stara metoda ponovo počela da radi (ili obrnuto, prestala). I ne odustaj dok nešto ne postigneš. Kao što sam već rekao, ako su sjajne ideje koje nisu uspele, u oblasti alokacije registara ideja je otprilike beskonačna. Možeš, na primer, čitati naučne publikacije. Iako je ova oblast sada postala pomerena mnogo sporije i jasnija nego u vreme svoje mladosti. Ipak, u ovoj oblasti radi cela beskonačnost ljudi i sve njihove ideje vredi isprobati, svi čekaju svoj trenutak. I ne možeš reći koliko su dobre ako ih ne isprobaš. Koliko dobro se integrišu sa svime ostalim u tvojoj alokaciji, jer alokacija radi mnoge stvari, i neke ideje u tvojoj konkretnoj alokaciji možda neće raditi, a u drugoj alokaciji - lako. Glavni način pobede za alokaciju je izbaciti sporu stvar van glavne staze i prisiliti je da se deli duž granica sporih puteva. Zato, ako želiš da pokreneš GC, kreni sporim putem, deoptimizuj, izbaci izuzetak, sve u tom stilu - znaš da su te stvari relativno retke. I zaista su retke, proverio sam. Radiš dodatni posao i zahvaljujući tome nestaje mnogo ograničenja na tim sporim putevima, ali to nije toliko važno, jer su spori i retko se prolazi preko njih. Na primer, nulti pokazivač - nikada se ne dešava, zar ne? Treba imati nekoliko puteva za različite stvari, ali oni se ne smeju mešati na glavnom. 

Vladimir: Šta misliš o mnogoj jezgri, kada ih ima hiljade odjednom? Da li je to korisna stvar?

Cliff: Uspeh GPU pokazuje da je prilično koristan!

Vladimir: Oni su prilično specijalizovani. A šta je sa procesorima opšte namene?

Cliff: Pa, to je bio poslovni model Azula. Odgovor je došao još u eri kada su ljudi jako voljeli predvidivu performansu. Tada je bilo teško pisati paralelni kod. H2O model kodiranja se dobro skalira, ali nije model opće namjene. Osim ako nije malo općenitiji u usporedbi s korištenjem GPU-a. Govorimo li o složenosti razvoja nečega takvog ili o složenosti njegove upotrebe? Na primjer, zanimljiva lekcija koju mi je dao Azul, prilično ne očigledna: mali keševi su u redu. 

The biggest challenge in life

Vladimir: Što je s netehničkim izazovima?

Cliff: Najveći izazov bio je ne biti... ljubazan i dobar prema ljudima. Kao rezultat toga, stalno sam se našao u ekstremno konfliktnim situacijama. Onima u kojima sam znao da sve ide naopako, ali nisam znao kako napredovati u rješavanju tih problema i nisam se znao nositi s njima. Mnoge dugotrajne probleme koji traju desetljećima pojavili su se upravo na taj način. To što u Javi postoje C1 i C2 kompajleri – neposredna je posljedica toga. To što u Javi već deset godina nije bilo višeslojne kompilacije – također je neposredna posljedica. Očigledno nam je bio potreban takav sustav, ali nije očigledno zašto ga nije bilo. Imao sam problema s jednim inženjerom... ili grupom inženjera. Davno, kada sam počeo raditi u Sunu, bio sam... dobro, ne samo tada, ja zapravo uvijek imam svoje mišljenje o svemu. I smatrao sam istinitim da se jednostavno može uzeti ta vlastita istina i reći je bez uvijanja. Tim više što sam većinu vremena bio šokantno u pravu. I ako ti se takav pristup ne sviđa... pogotovo ako očigledno griješiš i radiš gluposti... Uglavnom, malo ljudi moglo je podnijeti takav oblik komunikacije. Ipak, neki su mogli, na primjer, ja. Cijeli sam život gradio na meritokratskim principima. Ako mi pokažete nešto što nije u redu, odmah ću se okrenuti i reći: rekao si glupost. Pri tome se, naravno, izvinjavam i sve to, ističem zasluge, ako ih uopće ima, i činim druge ispravne korake. S druge strane, ja sam šokantno u pravu šokantno veliki postotak ukupnog vremena. I to ne funkcionira baš najbolje u odnosima s ljudima. I ne trudim se biti ljubazan, već postavljam pitanje direktno. 'To nikada neće uspjeti, jer broj jedan, dva i tri.' A oni su tako: 'Oh!'. Bilo je i drugih posljedica koje je bolje propustiti: na primjer, one koje su dovele do razvoda s ženom i deset godina depresije nakon toga.

Izazov je borba s ljudima, s njihovim percepcijama onoga što možeš ili ne možeš raditi, što je važno, a što nije. Bilo je mnogo izazova vezanih za stil kodiranja. Još uvijek pišem puno koda, a u to vrijeme morao sam se čak usporiti jer sam radio previše paralelnih zadataka i radili su ih slabo, umjesto da se usredotočim na jedan. Gledajući unatrag, napisao sam polovicu koda tima Java JIT, tima C2. Sljedeći najbrži programer pisao je dvostruko sporije, sljedeći još duplo sporije i to je bio eksponencijalni pad. Sedmi u toj liniji bio je jako, jako spor – tako to uvijek ide! Dotaknuo sam se gomile koda. Gledao sam tko što piše, bez iznimaka, buljio sam u njihov kod, pregledavao svakoga od njih, a i dalje sam pisao više od njih. Takav pristup s ljudima često ne funkcionira dobro. Neki to ne vole. A kada s tim ne mogu upravljati, počinju razne pritužbe. Na primjer, jednom su mi rekli da prestanem pisati kod jer pišem previše i to ugrožava tim, a meni je to sve zvučalo kao šala: čovječe, ako cijeli ostatak tima nestane, a ja nastavim pisati kod, ti ćeš izgubiti samo polovicu tima. S druge strane, ako nastavim pisati kod i ti izgubiš polovicu tima – to mi zvuči kao jako loše vođenje. Nikada posebno nisam razmišljao o tome, nikada o tome nisam govorio, ali je svejedno bilo negdje u mojoj glavi. U dubini uma vrtjela se misao: 'Pa vi se svi šalite'. Tako da, najveći problem sam bio ja i moje odnose s ljudima. Sada se bolje razumijem, dugo sam bio vođa tima programera, i sada ljudima izravno kažem: znaš, ja sam onakvog kakav jesam i morat ćete se nositi sa mnom – je li u redu da se tu postojim? I kada su se s tim počeli nositi, sve je počelo funkcionirati. Na kraju, nisam ni loš, ni dobar, nemam nikakve loše namjere ili sebične težnje, to je samo moja priroda i s tim se nekako treba živjeti.

Andrey: Nedavno su svi počeli govoriti o samosvijesti za introverte, i općenito o softver vještinama. Što se može reći o tome?

Cliff: Da, to je bilo razumijevanje i lekcija koju sam izvukao iz razvoda s bivšom ženom. Ono što sam stekao iz razvoda – to je razumijevanje sebe. Tako sam počeo razumjeti i druge ljude. Razumjeti kako ta interakcija funkcionira. To je dovelo do otkrića jedno za drugim. Pojavilo se svjesnost tko sam i što predstavljam. Što radim: ili sam zaokupljen zadatkom, ili izbjegavam sukob, ili nešto drugo – i takva razina samosvijesti zaista pomaže održati se pod kontrolom. Nakon toga sve ide mnogo lakše. Jedna stvar koju sam otkrio ne samo kod sebe, već i kod drugih programera – nemogućnost verbaliziranja misli kada si pod emocionalnim stresom. Na primjer, sjediš, kodiraš, nalazite se u stanju toka, a onda ti trče i počinju vrištati u histeriji da je nešto pokvareno, i odmah će se primijeniti ekstremne mjere. I ne možeš izgovoriti nijednu riječ, jer si u stanju emocionalnog stresa. Stečeno znanje omogućava pripremu za taj trenutak, proći kroz njega i preći na plan povlačenja, nakon kojeg se možeš tada nešto učiniti. Dakle, da, kada počneš prepoznavati kako sve ovo funkcionira – to je ogroman događaj koji mijenja život. 
Sam nisam mogao pronaći prave riječi, ali zapamtili ste niz postupaka. Bit je u tome da je ova reakcija – jednako fizička koliko i verbalna, i treba ti prostor. Takav prostor, u zen smislu. To je ono što treba objasniti, a zatim odmah ustupiti – fizički se udaljiti. Kada šutim riječima, mogu obraditi situaciju u emotivnom smislu. Kako adrenalin dospije do mozga, prebacuje te u način rada ‘udariti ili pobjeći’, više ne možeš ništa reći, ne – sada si idiot, inženjer za udaranje, nesposoban dati dostojan odgovor ili barem zaustaviti napad, a napadač može slobodno ponovo i ponovo napadati. Prvo se moraš ponovno postati sebi, povratiti kontrolu, izići iz režima ‘udariti ili pobjeći’.

I za to je potrebno verbalno prostor. Samo slobodan prostor. Ako uopće nešto reći, to se može upravo tako izjaviti, a zatim otići i stvarno pronaći svoje "prostor": otići u šetnju po parku, zaključati se u tušu - nije važno. Najvažnije je privremeno se isključiti iz te situacije. Čim se malo isključiš, kontrola se vraća, počinješ razmišljati jasno. "Dobro, ja nisam neki idiot, ne radim glupe stvari, prilično sam korisna osoba." Kada uspiješ uvjeriti samog sebe, vrijeme je za sljedeću fazu: shvatiti što se dogodilo. Napali su te, napad je došao odakle nisi ni očekivao, to je bila nepoštena, podla zamka. To je loše. Sljedeći korak je razumjeti zašto je napadaču to bilo potrebno. Zaista, zašto? Možda zato što je sam u bijesu? Zašto je u bijesu? Na primjer, zato što se sam osramotio i ne može preuzeti odgovornost? Na taj način treba pažljivo obraditi cijelu situaciju. No, za to je potrebno prostor za manevriranje, verbalno prostor. Prvi korak je prekinuti verbalni kontakt. Udaljiti se od rasprave riječima. Otkaži to, udalji se što je brže moguće. Ako je to telefonski razgovor - samo spusti slušalicu - to je vještina koju sam stekao iz razgovora s bivšom suprugom. Ako razgovor ne vodi ničemu dobrom, jednostavno reci "doviđenja" i objesi slušalicu. S one strane slušalice: "bla-bla-bla", ti odgovaraš: "aha, pa, čao!" i spustiš slušalicu. Jednostavno prekineš razgovor. Pet minuta kasnije, kada ti se vraća sposobnost racionalnog razmišljanja, malo se ohladiš, postaje moguće razmisliti o tome što se uopće dogodilo i što će dalje biti. I početi formulirati promišljeni odgovor, a ne samo reagirati na emocije. Za mene je proboj u samospoznaji upravo to što u slučaju emocionalnog stresa ne mogu govoriti. Izaći iz tog stanja, razmisliti i isplanirati kako odgovoriti i kompenzirati probleme - to su pravi koraci kada ne možeš govoriti. Najjednostavniji način je pobjeći od situacije u kojoj se manifestira emocionalni stres i jednostavno prestati sudjelovati u tom stresu. Nakon toga dobivaš sposobnost razmišljanja, kada možeš razmišljati, stvaraš mogućnost govora i tako dalje.

Usput, u sudu advokat suprotne strane pokušava to učiniti s tobom – sada je već jasno zašto. Zato što ima mogućnost da te pritisne do takvog stanja da ne možeš izgovoriti svoje ime, na primjer. U najdoslovnijem smislu, ne možeš govoriti. Ako ti se to događa i ako znaš da ćeš se naći na mjestu gdje se vode verbalne bitke, u mjestu poput suda, možeš doći sa svojim pravnikom. Pravni stručnjak će se zauzeti za tebe i prekinuti verbalni napad, a to će učiniti potpuno zakonitim putem, i tvoje izgubljeno zen mjesto će se vratiti. Na primjer, nekoliko puta sam morao nazvati obitelj, sudac je to smatrao prilično prijateljski, ali su advokat suprotne strane vikao i vičio na mene, nisam mogao ubaciti niti jednu riječ. U takvim situacijama najbolje djeluje korištenje posrednika. Posrednik prekida sav pritisak koji neprekidno teče prema tebi, pronalaziš potrebno zen mjesto, zajedno s njim se vraća sposobnost govora. To je cijelo područje znanja u kojem se mora mnogo učiti, mnogo otkriti unutar sebe, a sve se to pretvara u visoko strateške odluke, koje se razlikuju za različite ljude. Neki ljudi nemaju gore opisane probleme, obično ih nemaju ljudi koji se profesionalno bave prodajom. Svi ti ljudi koji zarađuju za život riječima – poznati pjevači, pjesnici, vjerski djelatnici i političari, uvijek imaju što reći. Oni nemaju takvih problema, ali ja ih imam.

Andrey: To je bilo… iznenađujuće. Odlično, već smo razgovarali prilično dugo i vrijeme je za završetak ovog intervjua. Obavezno ćemo se sastati na konferenciji i moći ćemo nastaviti ovaj dijalog. Vidimo se na Hydri!

Možete nastaviti razgovor s Cliffom na konferenciji Hydra 2019, koja će se održati 11-12. srpnja 2019. godine u Sankt Peterburgu. On će doći s izlaganjem «Iskustvo sa Azul Hardware Transactional Memory». Karte se mogu kupiti na službenoj stranici.

Izvor: habr.com

Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere 🔥 Kupite pouzdan hosting za web stranice s DDoS zaštitom, VPS VDS servere | ProHoster