Fantastisk interview med Cliff Click, faderen til JIT-kompilering i Java

Fantastisk interview med Cliff Click, faderen til JIT-kompilering i JavaCliff klik — CTO for Cratus (IoT-sensorer til procesforbedring), grundlægger og medstifter af flere startups (inklusive Rocket Realtime School, Neurensic og H2O.ai) med flere succesfulde exits. Cliff skrev sin første compiler i en alder af 15 (Pascal til TRS Z-80)! Han er bedst kendt for sit arbejde på C2 i Java (The Sea of ​​​​Nodes IR). Denne compiler viste verden, at JIT kunne producere kode af høj kvalitet, hvilket var en af ​​faktorerne i fremkomsten af ​​Java som en af ​​de vigtigste moderne softwareplatforme. Derefter hjalp Cliff Azul Systems med at bygge en 864-core mainframe med ren Java-software, der understøttede GC-pauser på en 500-gigabyte-bunke inden for 10 millisekunder. Generelt formåede Cliff at arbejde på alle aspekter af JVM.

 
Denne habrapost er et godt interview med Cliff. Vi vil tale om følgende emner:

  • Overgang til optimeringer på lavt niveau
  • Sådan laver du en stor refaktorering
  • Omkostningsmodel
  • Optimeringstræning på lavt niveau
  • Praktiske eksempler på præstationsforbedring
  • Hvorfor oprette dit eget programmeringssprog
  • Karriere som præstationsingeniør
  • Tekniske udfordringer
  • Lidt om registertildeling og multikerner
  • Den største udfordring i livet

Interviewet er foretaget af:

  • Andrey Satarin fra Amazon Web Services. I sin karriere lykkedes det ham at arbejde i helt andre projekter: han testede den distribuerede NewSQL-database i Yandex, et skydetekteringssystem i Kaspersky Lab, et multiplayer-spil i Mail.ru og en tjeneste til beregning af valutakurser i Deutsche Bank. Interesseret i at teste backend og distribuerede systemer i stor skala.
  • Vladimir Sitnikov fra Netcracker. Ti års arbejde med ydeevnen og skalerbarheden af ​​NetCracker OS, software, der bruges af teleoperatører til at automatisere netværks- og netværksudstyrsstyringsprocesser. Interesseret i Java og Oracle Database-ydelsesproblemer. Forfatter til mere end et dusin præstationsforbedringer i den officielle PostgreSQL JDBC-driver.

Overgang til optimeringer på lavt niveau

Andrew: Du er et stort navn i verden af ​​JIT-kompilering, Java og performance-arbejde generelt, ikke? 

Klint: Sådan er det!

Andrew: Lad os starte med nogle generelle spørgsmål om præstationsarbejde. Hvad synes du om valget mellem optimeringer på højt niveau og lavt niveau som at arbejde på CPU-niveau?

Klint: Ja, alt er enkelt her. Den hurtigste kode er den, der aldrig kører. Derfor skal du altid starte fra et højt niveau, arbejde på algoritmer. En bedre O-notation vil slå en dårligere O-notation, medmindre nogle tilstrækkeligt store konstanter griber ind. Ting på lavt niveau går sidst. Typisk, hvis du har optimeret resten af ​​din stack godt nok, og der stadig er nogle interessante ting tilbage, er det et lavt niveau. Men hvordan starter man fra et højt niveau? Hvordan ved du, at der er udført nok arbejde på højt niveau? Nå... ingen måde. Der er ingen færdige opskrifter. Du skal forstå problemet, beslutte dig for, hvad du vil gøre (for ikke at tage unødvendige skridt i fremtiden), og så kan du afdække profileren, som kan sige noget nyttigt. På et tidspunkt indser du selv, at du er sluppet af med unødvendige ting, og det er tid til at lave en finjustering på lavt niveau. Dette er bestemt en speciel form for kunst. Der er mange mennesker, der gør unødvendige ting, men bevæger sig så hurtigt, at de ikke har tid til at bekymre sig om produktiviteten. Men dette er indtil spørgsmålet melder sig ligeud. Normalt 99% af tiden er der ingen, der bekymrer sig om, hvad jeg laver, indtil det øjeblik, hvor der kommer en vigtig ting på den kritiske vej, som ingen bekymrer sig om. Og her begynder alle at nage dig om "hvorfor det ikke fungerede perfekt fra begyndelsen." Generelt er der altid noget at forbedre i ydeevnen. Men 99% af tiden har du ingen kundeemner! Du prøver bare at få noget til at fungere, og i processen finder du ud af, hvad der er vigtigt. Du kan aldrig vide på forhånd, at dette stykke skal være perfekt, så faktisk skal du være perfekt i alt. Men dette er umuligt, og du gør det ikke. Der er altid mange ting, der skal rettes – og det er helt normalt.

Sådan laver du en stor refaktorering

Andrew: Hvordan arbejder du på en forestilling? Dette er et tværgående problem. Har du f.eks. nogensinde skullet arbejde på problemer, der opstår fra krydset mellem en masse eksisterende funktionalitet?

Klint: Jeg prøver at undgå det. Hvis jeg ved, at ydeevne vil være et problem, tænker jeg over det, før jeg begynder at kode, især med datastrukturer. Men ofte opdager man alt dette meget senere. Og så skal du gå til ekstreme foranstaltninger og gøre det, jeg kalder "rewrite and conquer": du skal have fat i en stor nok brik. Noget af koden skal stadig omskrives på grund af ydeevneproblemer eller andet. Uanset årsagen til omskrivning af kode, er det næsten altid bedre at omskrive et større stykke end et mindre stykke. I dette øjeblik begynder alle at ryste af frygt: "Åh min Gud, du kan ikke røre så meget kode!" Men faktisk virker denne tilgang næsten altid meget bedre. Du skal straks tage fat på et stort problem, tegne en stor cirkel rundt om det og sige: Jeg vil omskrive alt inde i cirklen. Kanten er meget mindre end indholdet inde i den, der skal udskiftes. Og hvis en sådan afgrænsning af grænser giver dig mulighed for at udføre arbejdet indeni perfekt, dine hænder er frie, gør hvad du vil. Når du først forstår problemet, er omskrivningsprocessen meget nemmere, så tag en stor bid!
På samme tid, når du laver en stor omskrivning og indser, at ydeevnen bliver et problem, kan du straks begynde at bekymre dig om det. Dette bliver normalt til simple ting som "kopier ikke data, administrer data så enkelt som muligt, gør dem små." Ved store omskrivninger er der standardmetoder til at forbedre ydeevnen. Og de kredser næsten altid om data.

Omkostningsmodel

Andrew: I en af ​​podcastsene talte du om omkostningsmodeller i forbindelse med produktivitet. Kan du forklare, hvad du mente med dette?

Klint: Sikkert. Jeg blev født i en æra, hvor processorydelse var ekstremt vigtig. Og denne æra vender tilbage igen - skæbnen er ikke uden ironi. Jeg begyndte at leve i otte-bit-maskinernes dage; min første computer arbejdede med 256 bytes. Præcis bytes. Alt var meget lille. Instruktioner skulle tælles, og da vi begyndte at rykke op i programmeringssprogsstakken, tog sprogene mere og mere på. Der var Assembler, så Basic, så C, og C tog sig af mange detaljer, som registertildeling og instruktionsvalg. Men alt var helt klart der, og hvis jeg lavede en pointer til en forekomst af en variabel, så ville jeg få belastning, og prisen på denne instruktion er kendt. Hardwaren producerer et vist antal maskincyklusser, så udførelseshastigheden af ​​forskellige ting kan beregnes blot ved at lægge alle instruktionerne sammen, som du skal køre. Hver sammenligning/test/filial/opkald/indlæs/butik kunne lægges sammen og sige: det er udførelsestiden for dig. Når du arbejder på at forbedre ydeevnen, vil du helt sikkert være opmærksom på, hvilke tal der svarer til små varme cyklusser. 
Men så snart du skifter til Java, Python og lignende ting, bevæger du dig meget hurtigt væk fra hardware på lavt niveau. Hvad koster det at ringe til en getter i Java? Hvis JIT i HotSpot er korrekt indlejret, det vil indlæse, men hvis det ikke gjorde dette, vil det være et funktionskald. Da opkaldet er på en hot loop, vil det tilsidesætte alle andre optimeringer i denne loop. Derfor vil de reelle omkostninger være meget højere. Og du mister med det samme muligheden for at se på et stykke kode og forstå, at vi bør udføre det med hensyn til processorens clock-hastighed, hukommelse og anvendt cache. Alt dette bliver kun interessant, hvis du virkelig kommer ind i forestillingen.
Nu befinder vi os i en situation, hvor processorhastighederne næsten ikke er steget i et årti. De gamle dage er tilbage! Du kan ikke længere regne med god enkelttrådet ydeevne. Men hvis du pludselig kommer ind i parallel computing, er det utroligt svært, alle ser på dig som James Bond. Tifold accelerationer her forekommer normalt på steder, hvor nogen har rodet noget sammen. Samtidighed kræver meget arbejde. For at få den XNUMXx speedup skal du forstå omkostningsmodellen. Hvad og hvor meget koster det? Og for at gøre dette skal du forstå, hvordan tungen passer på den underliggende hardware.
Martin Thompson valgte et godt ord til sin blog Mekanisk sympati! Du skal forstå, hvad hardwaren skal gøre, hvordan den præcist vil gøre det, og hvorfor den gør, hvad den gør i første omgang. Ved at bruge dette er det ret nemt at begynde at tælle instruktioner og finde ud af, hvor udførelsestiden går hen. Hvis du ikke har den rette uddannelse, leder du bare efter en sort kat i et mørkt rum. Jeg ser folk optimere ydeevnen hele tiden, som ikke aner, hvad fanden de laver. De lider meget og gør ikke store fremskridt. Og når jeg tager det samme stykke kode, smutter et par små hacks ind og får en fem- eller ti-dobbelt fremskyndelse, lyder de: Nå, det er ikke fair, vi vidste allerede, at du var bedre. Fantastiske. Hvad taler jeg om... omkostningsmodellen handler om, hvilken slags kode du skriver og hvor hurtigt den kører i gennemsnit i det store billede.

Andrew: Og hvordan kan man holde sådan en volumen i hovedet? Er dette opnået med mere erfaring, eller? Hvor kommer sådanne erfaringer fra?

Klint: Nå, jeg fik ikke min erfaring på den nemmeste måde. Jeg programmerede i Assembly tilbage i de dage, hvor man kunne forstå hver enkelt instruktion. Det lyder dumt, men siden da har Z80 instruktionssættet altid siddet i mit hoved, i min hukommelse. Jeg kan ikke huske folks navne inden for et minut efter at have talt, men jeg husker kode skrevet for 40 år siden. Det er sjovt, det ligner et syndrom"idiot videnskabsmand'.

Optimeringstræning på lavt niveau

Andrew: Er der en nemmere måde at komme ind på?

Klint: Ja og nej. Den hardware, vi alle bruger, har ikke ændret sig så meget over tid. Alle bruger x86, med undtagelse af Arm-smartphones. Hvis du ikke laver en form for hardcore-indlejring, gør du det samme. Okay, næste gang. Instruktionerne har heller ikke ændret sig i århundreder. Du skal hen og skrive noget i Assembly. Ikke meget, men nok til at begynde at forstå. Du smiler, men jeg taler helt seriøst. Du skal forstå korrespondancen mellem sprog og hardware. Derefter skal du gå og skrive lidt og lave en lille legetøjskompiler til lidt legetøjssprog. Legetøjslignende betyder, at det skal laves i rimelig tid. Det kan være super simpelt, men det skal generere instruktioner. Handlingen med at generere en instruktion vil hjælpe dig med at forstå omkostningsmodellen for broen mellem højniveaukoden, som alle skriver, og maskinkoden, der kører på hardwaren. Denne korrespondance vil blive brændt ind i hjernen på det tidspunkt, hvor compileren skrives. Selv den enkleste compiler. Derefter kan du begynde at se på Java og det faktum, at dets semantiske kløft er meget dybere, og det er meget sværere at bygge broer over det. I Java er det meget sværere at forstå, om vores bro blev god eller dårlig, hvad vil få den til at falde fra hinanden, og hvad vil ikke. Men du har brug for en form for udgangspunkt, hvor du ser på koden og forstår: "ja, denne getter skal indsættes hver gang." Og så viser det sig, at det nogle gange sker, bortset fra situationen, hvor metoden bliver for stor, og JIT'en begynder at inline alt. Sådanne steders præstationer kan forudsiges øjeblikkeligt. Normalt fungerer getters godt, men så ser du på store varme loops og indser, at der er nogle funktionskald, der flyder rundt der, som ikke ved, hvad de laver. Dette er problemet med den udbredte brug af gettere, grunden til at de ikke er inlinet er, at det ikke er klart, om de er en getter. Hvis du har en super lille kodebase, kan du blot huske det og så sige: det her er en getter, og det her er en setter. I en stor kodebase lever hver funktion sin egen historie, som generelt ikke er kendt af nogen. Profileren siger, at vi mistede 24 % af tiden på en eller anden løkke, og for at forstå, hvad denne løkke gør, skal vi se på hver funktion indeni. Det er umuligt at forstå dette uden at studere funktionen, og det bremser for alvor forståelsesprocessen. Derfor bruger jeg ikke getters og settere, jeg har nået et nyt niveau!
Hvor får man omkostningsmodellen? Nå, du kan selvfølgelig læse noget... Men jeg tror, ​​den bedste måde er at handle. At lave en lille compiler vil være den bedste måde at forstå omkostningsmodellen og passe den ind i dit eget hoved. En lille compiler, der ville være egnet til at programmere en mikrobølgeovn, er en opgave for en begynder. Nå, jeg mener, hvis du allerede har programmeringsevner, så burde det være nok. Alle disse ting som at parse en streng, som du har som en form for algebraisk udtryk, udtrække instruktioner til matematiske operationer derfra i den rigtige rækkefølge, tage de korrekte værdier fra registre - alt dette gøres på én gang. Og mens du gør det, vil det blive indprentet i din hjerne. Jeg tror, ​​at alle ved, hvad en compiler gør. Og dette vil give en forståelse af omkostningsmodellen.

Praktiske eksempler på præstationsforbedring

Andrew: Hvad skal du ellers være opmærksom på, når du arbejder med produktivitet?

Klint: Datastrukturer. Forresten, ja, jeg har ikke undervist i disse klasser i lang tid... Raketskole. Det var sjovt, men det krævede en stor indsats, og jeg har også et liv! OKAY. Så i en af ​​de store og interessante klasser, "Hvor går din præstation hen", gav jeg eleverne et eksempel: to en halv gigabyte fintech-data blev læst fra en CSV-fil, og så skulle de beregne antallet af solgte produkter . Regelmæssige tick-markedsdata. UDP-pakker konverteret til tekstformat siden 70'erne. Chicago Mercantile Exchange - alle mulige ting som smør, majs, sojabønner, sådan noget. Det var nødvendigt at tælle disse produkter, antallet af transaktioner, den gennemsnitlige mængde bevægelse af midler og varer osv. Det er ret simpelt handelsmatematik: find produktkoden (det er 1-2 tegn i hash-tabellen), få ​​beløbet, tilføj det til et af handelssættene, tilføj volumen, tilføj værdi og et par andre ting. Meget simpel matematik. Legetøjsimplementeringen var meget ligetil: alt er i en fil, jeg læser filen og bevæger mig igennem den, opdeler individuelle poster i Java-strenge, leder efter de nødvendige ting i dem og tilføjer dem i henhold til matematikken beskrevet ovenfor. Og det virker ved lav hastighed.

Med denne tilgang er det indlysende, hvad der foregår, og parallel computing hjælper ikke, vel? Det viser sig, at en femdobling af ydeevnen kan opnås blot ved at vælge de rigtige datastrukturer. Og dette overrasker selv erfarne programmører! I mit særlige tilfælde var tricket, at du ikke skulle lave hukommelsestildelinger i en hot loop. Nå, dette er ikke hele sandheden, men generelt - du bør ikke fremhæve "en gang i X", når X er stort nok. Når X er to en halv gigabyte, bør du ikke allokere noget "en gang pr. bogstav", eller "en gang pr. linje", eller "en gang pr. felt", noget lignende. Det er her tiden bruges. Hvordan fungerer dette overhovedet? Forestil dig, at jeg ringer String.split() eller BufferedReader.readLine(). Readline laver en streng fra et sæt bytes, der kom over netværket, én gang for hver linje, for hver af de hundreder af millioner af linjer. Jeg tager denne linje, analyserer den og smider den væk. Hvorfor smider jeg det væk - ja, jeg har allerede behandlet det, det er alt. Så for hver byte, der læses fra disse 2.7G, vil to tegn blive skrevet i linjen, det vil sige allerede 5.4G, og jeg har ikke brug for dem til noget mere, så de bliver smidt væk. Hvis man ser på hukommelsesbåndbredden, indlæser vi 2.7G, der går gennem hukommelsen og hukommelsesbussen i processoren, og så sendes der dobbelt så meget til den linje, der ligger i hukommelsen, og alt dette flosses, når hver ny linje oprettes. Men jeg skal læse det, hardwaren læser det, selvom alt bliver flosset senere. Og jeg er nødt til at skrive det ned, fordi jeg har oprettet en linje og cacherne er fyldte - cachen kan ikke rumme 2.7G. Så for hver byte, jeg læser, læser jeg to bytes mere og skriver to bytes mere, og i sidste ende har de et forhold på 4:1 - i dette forhold spilder vi hukommelsesbåndbredde. Og så viser det sig, at hvis jeg gør String.split() – det er ikke sidste gang jeg gør det, der kan være yderligere 6-7 felter inde. Så den klassiske kode med at læse CSV og derefter parse strengene resulterer i et spild af hukommelsesbåndbredde på omkring 14:1 i forhold til det, du faktisk gerne vil have. Hvis du smider disse valg væk, kan du få en femdobling af speedup.

Og det er ikke så svært. Hvis du ser på koden fra den rigtige vinkel, bliver det hele ret simpelt, når du først indser problemet. Du bør ikke stoppe helt med at allokere hukommelse: Det eneste problem er, at du allokerer noget, og det dør straks, og undervejs brænder det en vigtig ressource, som i dette tilfælde er hukommelsesbåndbredde. Og alt dette resulterer i et fald i produktiviteten. På x86 skal du normalt aktivt brænde processorcyklusser, men her brændte du al hukommelsen meget tidligere op. Løsningen er at reducere mængden af ​​udledning. 
Den anden del af problemet er, at hvis du kører profileren, når hukommelsesstriben løber ud, lige når det sker, venter du normalt på, at cachen kommer tilbage, fordi den er fuld af skrald, som du lige har produceret, alle de linjer. Derfor bliver hver indlæsning eller butiksoperation langsom, fordi de fører til cache-misser - hele cachen er blevet langsom og venter på, at skrald forlader den. Derfor vil profileren blot vise varm tilfældig støj smurt ud over hele løkken - der vil ikke være nogen separat hot instruktion eller plads i koden. Kun støj. Og hvis du ser på GC-cyklerne, er de alle Young Generation og superhurtige - mikrosekunder eller millisekunder maksimalt. Når alt kommer til alt, dør al denne hukommelse øjeblikkeligt. Du tildeler milliarder af gigabyte, og han skærer dem, og skærer dem, og skærer dem igen. Alt dette sker meget hurtigt. Det viser sig, at der er billige GC-cyklusser, varm støj under hele cyklussen, men vi ønsker at få en 5x speedup. I dette øjeblik skulle noget lukke sig i dit hoved og lyde: "hvorfor er det?!" Memory strip overflow vises ikke i den klassiske debugger; du skal køre hardware performance counter debugger og se den selv og direkte. Men dette kan ikke direkte mistænkes ud fra disse tre symptomer. Det tredje symptom er, når du ser på det, du fremhæver, spørg profileren, og han svarer: "Du lavede en milliard rækker, men GC arbejdede gratis." Så snart dette sker, indser du, at du har skabt for mange objekter og brændt hele hukommelsesbanen op. Der er en måde at finde ud af dette på, men det er ikke indlysende. 

Problemet ligger i datastrukturen: den nøgne struktur, der ligger til grund for alt, hvad der sker, den er for stor, den er 2.7 G på disken, så det er meget uønsket at lave en kopi af denne ting - du vil straks indlæse den fra netværkets bytebuffer ind i registrene, for ikke at læse-skrive til linjen frem og tilbage fem gange. Desværre giver Java dig ikke som standard et sådant bibliotek som en del af JDK. Men det er trivielt, ikke? I bund og grund er disse 5-10 linjer kode, der vil blive brugt til at implementere din egen bufrede strengindlæser, som gentager strengklassens adfærd, mens den er en indpakning omkring den underliggende bytebuffer. Som et resultat viser det sig, at du næsten arbejder som med strenge, men faktisk flytter pointere til bufferen dertil, og de rå bytes bliver ikke kopieret nogen steder, og dermed genbruges de samme buffere igen og igen, og operativsystemet er glad for at påtage sig de ting, det er designet til, som skjult dobbeltbuffering af disse byte-buffere, og du kværner ikke længere gennem en endeløs strøm af unødvendige data. Forstår du i øvrigt, at når du arbejder med GC, er det garanteret, at hver hukommelsesallokering ikke vil være synlig for processoren efter den sidste GC-cyklus? Alt dette kan derfor umuligt være i cachen, og så sker der en 100% garanteret miss. Når du arbejder med en pointer, på x86, tager det 1-2 clock-cyklusser at trække et register fra hukommelsen, og så snart dette sker, betaler du, betaler, betaler, fordi hukommelsen er tændt. NI caches – og det er omkostningerne ved hukommelsestildeling. Virkelig værdi.

Datastrukturer er med andre ord det sværeste at ændre. Og når du først indser, at du har valgt den forkerte datastruktur, som vil dræbe ydeevnen senere hen, er der normalt meget arbejde, der skal gøres, men hvis du ikke gør det, vil tingene blive værre. Først og fremmest skal du tænke på datastrukturer, det er vigtigt. Hovedomkostningerne her falder på fede datastrukturer, som begynder at blive brugt i stil med "Jeg kopierede datastruktur X ind i datastruktur Y, fordi jeg bedre kan lide formen på Y." Men kopieringsoperationen (som virker billig) spilder faktisk hukommelsesbåndbredde, og det er her al den spildte eksekveringstid er begravet. Hvis jeg har en gigantisk streng af JSON, og jeg vil gøre den til et struktureret DOM-træ af POJO'er eller noget, vil operationen med at parse den streng og bygge POJO'en, og derefter få adgang til POJO'en igen senere, resultere i unødvendige omkostninger - det er ikke billigt. Bortset fra hvis du løber rundt om POJO'er meget oftere, end du løber rundt om en snor. Umiddelbart kan du i stedet prøve at dekryptere strengen og kun udtrække det, du har brug for derfra, uden at gøre det til en POJO. Hvis alt dette sker på en vej, hvorfra der kræves maksimal ydeevne, ingen POJO'er for dig, skal du på en eller anden måde grave direkte ind i linjen.

Hvorfor oprette dit eget programmeringssprog

Andrew: Du sagde, at for at forstå omkostningsmodellen, skal du skrive dit eget lille sprog...

Klint: Ikke et sprog, men en compiler. Et sprog og en compiler er to forskellige ting. Den vigtigste forskel er i dit hoved. 

Andrew: Så vidt jeg ved, eksperimenterer du i øvrigt med at skabe dine egne sprog. For hvad?

Klint: Fordi jeg kan! Jeg er halvpensioneret, så det er min hobby. Jeg har implementeret andres sprog hele mit liv. Jeg arbejdede også meget med min kodningsstil. Og også fordi jeg ser problemer på andre sprog. Jeg kan se, at der er bedre måder at gøre velkendte ting på. Og jeg ville bruge dem. Jeg er bare træt af at se problemer i mig selv, i Java, i Python, på et hvilket som helst andet sprog. Jeg skriver nu i React Native, JavaScript og Elm som en hobby, der ikke handler om pension, men om aktivt arbejde. Jeg skriver også i Python og vil højst sandsynligt fortsætte med at arbejde på maskinlæring til Java-backends. Der er mange populære sprog, og de har alle interessante funktioner. Alle er gode på deres egen måde, og du kan prøve at bringe alle disse funktioner sammen. Så jeg studerer ting, der interesserer mig, sprogets adfærd, og prøver at finde på en fornuftig semantik. Og indtil videre er det lykkedes mig! I øjeblikket kæmper jeg med hukommelsessemantik, fordi jeg vil have det ligesom i C og Java, og få en stærk hukommelsesmodel og hukommelsessemantik til belastninger og lagre. På samme tid skal du have automatisk typeslutning som i Haskell. Her forsøger jeg at blande Haskell-lignende typeinferens med hukommelsesarbejde i både C og Java. Det er det jeg har lavet de sidste 2-3 måneder f.eks.

Andrew: Hvis du bygger et sprog, der tager bedre aspekter fra andre sprog, tror du, at nogen vil gøre det modsatte: tage dine ideer og bruge dem?

Klint: Det er præcis sådan, nye sprog dukker op! Hvorfor ligner Java C? Fordi C havde en god syntaks, som alle forstod, og Java var inspireret af denne syntaks, tilføjede typesikkerhed, array bounds checking, GC, og de forbedrede også nogle ting fra C. De tilføjede deres egne. Men de blev inspireret ret meget, ikke? Alle står på skuldrene af de giganter, der kom før dig – det er sådan fremskridt sker.

Andrew: Som jeg forstår det, vil dit sprog være hukommelsessikkert. Har du tænkt på at implementere noget som en lånetjek fra Rust? Har du kigget på ham, hvad synes du om ham?

Klint: Nå, jeg har skrevet C i evigheder, med alt dette malloc og gratis, og manuelt administreret levetiden. Du ved, 90-95% af manuelt styret levetid har samme struktur. Og det er meget, meget smertefuldt at gøre det manuelt. Jeg vil gerne have compileren til blot at fortælle dig, hvad der foregår der, og hvad du opnåede med dine handlinger. For nogle ting gør borrow checker dette ud af boksen. Og det skal automatisk vise information, forstå alt og ikke engang belaste mig med at præsentere denne forståelse. Den skal i det mindste lave en lokal escape-analyse, og kun hvis den mislykkes, så skal den tilføje typeanmærkninger, der beskriver levetiden - og sådan en ordning er meget mere kompleks end en lånekontrol eller en hvilken som helst eksisterende hukommelseskontrol. Valget mellem "alt er fint" og "jeg forstår ingenting" - nej, der skal være noget bedre. 
Så som en, der har skrevet meget kode i C, tror jeg, at det er det vigtigste at have understøttelse af automatisk livstidskontrol. Jeg er også træt af, hvor meget Java bruger hukommelse, og hovedklagen er GC. Når du tildeler hukommelse i Java, vil du ikke få den hukommelse tilbage, der var lokal ved den sidste GC-cyklus. Dette er ikke tilfældet i sprog med mere præcis hukommelsesstyring. Ringer man til malloc, får man straks den hukommelse, der normalt bare blev brugt. Normalt gør man nogle midlertidige ting med hukommelsen og returnerer den straks tilbage. Og den vender straks tilbage til malloc-bassinet, og den næste malloc-cyklus trækker den ud igen. Derfor reduceres det faktiske hukommelsesforbrug til mængden af ​​levende objekter på et givet tidspunkt, plus lækager. Og hvis alt ikke lækker på en helt uanstændig måde, ender det meste af hukommelsen i caches og processoren, og det virker hurtigt. Men kræver en del manuel hukommelsesstyring med malloc og gratis kaldet i den rigtige rækkefølge, på det rigtige sted. Rust kan klare dette ordentligt alene, og giver i mange tilfælde endnu bedre ydeevne, da hukommelsesforbruget er indsnævret til kun den aktuelle beregning - i modsætning til at vente på næste GC-cyklus for at frigøre hukommelse. Som et resultat fik vi en meget interessant måde at forbedre ydeevnen på. Og ret kraftfuldt - jeg mener, jeg gjorde sådanne ting, da jeg behandlede data til fintech, og det gjorde det muligt for mig at få en speedup på omkring fem gange. Det er et ret stort løft, især i en verden, hvor processorerne ikke bliver hurtigere, og vi stadig venter på forbedringer.

Karriere som præstationsingeniør

Andrew: Jeg vil også gerne spørge rundt om karriere generelt. Du steg frem med dit JIT-arbejde hos HotSpot og flyttede derefter til Azul, som også er en JVM-virksomhed. Men vi arbejdede allerede mere på hardware end software. Og så skiftede de pludselig til Big Data og Machine Learning og derefter til svindeldetektion. Hvordan skete dette? Det er meget forskellige udviklingsområder.

Klint: Jeg har programmeret i ret lang tid og har formået at tage en masse forskellige klasser. Og når folk siger: "åh, det var dig, der lavede JIT til Java!", er det altid sjovt. Men før det arbejdede jeg på en klon af PostScript – det sprog, som Apple engang brugte til sine laserprintere. Og før det lavede jeg en implementering af Forth-sproget. Jeg tror, ​​at et fælles tema for mig er værktøjsudvikling. Hele mit liv har jeg lavet værktøjer, som andre mennesker skriver deres fede programmer med. Men jeg var også involveret i udviklingen af ​​operativsystemer, drivere, debuggere på kerneniveau, sprog til OS-udvikling, som startede trivielt, men med tiden blev mere og mere komplekst. Men hovedemnet er stadig udvikling af værktøjer. En stor del af mit liv gik mellem Azul og Sun, og det handlede om Java. Men da jeg kom ind i Big Data og Machine Learning, tog jeg min fancy hat på igen og sagde: "Åh, nu har vi et ikke-trivielt problem, og der foregår en masse interessante ting, og folk gør ting." Dette er en fantastisk udviklingsvej at tage.

Ja, jeg elsker virkelig distribueret databehandling. Mit første job var som studerende i C, på et annonceprojekt. Dette var distribueret databehandling på Zilog Z80-chips, der indsamlede data til analog OCR, produceret af en rigtig analog analysator. Det var et fedt og fuldstændig skørt emne. Men der var problemer, en del blev ikke genkendt korrekt, så man skulle tage et billede frem og vise det til en person, der allerede kunne læse med øjnene og rapportere, hvad der stod, og derfor var der job med data, og disse jobs havde deres eget sprog. Der var en backend, der behandlede alt dette - Z80'er, der kørte parallelt med vt100-terminaler, der kørte - en pr. person, og der var en parallel programmeringsmodel på Z80. Nogle fælles stykke hukommelse deles af alle Z80'ere i en stjernekonfiguration; Bagplanet blev også delt, og halvdelen af ​​RAM blev delt inden for netværket, og en anden halvdel var privat eller gik til noget andet. Et meningsfuldt komplekst parallelt distribueret system med delt... semi-delt hukommelse. Hvornår var dette... Jeg kan ikke engang huske, et sted i midten af ​​80'erne. For ret lang tid siden. 
Ja, lad os antage, at 30 år er ret lang tid siden. Problemer relateret til distribueret computing har eksisteret i ret lang tid; folk har længe været i krig med Beowulf-klynger. Sådanne klynger ser ud som... For eksempel: der er Ethernet og din hurtige x86 er forbundet til dette Ethernet, og nu vil du have falsk delt hukommelse, fordi ingen kunne lave distribueret computerkodning dengang, det var for svært og derfor var falsk delt hukommelse med beskyttelseshukommelsessider på x86, og hvis du skrev til denne side, så fortalte vi andre processorer, at hvis de tilgår den samme delte hukommelse, skulle den indlæses fra dig, og dermed noget i retning af en protokol til at understøtte cache kohærens dukkede op og software til dette. Interessant koncept. Det egentlige problem var selvfølgelig noget andet. Alt dette virkede, men man fik hurtigt præstationsproblemer, for ingen forstod præstationsmodellerne på et godt nok niveau – hvilke hukommelsesadgangsmønstre var der, hvordan man sikrede sig, at noderne ikke uendeligt pingede hinanden, og så videre.

Det, jeg kom frem til i H2O, er, at det er udviklerne selv, der er ansvarlige for at bestemme, hvor parallelitet er skjult, og hvor den ikke er. Jeg fandt på en kodningsmodel, der gjorde det nemt og enkelt at skrive højtydende kode. Men at skrive langsomt kørende kode er svært, det vil se dårligt ud. Du skal seriøst prøve at skrive langsom kode, du bliver nødt til at bruge ikke-standardiserede metoder. Bremsekoden er synlig ved første øjekast. Som følge heraf skriver du normalt kode, der kører hurtigt, men du skal finde ud af, hvad du skal gøre i tilfælde af delt hukommelse. Alt dette er bundet til store arrays, og adfærden der ligner ikke-flygtige store arrays i parallel Java. Jeg mener, forestil dig, at to tråde skriver til et parallelt array, en af ​​dem vinder, og den anden taber følgelig, og du ved ikke, hvilken der er hvilken. Hvis de ikke er flygtige, så kan ordren være hvad du vil – og det fungerer rigtig godt. Folk bekymrer sig virkelig om rækkefølgen af ​​operationer, de sætter flygtige de rigtige steder, og de forventer hukommelsesrelaterede præstationsproblemer de rigtige steder. Ellers ville de simpelthen skrive kode i form af loops fra 1 til N, hvor N er nogle billioner, i håb om at alle komplekse sager automatisk bliver parallelle - og det virker ikke der. Men i H2O er dette hverken Java eller Scala; du kan betragte det som "Java minus minus", hvis du vil. Dette er en meget klar programmeringsstil og ligner at skrive simpel C- eller Java-kode med loops og arrays. Men samtidig kan hukommelsen behandles i terabyte. Jeg bruger stadig H2O. Jeg bruger det fra tid til anden i forskellige projekter – og det er stadig det hurtigste, snesevis af gange hurtigere end dets konkurrenter. Hvis du laver Big Data med kolonnedata, er det meget svært at slå H2O.

Tekniske udfordringer

Andrew: Hvad har været din største udfordring i hele din karriere?

Klint: Diskuterer vi den tekniske eller ikke-tekniske del af spørgsmålet? Jeg vil sige, at de største udfordringer ikke er tekniske. 
Hvad angår tekniske udfordringer. Jeg besejrede dem simpelthen. Jeg ved ikke engang, hvad den største var, men der var nogle ret interessante, der tog en del tid, mental kamp. Da jeg gik til Sun, var jeg sikker på, at jeg ville lave en hurtig compiler, og en flok seniorer sagde som svar, at det aldrig ville lykkes mig. Men jeg fulgte denne vej, skrev en compiler ned til registerallokatoren, og det var ret hurtigt. Det var lige så hurtigt som moderne C1, men allokatoren var meget langsommere dengang, og set i bakspejlet var det et stort datastrukturproblem. Jeg havde brug for det til at skrive en grafisk registerallokator, og jeg forstod ikke dilemmaet mellem kodeekspressivitet og hastighed, som eksisterede i den æra og var meget vigtigt. Det viste sig, at datastrukturen normalt overstiger cachestørrelsen på datidens x86'ere, og derfor, hvis jeg oprindeligt antog, at registerallokatoren ville regne ud 5-10 procent af den samlede jittertid, så viste det sig i virkeligheden at være 50 procent.

Som tiden gik, blev compileren renere og mere effektiv, holdt op med at generere forfærdelig kode i flere tilfælde, og ydeevnen begyndte i stigende grad at ligne, hvad en C-compiler producerer. Medmindre du selvfølgelig skriver noget lort, som selv C ikke accelererer. . Hvis du skriver kode som C, får du ydeevne som C i flere tilfælde. Og jo længere du gik, jo oftere fik du kode, der asymptotisk faldt sammen med niveau C, registerallokatoren begyndte at ligne noget komplet... uanset om din kode kører hurtigt eller langsomt. Jeg fortsatte med at arbejde på fordeleren for at få den til at foretage bedre valg. Han blev langsommere og langsommere, men han ydede bedre og bedre præstationer i de tilfælde, hvor ingen andre kunne klare sig. Jeg kunne dykke ned i en registerallokator, begrave en måneds arbejde der, og pludselig ville hele koden begynde at eksekvere 5 % hurtigere. Dette skete gang på gang, og registerfordeleren blev noget af et kunstværk - alle elskede det eller hadede det, og folk fra akademiet stillede spørgsmål om emnet "hvorfor gøres alt på denne måde", hvorfor ikke linje scanning, og hvad er forskellen. Svaret er stadig det samme: en allokator baseret på at male grafen plus meget omhyggeligt arbejde med bufferkoden er lig med et sejrsvåben, den bedste kombination, som ingen kan besejre. Og dette er en ret ikke-oplagt ting. Alt andet, som compileren laver der, er ret velundersøgte ting, selvom de også er blevet bragt til kunstens niveau. Jeg gjorde altid ting, der skulle gøre compileren til et kunstværk. Men intet af dette var noget ekstraordinært - bortset fra registerfordeleren. Tricket er at være forsigtig skære ned under belastning, og hvis dette sker (jeg kan forklare mere detaljeret, hvis det er interesseret), betyder det, at du kan inline mere aggressivt, uden risiko for at falde over et knæk i præstationsplanen. I de dage var der en flok fuldskala-kompilatorer, hængt med kugler og fløjter, som havde registerfordelere, men ingen andre kunne gøre det.

Problemet er, at hvis du tilføjer metoder, der er genstand for inlining, forøgelse og forøgelse af inlining-området, overgår sættet af brugte værdier øjeblikkeligt antallet af registre, og du skal skære dem. Det kritiske niveau kommer normalt, når tildeleren giver op, og en god kandidat til et spild er en anden værd, vil du sælge nogle generelt vilde ting. Værdien af ​​inlining her er, at du mister en del af overheaden, overhead til at ringe og gemme, du kan se værdierne indeni og kan optimere dem yderligere. Omkostningerne ved inlining er, at der dannes et stort antal levende værdier, og hvis din registerallokator brænder mere op end nødvendigt, taber du med det samme. Derfor har de fleste fordelere et problem: Når inlining krydser en bestemt linje, begynder alt i verden at blive skåret ned, og produktiviteten kan skylles ned i toilettet. De, der implementerer compileren, tilføjer nogle heuristika: for eksempel at stoppe inlining, begyndende med en tilstrækkelig stor størrelse, da allokeringer vil ødelægge alt. Sådan dannes et knæk i præstationsgrafen - du inline, inline, præstationen vokser langsomt - og så boom! – den falder ned som en hurtig donkraft, fordi du forede for meget. Sådan fungerede alt før Javas fremkomst. Java kræver meget mere inlining, så jeg var nødt til at gøre min allocator meget mere aggressiv, så den jævner ud i stedet for at crashe, og hvis du inline for meget, begynder den at spilde, men så kommer "no more spiling"-øjeblikket stadig. Dette er en interessant observation, og den kom bare til mig ud af ingenting, ikke indlysende, men det gav godt resultat. Jeg tog aggressiv inlining op, og det tog mig til steder, hvor Java og C-performance arbejder side om side. De er virkelig tæt på - jeg kan skrive Java-kode, der er betydeligt hurtigere end C-kode og den slags, men i gennemsnit er de i det store billede nogenlunde sammenlignelige. Jeg tror, ​​at en del af denne fordel er registerallokatoren, som giver mig mulighed for at inline så dumt som muligt. Jeg inline bare alt, hvad jeg ser. Spørgsmålet her er, om allokatoren fungerer godt, om resultatet er intelligent fungerende kode. Dette var en stor udfordring: at forstå alt dette og få det til at fungere.

Lidt om registertildeling og multikerner

Vladimir: Problemer som registertildeling virker som en slags evigt, uendeligt emne. Jeg spekulerer på, om der nogensinde har været en idé, der virkede lovende og derefter slog fejl i praksis?

Klint: Sikkert! Registerallokering er et område, hvor man forsøger at finde nogle heuristika til at løse et NP-komplet problem. Og du kan aldrig opnå en perfekt løsning, vel? Dette er simpelthen umuligt. Se, Ahead of Time-kompilering - den fungerer også dårligt. Samtalen her handler om nogle gennemsnitssager. Om typiske præstationer, så du kan gå hen og måle noget, som du synes er god typisk præstation – du arbejder trods alt på at forbedre det! Registertildeling er et emne, der handler om præstation. Når du har den første prototype, den virker og maler det der skal til, performance-arbejdet begynder. Du skal lære at måle godt. Hvorfor er det vigtigt? Hvis du har klare data, kan du se på forskellige områder og se: ja, det hjalp her, men det var her, alt gik i stykker! Nogle gode ideer dukker op, du tilføjer nye heuristika og pludselig begynder alt at fungere lidt bedre i gennemsnit. Eller den starter ikke. Jeg havde en masse sager, hvor vi kæmpede for de fem procents præstationer, der adskilte vores udvikling fra den tidligere allokator. Og hver gang ser det sådan ud: et sted vinder du, et sted taber du. Hvis du har gode præstationsanalyseværktøjer, kan du finde de tabende ideer og forstå, hvorfor de fejler. Måske er det værd at lade alt være som det er, eller måske tage en mere seriøs tilgang til at finjustere, eller gå ud og ordne noget andet. Det er en hel masse ting! Jeg lavede dette seje hack, men jeg har også brug for denne, og denne og denne - og deres samlede kombination giver nogle forbedringer. Og ensomme kan fejle. Dette er karakteren af ​​præstationsarbejde på NP-komplette problemer.

Vladimir: Man får en fornemmelse af, at ting som at male i tildelere er et problem, der allerede er løst. Nå, det er bestemt for dig, at dømme efter hvad du siger, så er det overhovedet det værd...

Klint: Det er ikke løst som sådan. Det er dig, der skal gøre det til "løst". Der er vanskelige problemer, og de skal løses. Når dette er gjort, er det tid til at arbejde på produktiviteten. Du skal forholde dig til dette arbejde i overensstemmelse hermed - lav benchmarks, indsaml metrics, forklar situationer, hvor, når du rullede tilbage til en tidligere version, dit gamle hack begyndte at fungere igen (eller omvendt, stoppede). Og giv ikke op, før du opnår noget. Som jeg allerede sagde, hvis der er fede ideer, der ikke fungerede, men inden for tildeling af idéregistre er det omtrent uendeligt. Du kan for eksempel læse videnskabelige publikationer. Selvom nu dette område er begyndt at bevæge sig meget langsommere og er blevet mere tydeligt end i sin ungdom. Men der er utallige mennesker, der arbejder inden for dette felt, og alle deres ideer er værd at prøve, de venter alle i kulissen. Og du kan ikke se, hvor gode de er, medmindre du prøver dem. Hvor godt kan de integreres med alt andet i din tildeler, fordi en tildeler gør mange ting, og nogle ideer i din specifikke tildeler vil ikke fungere, men i en anden tildeler vil de nemt. Den vigtigste måde at vinde på for tildeleren er at trække de langsomme ting uden for hovedstien og tvinge den til at dele sig langs grænserne for de langsomme stier. Så hvis du vil køre en GC, tag den langsomme vej, deoptimer, smid en undtagelse, alt det der - du ved, at disse ting er relativt sjældne. Og de er virkelig sjældne, tjekkede jeg. Du laver ekstra arbejde, og det fjerner mange af begrænsningerne på disse langsomme stier, men det gør ikke rigtig noget, fordi de er langsomme og sjældent befærdede. For eksempel en nul-pointer - det sker aldrig, vel? Du skal have flere stier til forskellige ting, men de bør ikke forstyrre den vigtigste. 

Vladimir: Hvad synes du om multikerner, når der er tusindvis af kerner på én gang? Er dette en nyttig ting?

Klint: GPU'ens succes viser, at den er ret nyttig!

Vladimir: De er ret specialiserede. Hvad med processorer til generelle formål?

Klint: Nå, det var Azuls forretningsmodel. Svaret kom tilbage i en tid, hvor folk virkelig elskede forudsigelig præstation. Det var svært at skrive parallel kode dengang. H2O-kodningsmodellen er meget skalerbar, men den er ikke en generel model. Måske lidt mere generelt end ved brug af en GPU. Taler vi om kompleksiteten i at udvikle sådan noget eller kompleksiteten i at bruge det? For eksempel lærte Azul mig en interessant lektion, en ret ikke-indlysende lektion: små caches er normale. 

Den største udfordring i livet

Vladimir: Hvad med ikke-tekniske udfordringer?

Klint: Den største udfordring var ikke at være... venlig og sød ved folk. Og som følge heraf befandt jeg mig konstant i ekstreme konfliktsituationer. Dem, hvor jeg vidste, at tingene gik galt, men ikke vidste, hvordan jeg skulle komme videre med disse problemer og ikke kunne håndtere dem. Mange langsigtede problemer, der varede i årtier, opstod på denne måde. At Java har C1 og C2 compilere er en direkte konsekvens af dette. Det faktum, at der ikke var nogen kompilering på flere niveauer i Java ti år i træk, er også en direkte konsekvens. Det er indlysende, at vi havde brug for et sådant system, men det er ikke indlysende, hvorfor det ikke eksisterede. Jeg havde problemer med en ingeniør... eller en gruppe ingeniører. Engang, da jeg begyndte at arbejde hos Sun, var jeg... Okay, ikke kun dengang, jeg har generelt altid min egen mening om alting. Og jeg troede, det var rigtigt, at du bare kunne tage denne din sandhed og fortælle den direkte. Især da jeg havde chokerende ret det meste af tiden. Og hvis du ikke kan lide denne tilgang ... især hvis du åbenlyst tager fejl og laver noget sludder ... Generelt var det de færreste der kunne tolerere denne form for kommunikation. Selvom nogle kunne, som mig. Jeg har bygget hele mit liv på meritokratiske principper. Hvis du viser mig noget forkert, vender jeg mig straks om og siger: du sagde noget sludder. Samtidig beklager jeg selvfølgelig og alt det der, jeg vil notere fordelene, hvis nogen, og tage andre korrekte handlinger. På den anden side har jeg chokerende ret omkring en chokerende stor procentdel af den samlede tid. Og det fungerer ikke særlig godt i forhold til mennesker. Jeg forsøger ikke at være sød, men jeg stiller spørgsmålet ligeud. "Dette vil aldrig fungere, for en, to og tre." Og de sagde: "Åh!" Der var andre konsekvenser, som nok var bedre at ignorere: for eksempel dem, der førte til en skilsmisse fra min kone og ti års depression efter det.

Udfordring er en kamp med mennesker, med deres opfattelse af, hvad du kan eller ikke kan, hvad der er vigtigt og hvad der ikke er. Der var mange udfordringer omkring kodningsstil. Jeg skriver stadig meget kode, og i de dage måtte jeg endda sætte farten ned, fordi jeg lavede for mange parallelle opgaver og lavede dem dårligt i stedet for at fokusere på én. Når jeg ser tilbage, skrev jeg halvdelen af ​​koden til Java JIT-kommandoen, C2-kommandoen. Den næsthurtigste koder skrev halvt så langsomt, den næste halvt så langsomt, og det var et eksponentielt fald. Den syvende person i denne række var meget, meget langsom - det sker altid! Jeg rørte en masse kode. Jeg så på, hvem der skrev hvad, uden undtagelse stirrede jeg på deres kode, gennemgik hver af dem og fortsatte stadig med at skrive mere selv end nogen af ​​dem. Denne tilgang fungerer ikke særlig godt med mennesker. Nogle mennesker kan ikke lide dette. Og når de ikke kan klare det, begynder alle mulige klager. For eksempel fik jeg engang besked på at stoppe med at kode, fordi jeg skrev for meget kode, og det var til fare for holdet, og det hele lød som en joke for mig: dude, hvis resten af ​​holdet forsvinder, og jeg bliver ved med at skrive kode, så vil kun miste halve hold. På den anden side, hvis jeg bliver ved med at skrive kode, og du mister halvdelen af ​​holdet, lyder det som meget dårlig ledelse. Jeg har aldrig rigtig tænkt over det, aldrig talt om det, men det var stadig et sted i mit hoved. Tanken snurrede i baghovedet: "Spilger I med mig?" Så det største problem var mig og mine forhold til mennesker. Nu forstår jeg mig selv meget bedre, jeg var teamleder for programmører i lang tid, og nu siger jeg direkte til folk: du ved, jeg er den, jeg er, og du bliver nødt til at håndtere mig - er det okay, hvis jeg står her? Og da de begyndte at beskæftige sig med det, virkede alt. Faktisk er jeg hverken dårlig eller god, jeg har ingen dårlige hensigter eller egoistiske forhåbninger, det er bare min essens, og jeg er nødt til at leve med det på en eller anden måde.

Andrew: For nylig begyndte alle at tale om selvbevidsthed for introverte og bløde færdigheder generelt. Hvad kan du sige om dette?

Klint: Ja, det var den indsigt og lektie, jeg lærte af min skilsmisse fra min kone. Det, jeg lærte af skilsmissen, var at forstå mig selv. Sådan begyndte jeg at forstå andre mennesker. Forstå, hvordan denne interaktion fungerer. Dette førte til opdagelser efter hinanden. Der var en bevidsthed om, hvem jeg er, og hvad jeg repræsenterer. Hvad laver jeg: enten er jeg optaget af opgaven, eller også undgår jeg konflikt, eller noget andet – og denne grad af selvbevidsthed er virkelig med til at holde mig selv i kontrol. Herefter går alt meget lettere. En ting, som jeg opdagede ikke kun hos mig selv, men også hos andre programmører, er manglende evne til at verbalisere tanker, når du er i en tilstand af følelsesmæssig stress. For eksempel sidder du der og koder, i en tilstand af flow, og så kommer de løbende til dig og begynder at skrige i hysterisk, at noget er gået i stykker, og nu vil der blive taget ekstreme forholdsregler imod dig. Og du kan ikke sige et ord, fordi du er i en tilstand af følelsesmæssig stress. Den tilegnede viden giver dig mulighed for at forberede dig på dette øjeblik, overleve det og gå videre til en retreatplan, hvorefter du kan gøre noget. Så ja, når du begynder at indse, hvordan det hele fungerer, er det en kæmpe livsændrende begivenhed. 
Jeg kunne ikke selv finde de rigtige ord, men jeg huskede rækkefølgen af ​​handlinger. Pointen er, at denne reaktion er lige så meget fysisk som den er verbal, og du har brug for plads. Sådan plads, i zen-forstand. Det er præcis det, der skal forklares, og så straks træde til side – rent fysisk træde væk. Når jeg forbliver tavs verbalt, kan jeg bearbejde situationen følelsesmæssigt. Efterhånden som adrenalinen når din hjerne, skifter dig til kamp eller fly-tilstand, kan du ikke længere sige noget, nej - nu er du en idiot, en piskende ingeniør, ude af stand til at reagere ordentligt eller endda stoppe angrebet, og angriberen er fri. at angribe igen og igen. Du skal først blive dig selv igen, genvinde kontrollen, komme ud af "fight or flight"-tilstanden.

Og til dette har vi brug for verbalt rum. Bare ledig plads. Hvis du overhovedet siger noget, så kan du sige præcis det, og så gå og virkelig finde "plads" til dig selv: gå en tur i parken, lås dig selv inde i brusebadet - det gør ikke noget. Det vigtigste er midlertidigt at afbryde forbindelsen fra den situation. Så snart du slukker i mindst et par sekunder, vender kontrollen tilbage, du begynder at tænke nøgternt. "Okay, jeg er ikke en slags idiot, jeg laver ikke dumme ting, jeg er en ret nyttig person." Når du har været i stand til at overbevise dig selv, er det tid til at gå videre til næste fase: at forstå, hvad der skete. Du blev angrebet, angrebet kom fra, hvor du ikke forventede det, det var et uærligt, modbydeligt baghold. Det her er slemt. Det næste skridt er at forstå, hvorfor angriberen havde brug for dette. Virkelig hvorfor? Måske fordi han selv er rasende? Hvorfor er han sur? For eksempel fordi han skruede op for sig selv og ikke kan tage ansvar? Dette er måden at håndtere hele situationen omhyggeligt på. Men dette kræver manøvrerum, verbalt rum. Det allerførste skridt er at afbryde den verbale kontakt. Undgå diskussion med ord. Annuller det, gå væk så hurtigt som muligt. Hvis det er en telefonsamtale, skal du bare lægge røret på – det er en færdighed, jeg har lært af at kommunikere med min ekskone. Hvis samtalen ikke går nogen steder godt, skal du bare sige "farvel" og lægge på. Fra den anden side af telefonen: "bla bla bla", svarer du: "yeah, bye!" og læg på. Du afslutter bare samtalen. Fem minutter senere, når evnen til at tænke fornuftigt vender tilbage til dig, er du kølet lidt ned, det bliver muligt at tænke på alting, hvad der skete og hvad der nu skal ske. Og begynd at formulere et tankevækkende svar, i stedet for blot at reagere ud fra følelser. For mig var gennembruddet i selvbevidsthed netop det faktum, at jeg i tilfælde af følelsesmæssig stress ikke kan tale. At komme ud af denne tilstand, tænke og planlægge, hvordan man reagerer og kompenserer for problemer - det er de rigtige trin i tilfældet, når man ikke kan tale. Den nemmeste måde er at løbe væk fra den situation, hvor følelsesmæssig stress viser sig og simpelthen stoppe med at deltage i denne stress. Derefter bliver du i stand til at tænke, når du kan tænke, bliver du i stand til at tale og så videre.

Forresten, i retten forsøger den modsatte advokat at gøre dette mod dig - nu er det klart hvorfor. Fordi han har evnen til at undertrykke dig til en sådan tilstand, at du for eksempel ikke engang kan udtale dit navn. I en meget reel forstand vil du ikke være i stand til at tale. Hvis dette sker for dig, og hvis du ved, at du vil befinde dig et sted, hvor verbale kampe raser, et sted som en domstol, så kan du komme med din advokat. Advokaten vil stå op for dig og stoppe det verbale angreb, og vil gøre det på en helt lovlig måde, og det tabte Zen-rum vender tilbage til dig. For eksempel var jeg nødt til at ringe til min familie et par gange, dommeren var ret venlig omkring dette, men den modsatte advokat skreg og råbte af mig, jeg kunne ikke engang få et ord ind. I disse tilfælde fungerer det bedst for mig at bruge en mediator. Formidleren stopper alt dette pres, der strømmer på dig i en kontinuerlig strøm, du finder det nødvendige Zen-rum, og med det vender evnen til at tale tilbage. Dette er et helt vidensfelt, hvor der er meget at studere, meget at opdage i dig selv, og alt dette bliver til strategiske beslutninger på højt niveau, der er forskellige for forskellige mennesker. Nogle mennesker har ikke de problemer, der er beskrevet ovenfor; normalt har folk, der er professionelle sælgere, ikke dem. Alle disse mennesker, der lever af ord - berømte sangere, digtere, religiøse ledere og politikere, de har altid noget at sige. De har ikke sådanne problemer, men det har jeg.

Andrew: Det var... uventet. Godt, vi har allerede talt meget, og det er tid til at afslutte dette interview. Vi vil helt sikkert mødes til konferencen og vil kunne fortsætte denne dialog. Vi ses på Hydra!

Du kan fortsætte din samtale med Cliff på Hydra 2019-konferencen, som afholdes den 11.-12. juli 2019 i St. Petersborg. Han kommer med en rapport "Azul Hardware Transactional Memory-oplevelsen". Billetter kan købes på den officielle hjemmeside.

Kilde: www.habr.com

Tilføj en kommentar