Mahusay na panayam kay Cliff Click, ang ama ng JIT compilation sa Java

Mahusay na panayam kay Cliff Click, ang ama ng JIT compilation sa JavaCliff Click β€” CTO ng Cratus (IoT sensors para sa pagpapabuti ng proseso), founder at co-founder ng ilang mga startup (kabilang ang Rocket Realtime School, Neurensic at H2O.ai) na may ilang matagumpay na paglabas. Isinulat ni Cliff ang kanyang unang compiler sa edad na 15 (Pascal para sa TRS Z-80)! Kilala siya sa kanyang trabaho sa C2 sa Java (ang Sea of ​​Nodes IR). Ipinakita ng compiler na ito sa mundo na ang JIT ay maaaring gumawa ng mataas na kalidad na code, na isa sa mga salik sa paglitaw ng Java bilang isa sa mga pangunahing modernong software platform. Pagkatapos ay tinulungan ni Cliff ang Azul Systems na bumuo ng 864-core mainframe na may purong Java software na sumusuporta sa mga pag-pause ng GC sa isang 500-gigabyte na heap sa loob ng 10 milliseconds. Sa pangkalahatan, nagawa ni Cliff ang lahat ng aspeto ng JVM.

 
Ang habrapost na ito ay isang magandang panayam kay Cliff. Pag-uusapan natin ang mga sumusunod na paksa:

  • Paglipat sa mababang antas ng pag-optimize
  • Paano gumawa ng isang malaking refactoring
  • Modelo ng gastos
  • Mababang antas ng pagsasanay sa pag-optimize
  • Mga praktikal na halimbawa ng pagpapabuti ng pagganap
  • Bakit lumikha ng iyong sariling programming language
  • Karera ng Performance Engineer
  • Mga Hamon sa Teknikal
  • Kaunti tungkol sa pagpaparehistro ng paglalaan at multi-core
  • Ang pinakamalaking hamon sa buhay

Ang panayam ay isinasagawa ni:

  • Andrey Satarin mula sa Amazon Web Services. Sa kanyang karera, nagawa niyang magtrabaho sa ganap na magkakaibang mga proyekto: sinubukan niya ang database na ipinamamahagi ng NewSQL sa Yandex, isang cloud detection system sa Kaspersky Lab, isang multiplayer na laro sa Mail.ru at isang serbisyo para sa pagkalkula ng mga presyo ng foreign exchange sa Deutsche Bank. Interesado sa pagsubok ng malakihang backend at mga distributed system.
  • Vladimir Sitnikov mula sa Netcracker. Sampung taon ng trabaho sa pagganap at scalability ng NetCracker OS, software na ginagamit ng mga operator ng telecom para i-automate ang mga proseso ng pamamahala ng network at network equipment. Interesado sa mga isyu sa pagganap ng Java at Oracle Database. May-akda ng higit sa isang dosenang mga pagpapahusay sa pagganap sa opisyal na driver ng PostgreSQL JDBC.

Paglipat sa mababang antas ng pag-optimize

Andrew: Isa kang malaking pangalan sa mundo ng JIT compilation, Java, at performance work sa pangkalahatan, tama ba? 

talampas: Parang ganun!

Andrew: Magsimula tayo sa ilang pangkalahatang tanong tungkol sa pagganap ng trabaho. Ano sa palagay mo ang pagpili sa pagitan ng mataas na antas at mababang antas ng pag-optimize tulad ng pagtatrabaho sa antas ng CPU?

talampas: Oo, ang lahat ay simple dito. Ang pinakamabilis na code ay ang hindi kailanman tumatakbo. Samakatuwid, kailangan mong palaging magsimula mula sa isang mataas na antas, magtrabaho sa mga algorithm. Ang isang mas mahusay na O notation ay matatalo ang isang mas masahol na O notation maliban kung ang ilang malalaking mga constants ay mamagitan. Ang mga bagay na mababa ang antas ay tumatagal. Karaniwan, kung na-optimize mo nang maayos ang natitirang bahagi ng iyong stack at mayroon pa ring ilang mga kawili-wiling bagay na natitira, mababa ang antas na iyon. Ngunit paano magsimula sa isang mataas na antas? Paano mo malalaman na sapat na ang mataas na antas ng trabaho? Well... hindi pwede. Walang mga handa na mga recipe. Kailangan mong maunawaan ang problema, magpasya kung ano ang iyong gagawin (upang hindi gumawa ng mga hindi kinakailangang hakbang sa hinaharap) at pagkatapos ay maaari mong alisan ng takip ang profiler, na maaaring magsabi ng isang bagay na kapaki-pakinabang. Sa isang punto, napagtanto mo mismo na naalis mo na ang mga hindi kinakailangang bagay at oras na para gumawa ng ilang mababang antas ng fine tuning. Ito ay tiyak na isang espesyal na uri ng sining. Maraming tao ang gumagawa ng mga hindi kinakailangang bagay, ngunit napakabilis na gumagalaw na wala silang oras na mag-alala tungkol sa pagiging produktibo. Ngunit ito ay hanggang sa ang tanong ay bumangon nang tahasan. Karaniwan 99% ng oras na walang nagmamalasakit sa aking ginagawa, hanggang sa sandaling dumating ang isang mahalagang bagay sa kritikal na landas na walang pakialam. At dito nagsimulang mag-alala ang lahat tungkol sa "bakit hindi ito gumana nang perpekto sa simula pa lang." Sa pangkalahatan, palaging may dapat pagbutihin sa pagganap. Ngunit 99% ng oras na wala kang mga lead! Sinusubukan mo lang na gumawa ng isang bagay at sa proseso ay malalaman mo kung ano ang mahalaga. Hindi mo malalaman nang maaga na ang piraso na ito ay kailangang maging perpekto, kaya, sa katunayan, kailangan mong maging perpekto sa lahat ng bagay. Ngunit ito ay imposible at hindi mo ito gagawin. Palaging maraming bagay ang dapat ayusin - at iyon ay ganap na normal.

Paano gumawa ng isang malaking refactoring

Andrew: Paano ka nagtatrabaho sa isang pagganap? Ito ay isang cross-cutting na problema. Halimbawa, kinailangan mo na bang gumawa ng mga problemang nagmumula sa intersection ng maraming umiiral na functionality?

talampas: Sinusubukan kong iwasan ito. Kung alam kong magiging isyu ang performance, iniisip ko ito bago ako magsimulang mag-coding, lalo na sa mga istruktura ng data. Ngunit madalas mong matuklasan ang lahat ng ito sa ibang pagkakataon. At pagkatapos ay kailangan mong pumunta sa matinding mga hakbang at gawin ang tinatawag kong "rewrite and conquer": kailangan mong kumuha ng sapat na malaking piraso. Ang ilan sa mga code ay kailangan pa ring muling isulat dahil sa mga problema sa pagganap o iba pa. Anuman ang dahilan ng muling pagsusulat ng code, halos palaging mas mahusay na muling isulat ang isang mas malaking piraso kaysa sa isang mas maliit na piraso. Sa sandaling ito, ang lahat ay nagsisimulang manginig sa takot: "Oh Diyos ko, hindi mo mahawakan ang napakaraming code!" Ngunit sa katunayan, ang pamamaraang ito ay halos palaging gumagana nang mas mahusay. Kailangan mong harapin kaagad ang isang malaking problema, gumuhit ng isang malaking bilog sa paligid nito at sabihin: Isusulat ko muli ang lahat sa loob ng bilog. Ang hangganan ay mas maliit kaysa sa nilalaman sa loob nito na kailangang palitan. At kung ang gayong delineation ng mga hangganan ay nagpapahintulot sa iyo na gawin ang gawain sa loob nang perpekto, ang iyong mga kamay ay libre, gawin kung ano ang gusto mo. Kapag naunawaan mo na ang problema, ang proseso ng muling pagsulat ay mas madali, kaya't kumain nang husto!
Kasabay nito, kapag gumawa ka ng isang malaking muling pagsulat at napagtanto na ang pagganap ay magiging isang isyu, maaari mong agad na magsimulang mag-alala tungkol dito. Karaniwan itong nagiging mga simpleng bagay tulad ng "huwag kumopya ng data, pamahalaan ang data nang simple hangga't maaari, gawin itong maliit." Sa malalaking muling pagsusulat, may mga karaniwang paraan upang mapabuti ang pagganap. At halos palaging umiikot sila sa data.

Modelo ng gastos

Andrew: Sa isa sa mga podcast na pinag-usapan mo ang tungkol sa mga modelo ng gastos sa konteksto ng pagiging produktibo. Maaari mo bang ipaliwanag kung ano ang ibig mong sabihin dito?

talampas: Oo naman. Ipinanganak ako sa panahon kung kailan napakahalaga ng pagganap ng processor. At muling nagbabalik ang panahong ito - ang kapalaran ay hindi walang kabalintunaan. Nagsimula akong mabuhay sa mga araw ng walong-bit na mga makina; ang aking unang computer ay gumana sa 256 bytes. Eksakto bytes. Napakaliit ng lahat. Kailangang bilangin ang mga tagubilin, at nang magsimula kaming umakyat sa stack ng programming language, dumami ang mga wika. Mayroong Assembler, pagkatapos ay Basic, pagkatapos C, at C ang nag-asikaso ng maraming detalye, tulad ng paglalaan ng rehistro at pagpili ng pagtuturo. Ngunit ang lahat ay medyo malinaw doon, at kung gumawa ako ng isang pointer sa isang halimbawa ng isang variable, pagkatapos ay makakakuha ako ng load, at ang halaga ng pagtuturo na ito ay kilala. Ang hardware ay gumagawa ng isang tiyak na bilang ng mga cycle ng makina, kaya ang bilis ng pagpapatupad ng iba't ibang mga bagay ay maaaring kalkulahin sa pamamagitan lamang ng pagdaragdag ng lahat ng mga tagubilin na iyong tatakbo. Ang bawat paghahambing/pagsusulit/branch/tawag/load/store ay maaaring idagdag at sabihin: iyon ang oras ng pagpapatupad para sa iyo. Kapag nagtatrabaho sa pagpapabuti ng pagganap, tiyak na bibigyan mo ng pansin kung anong mga numero ang tumutugma sa maliliit na mainit na siklo. 
Ngunit sa sandaling lumipat ka sa Java, Python at mga katulad na bagay, mabilis kang lumayo sa mababang antas ng hardware. Magkano ang halaga ng pagtawag sa isang getter sa Java? Kung tama ang JIT sa HotSpot inline, ito ay maglo-load, ngunit kung hindi nito ginawa ito, ito ay isang function na tawag. Dahil nasa mainit na loop ang tawag, i-override nito ang lahat ng iba pang pag-optimize sa loop na iyon. Samakatuwid, ang tunay na gastos ay magiging mas mataas. At agad kang nawalan ng kakayahang tumingin sa isang piraso ng code at maunawaan na dapat namin itong isagawa sa mga tuntunin ng bilis ng orasan ng processor, memorya at cache na ginamit. Ang lahat ng ito ay nagiging kawili-wili lamang kung talagang pumasok ka sa pagganap.
Ngayon nakita namin ang aming sarili sa isang sitwasyon kung saan ang bilis ng processor ay halos hindi tumaas sa loob ng isang dekada. Ang mga lumang araw ay bumalik! Hindi ka na makakaasa sa magandang single-threaded performance. Ngunit kung bigla kang pumasok sa parallel computing, ito ay hindi kapani-paniwalang mahirap, lahat ay tumingin sa iyo tulad ng James Bond. Sampung beses na mga acceleration dito ay kadalasang nangyayari sa mga lugar kung saan may nanggugulo. Ang concurrency ay nangangailangan ng maraming trabaho. Upang makuha ang XNUMXx na speedup na iyon, kailangan mong maunawaan ang modelo ng gastos. Ano at magkano ang halaga nito? At para magawa ito, kailangan mong maunawaan kung paano umaangkop ang dila sa pinagbabatayan na hardware.
Pinili ni Martin Thompson ang isang magandang salita para sa kanyang blog Mekanikal na pakikiramay! Kailangan mong maunawaan kung ano ang gagawin ng hardware, kung paano ito eksaktong gagawin, at kung bakit ginagawa nito ang ginagawa nito sa unang lugar. Gamit ito, medyo madaling simulan ang pagbilang ng mga tagubilin at pag-alam kung saan pupunta ang oras ng pagpapatupad. Kung wala kang angkop na pagsasanay, naghahanap ka lamang ng isang itim na pusa sa isang madilim na silid. Nakikita ko ang mga taong nag-o-optimize sa pagganap sa lahat ng oras na walang ideya kung ano ang ginagawa nila. Marami silang paghihirap at hindi gaanong umuunlad. At kapag kinuha ko ang parehong piraso ng code, ipasok ang ilang maliliit na hack at makakuha ng lima o sampung beses na speedup, ang mga ito ay tulad ng: well, hindi iyon patas, alam na namin na mas mahusay ka. Kahanga-hanga. Ano ang pinag-uusapan ko... ang modelo ng gastos ay tungkol sa kung anong uri ng code ang isinusulat mo at kung gaano ito kabilis tumakbo sa average sa malaking larawan.

Andrew: At paano mo mapapanatili ang ganoong volume sa iyong ulo? Nakamit ba ito nang may higit na karanasan, o? Saan nagmula ang gayong karanasan?

talampas: Buweno, hindi ko nakuha ang aking karanasan sa pinakamadaling paraan. Nagprograma ako sa Assembly noong mga araw na naiintindihan mo ang bawat pagtuturo. Parang hangal, ngunit mula noon ang set ng pagtuturo ng Z80 ay palaging nananatili sa aking ulo, sa aking memorya. Hindi ko matandaan ang mga pangalan ng mga tao sa loob ng isang minuto ng pakikipag-usap, ngunit naaalala ko ang code na isinulat 40 taon na ang nakakaraan. Nakakatawa, parang sindrom eh"idiot scientist'.

Mababang antas ng pagsasanay sa pag-optimize

Andrew: Mayroon bang mas madaling paraan para makapasok?

talampas: Oo at hindi. Ang hardware na ginagamit nating lahat ay hindi gaanong nagbago sa paglipas ng panahon. Gumagamit ang lahat ng x86, maliban sa mga Arm smartphone. Kung hindi ka gumagawa ng ilang uri ng hardcore na pag-embed, ginagawa mo ang parehong bagay. Okay, sa susunod. Ang mga tagubilin ay hindi rin nagbabago sa loob ng maraming siglo. Kailangan mong pumunta at magsulat ng isang bagay sa Assembly. Hindi gaanong, ngunit sapat na upang simulan upang maunawaan. Nakangiti ka, pero seryoso akong nagsasalita. Kailangan mong maunawaan ang pagsusulatan sa pagitan ng wika at hardware. Pagkatapos nito kailangan mong pumunta at magsulat ng kaunti at gumawa ng isang maliit na laruang compiler para sa isang maliit na laruang wika. Tulad ng laruan ay nangangahulugan na kailangan itong gawin sa isang makatwirang tagal ng panahon. Maaari itong maging sobrang simple, ngunit dapat itong bumuo ng mga tagubilin. Ang pagkilos ng pagbuo ng isang pagtuturo ay makakatulong sa iyo na maunawaan ang modelo ng gastos para sa tulay sa pagitan ng mataas na antas na code na isinusulat ng lahat at ang machine code na tumatakbo sa hardware. Ang sulat na ito ay masusunog sa utak sa oras na isinulat ang compiler. Kahit na ang pinakasimpleng compiler. Pagkatapos nito, maaari mong simulan ang pagtingin sa Java at ang katotohanan na ang semantic chasm nito ay mas malalim, at mas mahirap na magtayo ng mga tulay sa ibabaw nito. Sa Java, mas mahirap maunawaan kung naging mabuti o masama ang ating tulay, ano ang magiging sanhi ng pagkawasak nito at kung ano ang hindi. Ngunit kailangan mo ng isang uri ng panimulang punto kung saan titingnan mo ang code at nauunawaan: "oo, ang getter na ito ay dapat na naka-inline sa bawat oras." At pagkatapos ay lumalabas na kung minsan ay nangyayari ito, maliban sa sitwasyon kapag ang pamamaraan ay nagiging masyadong malaki, at ang JIT ay nagsisimulang i-inlining ang lahat. Ang pagganap ng mga naturang lugar ay maaaring mahulaan kaagad. Karaniwang gumagana nang maayos ang mga getter, ngunit pagkatapos ay tumingin ka sa malalaking maiinit na loop at napagtanto mo na may ilang function call na lumulutang doon na hindi alam kung ano ang kanilang ginagawa. Ito ang problema sa malawakang paggamit ng mga getter, ang dahilan kung bakit hindi sila inline ay hindi malinaw kung sila ay isang getter. Kung mayroon kang napakaliit na code base, maaari mo lamang itong tandaan at pagkatapos ay sabihin: ito ay isang getter, at ito ay isang setter. Sa isang malaking code base, ang bawat function ay nabubuhay ng sarili nitong kasaysayan, na, sa pangkalahatan, ay hindi alam ng sinuman. Sinasabi ng profiler na nawala kami ng 24% ng oras sa ilang loop at upang maunawaan kung ano ang ginagawa ng loop na ito, kailangan naming tingnan ang bawat function sa loob. Imposibleng maunawaan ito nang hindi pinag-aaralan ang function, at ito ay seryosong nagpapabagal sa proseso ng pag-unawa. Kaya lang hindi ako gumagamit ng getter at setter, bagong level na ako!
Saan makukuha ang modelo ng gastos? Well, may mababasa ka, siyempre... Pero sa tingin ko ang pinakamagandang paraan ay ang kumilos. Ang paggawa ng isang maliit na compiler ay ang pinakamahusay na paraan upang maunawaan ang modelo ng gastos at ibagay ito sa iyong sariling ulo. Ang isang maliit na compiler na magiging angkop para sa pagprograma ng microwave ay isang gawain para sa isang baguhan. Well, I mean, kung mayroon ka nang programming skills, dapat sapat na iyon. Ang lahat ng mga bagay na ito tulad ng pag-parse ng isang string na mayroon ka bilang isang uri ng algebraic expression, pagkuha ng mga tagubilin para sa mga pagpapatakbo ng matematika mula doon sa tamang pagkakasunud-sunod, pagkuha ng mga tamang halaga mula sa mga rehistro - lahat ng ito ay ginagawa nang sabay-sabay. At habang ginagawa mo ito, ito ay itatatak sa iyong utak. Sa tingin ko alam ng lahat kung ano ang ginagawa ng isang compiler. At ito ay magbibigay ng pag-unawa sa modelo ng gastos.

Mga praktikal na halimbawa ng pagpapabuti ng pagganap

Andrew: Ano pa ang dapat mong bigyang pansin kapag nagtatrabaho sa pagiging produktibo?

talampas: Mga istruktura ng data. Oo nga pala, matagal na akong hindi nagtuturo sa mga klaseng ito... Rocket School. Masaya ito, ngunit nangangailangan ito ng maraming pagsisikap, at mayroon din akong buhay! OK. Kaya, sa isa sa malaki at kawili-wiling mga klase, "Saan napupunta ang iyong pagganap," binigyan ko ang mga mag-aaral ng isang halimbawa: dalawa at kalahating gigabytes ng data ng fintech ay binasa mula sa isang CSV file at pagkatapos ay kinailangan nilang kalkulahin ang bilang ng mga produktong naibenta . Regular na tiktikan ang data ng merkado. Ang mga UDP packet ay na-convert sa format ng teksto mula noong 70s. Chicago Mercantile Exchange - lahat ng uri ng mga bagay tulad ng mantikilya, mais, soybeans, mga bagay na tulad niyan. Kinakailangang bilangin ang mga produktong ito, ang bilang ng mga transaksyon, ang average na dami ng paggalaw ng mga pondo at kalakal, atbp. Ito ay medyo simpleng matematika ng kalakalan: hanapin ang code ng produkto (1-2 character iyon sa hash table), kunin ang halaga, idagdag ito sa isa sa mga trade set, magdagdag ng volume, magdagdag ng halaga, at ilang iba pang bagay. Napakasimpleng math. Ang pagpapatupad ng laruan ay napaka-simple: lahat ay nasa isang file, binasa ko ang file at inilipat ito, hinahati ang mga indibidwal na tala sa mga string ng Java, hinahanap ang mga kinakailangang bagay sa mga ito at idinagdag ang mga ito ayon sa matematika na inilarawan sa itaas. At ito ay gumagana sa ilang mababang bilis.

Sa diskarteng ito, malinaw kung ano ang nangyayari, at ang parallel computing ay hindi makakatulong, tama? Lumalabas na ang limang beses na pagtaas sa pagganap ay maaaring makamit sa pamamagitan lamang ng pagpili ng mga tamang istruktura ng data. At ito ay sorpresa kahit na nakaranas ng mga programmer! Sa aking partikular na kaso, ang lansihin ay hindi ka dapat gumawa ng mga paglalaan ng memorya sa isang mainit na loop. Well, hindi ito ang buong katotohanan, ngunit sa pangkalahatan - hindi mo dapat i-highlight ang "minsan sa X" kapag ang X ay sapat na malaki. Kapag ang X ay dalawa't kalahating gigabytes, hindi ka dapat maglaan ng anumang bagay na "isang beses sa bawat letra", o "isang beses sa bawat linya", o "isang beses sa bawat patlang", anumang bagay na tulad nito. Dito ginugugol ang oras. Paano ito gumagana? Imagine, tumatawag ako String.split() o BufferedReader.readLine(). Readline gumagawa ng string mula sa isang hanay ng mga byte na dumating sa network, isang beses para sa bawat linya, para sa bawat isa sa daan-daang milyong linya. Kinukuha ko ang linyang ito, pina-parse at itinapon. Bakit ko itinatapon - mabuti, naproseso ko na, iyon lang. Kaya, para sa bawat byte na nabasa mula sa 2.7G na ito, dalawang character ang isusulat sa linya, iyon ay, 5.4G na, at hindi ko na kailangan ang mga ito para sa anumang bagay, kaya itinapon sila. Kung titingnan mo ang bandwidth ng memorya, naglo-load kami ng 2.7G na dumadaan sa memorya at memory bus sa processor, at pagkatapos ay dalawang beses na mas marami ang ipinadala sa linya na nakahiga sa memorya, at ang lahat ng ito ay nasira kapag ang bawat bagong linya ay nilikha. Ngunit kailangan kong basahin ito, binabasa ito ng hardware, kahit na ang lahat ay nasira sa bandang huli. At kailangan kong isulat ito dahil gumawa ako ng isang linya at ang mga cache ay puno - ang cache ay hindi maaaring tumanggap ng 2.7G. Kaya, para sa bawat byte na nabasa ko, nagbasa ako ng dalawa pang byte at sumulat ng dalawa pang byte, at sa huli mayroon silang 4:1 ratio - sa ratio na ito ay nag-aaksaya kami ng bandwidth ng memorya. At pagkatapos ay lumalabas na kung gagawin ko String.split() – hindi ito ang huling beses na gagawin ko ito, maaaring may isa pang 6-7 na field sa loob. Kaya ang klasikong code ng pagbabasa ng CSV at pagkatapos ay pag-parse ng mga string ay nag-aaksaya ng halos 14:1 ng memory bandwidth na talagang gusto mo. Kung itatapon mo ang mga pagpipiliang ito, maaari kang makakuha ng limang beses na bilis.

At hindi naman ganoon kahirap. Kung titingnan mo ang code mula sa tamang anggulo, ang lahat ay nagiging simple kapag napagtanto mo ang problema. Hindi mo dapat ihinto ang lahat ng paglalaan ng memorya: ang tanging problema ay ang paglalaan mo ng isang bagay at agad itong namatay, at sa kahabaan ng paraan ay sinusunog nito ang isang mahalagang mapagkukunan, na sa kasong ito ay memory bandwidth. At ang lahat ng ito ay nagreresulta sa pagbaba ng produktibidad. Sa x86 karaniwang kailangan mong aktibong magsunog ng mga cycle ng processor, ngunit dito mo sinunog ang lahat ng memorya nang mas maaga. Ang solusyon ay upang bawasan ang dami ng discharge. 
The other part of the problem is that if you run the profiler when the memory stripe run out, right when it happens, you're usually waiting for the cache to come back kasi puro basura na kakaproduce mo lang, lahat ng lines na yan. Samakatuwid, ang bawat pag-load o pagpapatakbo ng tindahan ay nagiging mabagal, dahil humantong sila sa mga pagkukulang ng cache - ang buong cache ay naging mabagal, naghihintay na umalis ang basura dito. Samakatuwid, ang profiler ay magpapakita lamang ng mainit na random na ingay na pinahiran sa buong loop - walang hiwalay na mainit na pagtuturo o lugar sa code. Ang ingay lang. At kung titingnan mo ang mga GC cycle, lahat sila ay Young Generation at napakabilis - microseconds o milliseconds maximum. Pagkatapos ng lahat, ang lahat ng alaalang ito ay namatay kaagad. Naglalaan ka ng bilyun-bilyong gigabytes, at pinutol niya ang mga ito, at pinutol ang mga ito, at pinutol muli ang mga ito. Ang lahat ng ito ay nangyayari nang napakabilis. Lumalabas na may mga murang GC cycle, mainit na ingay sa buong cycle, ngunit gusto naming makakuha ng 5x speedup. Sa sandaling ito, may isang bagay na dapat sumakit sa iyong ulo at tunog: "bakit ito?!" Ang memory strip overflow ay hindi ipinapakita sa klasikong debugger; kailangan mong patakbuhin ang hardware performance counter debugger at makita ito nang direkta at direkta. Ngunit hindi ito direktang mapaghihinalaan mula sa tatlong sintomas na ito. Ang pangatlong sintomas ay kapag tiningnan mo kung ano ang iyong na-highlight, tanungin ang profiler, at sumagot siya: "Nakagawa ka ng isang bilyong row, ngunit nagtrabaho ang GC nang libre." Sa sandaling mangyari ito, napagtanto mo na gumawa ka ng masyadong maraming mga bagay at nasunog ang buong memory lane. Mayroong isang paraan upang malaman ito, ngunit hindi ito halata. 

Ang problema ay nasa istraktura ng data: ang hubad na istraktura na pinagbabatayan ng lahat ng nangyayari, ito ay masyadong malaki, ito ay 2.7G sa disk, kaya ang paggawa ng isang kopya ng bagay na ito ay napaka hindi kanais-nais - gusto mong i-load ito kaagad mula sa network byte buffer sa mga rehistro, upang hindi magbasa-magsulat sa linya nang pabalik-balik nang limang beses. Sa kasamaang palad, hindi ka binibigyan ng Java ng ganoong library bilang bahagi ng JDK bilang default. Ngunit ito ay walang kuwenta, tama? Sa pangkalahatan, ito ay 5-10 linya ng code na gagamitin upang ipatupad ang iyong sariling buffered string loader, na inuulit ang gawi ng string class, habang ito ay isang wrapper sa paligid ng pinagbabatayan na byte buffer. Bilang isang resulta, lumalabas na ikaw ay nagtatrabaho halos na parang may mga string, ngunit sa katunayan ang mga pointer sa buffer ay gumagalaw doon, at ang mga raw byte ay hindi kinokopya kahit saan, at sa gayon ang parehong mga buffer ay muling ginagamit nang paulit-ulit, at ang operating system ay masaya na kunin ang iyong sarili sa mga bagay na idinisenyo para sa, tulad ng nakatagong double-buffering ng mga byte buffer na ito, at hindi ka na nakikinig sa walang katapusang stream ng hindi kinakailangang data. Sa pamamagitan ng paraan, naiintindihan mo ba na kapag nagtatrabaho sa GC, ginagarantiyahan na ang bawat paglalaan ng memorya ay hindi makikita ng processor pagkatapos ng huling GC cycle? Samakatuwid, ang lahat ng ito ay hindi maaaring nasa cache, at pagkatapos ay isang 100% na garantisadong miss ang nangyayari. Kapag nagtatrabaho sa isang pointer, sa x86, ang pagbabawas ng isang rehistro mula sa memorya ay tumatagal ng 1-2 na mga siklo ng orasan, at sa sandaling mangyari ito, magbabayad ka, magbabayad, magbayad, dahil ang memorya ay nasa lahat. SIYAM na cache – at ito ang halaga ng paglalaan ng memorya. Totoong halaga.

Sa madaling salita, ang mga istruktura ng data ang pinakamahirap na baguhin. At kapag napagtanto mo na napili mo ang maling istraktura ng data na pumatay sa pagganap sa susunod, kadalasan ay maraming trabaho ang dapat gawin, ngunit kung hindi mo gagawin, lalala ang mga bagay. Una sa lahat, kailangan mong mag-isip tungkol sa mga istruktura ng data, ito ay mahalaga. Ang pangunahing gastos dito ay nahuhulog sa mga matabang istruktura ng data, na nagsisimula nang gamitin sa istilong "Kinopya ko ang istraktura ng data X sa istruktura ng data na Y dahil mas gusto ko ang hugis ng Y." Ngunit ang operasyon ng pagkopya (na tila mura) ay talagang nag-aaksaya ng bandwidth ng memorya at doon nakalibing ang lahat ng nasayang na oras ng pagpapatupad. Kung mayroon akong higanteng string ng JSON at gusto kong gawing structured DOM tree ng mga POJO o iba pa, ang pagpapatakbo ng pag-parse ng string na iyon at pagbuo ng POJO, at pagkatapos ay pag-access muli sa POJO sa ibang pagkakataon, ay magreresulta sa hindi kinakailangang gastos - ito ay hindi mura. Maliban kung tumatakbo ka sa paligid ng mga POJO nang mas madalas kaysa sa pagtakbo mo sa paligid ng isang string. Sa halip, maaari mong subukang i-decrypt ang string at kunin lamang ang kailangan mo mula doon, nang hindi ito ginagawang anumang POJO. Kung ang lahat ng ito ay nangyari sa isang landas kung saan kinakailangan ang maximum na pagganap, walang mga POJO para sa iyo, kailangan mong direktang maghukay sa linya.

Bakit lumikha ng iyong sariling programming language

Andrew: Sinabi mo na upang maunawaan ang modelo ng gastos, kailangan mong magsulat ng iyong sariling maliit na wika...

talampas: Hindi isang wika, ngunit isang compiler. Ang isang wika at isang compiler ay dalawang magkaibang bagay. Ang pinakamahalagang pagkakaiba ay nasa iyong ulo. 

Andrew: Oo nga pala, sa pagkakaalam ko, nag-eeksperimento ka sa paglikha ng sarili mong mga wika. Para saan?

talampas: Dahil kaya ko! I'm semi-retired, so ito ang hobby ko. Ipinatupad ko ang mga wika ng ibang tao sa buong buhay ko. Marami rin akong ginawa sa aking coding style. At dahil din sa nakikita ko ang mga problema sa ibang mga wika. Nakikita ko na may mas mahusay na mga paraan upang gawin ang mga pamilyar na bagay. At gagamitin ko sila. Pagod na akong makakita ng mga problema sa aking sarili, sa Java, sa Python, sa anumang iba pang wika. Nagsusulat ako ngayon sa React Native, JavaScript at Elm bilang isang libangan na hindi tungkol sa pagreretiro, ngunit tungkol sa aktibong trabaho. Sumulat din ako sa Python at, malamang, ay patuloy na gagana sa machine learning para sa mga backend ng Java. Mayroong maraming mga sikat na wika at lahat sila ay may mga kagiliw-giliw na tampok. Ang bawat isa ay mahusay sa kanilang sariling paraan at maaari mong subukang pagsamahin ang lahat ng mga tampok na ito. Kaya, pinag-aaralan ko ang mga bagay na interesado ako, ang pag-uugali ng wika, sinusubukang makabuo ng mga makatwirang semantika. At hanggang ngayon ay nagtagumpay ako! Sa ngayon ay nahihirapan ako sa memory semantics, dahil gusto kong magkaroon ito tulad ng sa C at Java, at makakuha ng isang malakas na memory model at memory semantics para sa mga load at store. Kasabay nito, magkaroon ng awtomatikong uri ng inference tulad ng sa Haskell. Dito, sinusubukan kong ihalo ang Haskell-like type inference sa memory work sa parehong C at Java. Ito ang ginagawa ko sa nakalipas na 2-3 buwan, halimbawa.

Andrew: Kung bubuo ka ng isang wika na kumukuha ng mas mahusay na mga aspeto mula sa iba pang mga wika, sa palagay mo ba ay gagawin ng isang tao ang kabaligtaran: kunin ang iyong mga ideya at gamitin ang mga ito?

talampas: Ito ay eksakto kung paano lumilitaw ang mga bagong wika! Bakit ang Java ay katulad ng C? Dahil ang C ay may magandang syntax na nauunawaan ng lahat at ang Java ay naging inspirasyon ng syntax na ito, pagdaragdag ng uri ng kaligtasan, array bounds checking, GC, at pinahusay din nila ang ilang bagay mula sa C. Nagdagdag sila ng sarili nila. Pero marami silang na-inspire, di ba? Ang lahat ay nakatayo sa balikat ng mga higante na nauna sa iyo - ganyan ang pag-unlad.

Andrew: Sa pagkakaintindi ko, magiging memory safe ang iyong wika. Naisip mo ba ang tungkol sa pagpapatupad ng isang bagay tulad ng isang borrow checker mula sa Rust? Tiningnan mo ba siya, ano ang tingin mo sa kanya?

talampas: Well, matagal na akong nagsusulat ng C, kasama ang lahat ng malloc na ito at libre, at manu-manong pinamamahalaan ang buhay. Alam mo, 90-95% ng manually controlled life time ay may parehong istraktura. At ito ay napaka, napakasakit na gawin ito nang manu-mano. Gusto kong sabihin sa iyo ng compiler kung ano ang nangyayari doon at kung ano ang iyong nakamit sa iyong mga aksyon. Para sa ilang bagay, ginagawa ito ng borrow checker sa labas ng kahon. At ito ay dapat na awtomatikong magpakita ng impormasyon, maunawaan ang lahat, at hindi maging pasanin sa akin sa paglalahad ng pag-unawang ito. Dapat itong gumawa ng hindi bababa sa lokal na pagsusuri sa pagtakas, at kung nabigo lamang ito, pagkatapos ay kailangan itong magdagdag ng mga uri ng anotasyon na maglalarawan sa panghabambuhay - at ang gayong pamamaraan ay mas kumplikado kaysa sa isang borrow checker, o sa katunayan anumang umiiral na memory checker. Ang pagpili sa pagitan ng "ang lahat ay maayos" at "Wala akong naiintindihan" - hindi, dapat mayroong isang bagay na mas mahusay. 
Kaya, bilang isang taong nagsulat ng maraming code sa C, sa tingin ko ang pagkakaroon ng suporta para sa awtomatikong panghabambuhay na kontrol ay ang pinakamahalagang bagay. Sawa na rin ako sa kung gaano karaming gumagamit ng memorya ang Java at ang pangunahing reklamo ay ang GC. Kapag naglaan ka ng memorya sa Java, hindi mo na maibabalik ang memorya na lokal noong huling GC cycle. Hindi ito ang kaso sa mga wikang may mas tumpak na pamamahala ng memorya. Kung tatawagan mo ang malloc, makukuha mo kaagad ang memorya na kadalasang ginagamit lang. Karaniwang ginagawa mo ang ilang mga pansamantalang bagay na may memorya at agad itong ibabalik. At agad itong bumalik sa malloc pool, at ang susunod na malloc cycle ay hinila muli. Samakatuwid, ang aktwal na paggamit ng memorya ay binabawasan sa hanay ng mga buhay na bagay sa isang partikular na oras, kasama ang mga pagtagas. At kung ang lahat ay hindi tumagas sa isang ganap na bastos na paraan, karamihan sa memorya ay napupunta sa mga cache at processor, at ito ay gumagana nang mabilis. Ngunit nangangailangan ng maraming manu-manong pamamahala ng memorya na may malloc at libreng tinatawag sa tamang pagkakasunud-sunod, sa tamang lugar. Ang kalawang ay maaaring hawakan ito nang maayos sa sarili nitong, at sa maraming mga kaso ay nagbibigay ng mas mahusay na pagganap, dahil ang pagkonsumo ng memorya ay pinaliit sa kasalukuyang pagkalkula lamang - kumpara sa paghihintay para sa susunod na GC cycle upang magbakante ng memorya. Bilang resulta, nakakuha kami ng isang napaka-kagiliw-giliw na paraan upang mapabuti ang pagganap. At medyo malakas - Ibig kong sabihin, ginawa ko ang mga ganoong bagay kapag nagpoproseso ng data para sa fintech, at ito ay nagbigay-daan sa akin na makakuha ng bilis ng halos limang beses. Malaking tulong iyon, lalo na sa mundo kung saan hindi bumibilis ang mga processor at naghihintay pa rin kami ng mga pagpapabuti.

Karera ng Performance Engineer

Andrew: Gusto ko ring magtanong tungkol sa mga karera sa pangkalahatan. Sumikat ka sa iyong JIT work sa HotSpot at pagkatapos ay lumipat sa Azul, na isa ring kumpanya ng JVM. Ngunit mas nagtatrabaho na kami sa hardware kaysa sa software. At pagkatapos ay bigla silang lumipat sa Big Data at Machine Learning, at pagkatapos ay sa pagtuklas ng panloloko. Paano ito nangyari? Ang mga ito ay ibang-iba na mga lugar ng pag-unlad.

talampas: Matagal na akong nagprograma at nakakuha ako ng maraming iba't ibang klase. At kapag sinabi ng mga tao: "oh, ikaw ang gumawa ng JIT para sa Java!", palaging nakakatawa. Ngunit bago iyon, nagtatrabaho ako sa isang clone ng PostScript - ang wika na minsang ginamit ng Apple para sa mga laser printer nito. At bago iyon gumawa ako ng pagpapatupad ng Forth na wika. Sa tingin ko ang karaniwang tema para sa akin ay ang pagbuo ng tool. Buong buhay ko, gumagawa ako ng mga tool kung saan isinusulat ng ibang tao ang kanilang mga cool na programa. Ngunit kasangkot din ako sa pagbuo ng mga operating system, mga driver, mga debugger sa antas ng kernel, mga wika para sa pag-unlad ng OS, na nagsimula nang walang halaga, ngunit sa paglipas ng panahon ay naging mas kumplikado. Ngunit ang pangunahing paksa ay ang pagbuo pa rin ng mga tool. Ang isang malaking bahagi ng aking buhay ay dumaan sa pagitan ng Azul at Sun, at ito ay tungkol sa Java. Ngunit nang makapasok ako sa Big Data at Machine Learning, ibinalik ko ang aking magarbong sumbrero at sinabing, "Oh, ngayon ay mayroon kaming hindi maliit na problema, at maraming mga kawili-wiling bagay na nangyayari at mga taong gumagawa ng mga bagay." Ito ay isang mahusay na landas sa pag-unlad na dapat gawin.

Oo, mahilig talaga ako sa distributed computing. Ang una kong trabaho ay bilang isang mag-aaral sa C, sa isang proyekto sa advertising. Ito ay ipinamahagi sa computing sa Zilog Z80 chips na nakolekta ng data para sa analog OCR, na ginawa ng isang tunay na analog analyzer. Ito ay isang cool at ganap na nakakabaliw na paksa. Ngunit may mga problema, ang ilang bahagi ay hindi nakilala nang tama, kaya kailangan mong kumuha ng isang larawan at ipakita ito sa isang tao na nababasa na ng kanilang mga mata at naiulat kung ano ang sinabi nito, at samakatuwid ay may mga trabaho na may data, at ang mga trabahong ito nagkaroon ng sariling wika. Mayroong isang backend na nagproseso ng lahat ng ito - ang mga Z80 ay tumatakbo nang kahanay ng mga terminal ng vt100 na tumatakbo - isa bawat tao, at mayroong isang parallel na modelo ng programming sa Z80. Ilang karaniwang piraso ng memorya na ibinabahagi ng lahat ng Z80 sa loob ng isang star configuration; Ibinahagi din ang backplane, at ang kalahati ng RAM ay ibinahagi sa loob ng network, at ang isa pang kalahati ay pribado o napunta sa ibang bagay. Isang makabuluhang kumplikadong parallel distributed system na may shared... semi-shared memory. Kailan ito... I can’t even remember, somewhere in the mid-80s. Medyo matagal na ang nakalipas. 
Oo, ipagpalagay natin na ang 30 taon ay medyo matagal na ang nakalipas. Ang mga problemang nauugnay sa distributed computing ay matagal nang umiral; ang mga tao ay matagal nang nakikipagdigma sa Beowulf-mga kumpol. Ang ganitong mga kumpol ay parang... Halimbawa: mayroong Ethernet at ang iyong mabilis na x86 ay konektado sa Ethernet na ito, at ngayon ay gusto mong makakuha ng pekeng nakabahaging memorya, dahil walang makakagawa ng distributed computing coding noon, ito ay napakahirap at samakatuwid ay naroon ay pekeng nakabahaging memorya na may mga pahina ng memorya ng proteksyon sa x86, at kung sumulat ka sa pahinang ito, sinabi namin sa iba pang mga processor na kung maa-access nila ang parehong nakabahaging memorya, kakailanganin itong i-load mula sa iyo, at sa gayon ay tulad ng isang protocol para sa pagsuporta lumabas ang cache coherence at software para dito. Kawili-wiling konsepto. Ang tunay na problema, siyempre, ay ibang bagay. Ang lahat ng ito ay nagtrabaho, ngunit mabilis kang nakakuha ng mga problema sa pagganap, dahil walang nakakaunawa sa mga modelo ng pagganap sa isang sapat na antas - kung anong mga pattern ng pag-access sa memorya ang naroroon, kung paano matiyak na ang mga node ay hindi walang katapusang nag-ping sa isa't isa, at iba pa.

Ang naisip ko sa H2O ay ang mga developer mismo ang may pananagutan sa pagtukoy kung saan nakatago ang parallelism at kung saan ito hindi. Nakabuo ako ng isang modelo ng coding na ginawang madali at simple ang pagsusulat ng high-performance code. Ngunit ang pagsusulat ng mabagal na tumatakbong code ay mahirap, ito ay magmumukhang masama. Kailangan mong seryosong subukang magsulat ng mabagal na code, kakailanganin mong gumamit ng mga hindi karaniwang pamamaraan. Ang braking code ay makikita sa unang tingin. Bilang resulta, karaniwan kang nagsusulat ng code na tumatakbo nang mabilis, ngunit kailangan mong malaman kung ano ang gagawin sa kaso ng shared memory. Ang lahat ng ito ay nakatali sa malalaking array at ang pag-uugali doon ay katulad ng hindi pabagu-bago ng mga malalaking array sa parallel Java. Ibig kong sabihin, isipin na ang dalawang thread ay sumulat sa isang parallel array, ang isa sa kanila ay nanalo, at ang isa pa, nang naaayon, ay natalo, at hindi mo alam kung alin ang isa. Kung ang mga ito ay hindi pabagu-bago, ang order ay maaaring maging anuman ang gusto mo - at ito ay talagang gumagana nang maayos. Talagang pinapahalagahan ng mga tao ang pagkakasunud-sunod ng mga operasyon, inilalagay nila ang pabagu-bago ng isip sa mga tamang lugar, at inaasahan nila ang mga problema sa pagganap na nauugnay sa memorya sa mga tamang lugar. Kung hindi, magsusulat lang sila ng code sa anyo ng mga loop mula 1 hanggang N, kung saan ang N ay ilang trilyon, sa pag-asa na ang lahat ng kumplikadong mga kaso ay awtomatikong magiging parallel - at hindi ito gagana doon. Ngunit sa H2O ito ay hindi Java o Scala; maaari mo itong isaalang-alang na "Java minus minus" kung gusto mo. Ito ay isang napakalinaw na istilo ng programming at katulad ng pagsusulat ng simpleng C o Java code na may mga loop at array. Ngunit sa parehong oras, ang memorya ay maaaring iproseso sa terabytes. Ginagamit ko pa rin ang H2O. Ginagamit ko ito paminsan-minsan sa iba't ibang mga proyekto - at ito pa rin ang pinakamabilis na bagay, dose-dosenang beses na mas mabilis kaysa sa mga katunggali nito. Kung gumagawa ka ng Big Data gamit ang columnar data, napakahirap talunin ang H2O.

Mga Hamon sa Teknikal

Andrew: Ano ang iyong pinakamalaking hamon sa iyong buong karera?

talampas: Tinatalakay ba natin ang teknikal o hindi teknikal na bahagi ng isyu? Masasabi kong ang pinakamalaking hamon ay hindi teknikal. 
Tulad ng para sa mga teknikal na hamon. Tinalo ko lang sila. Hindi ko alam kung ano ang pinakamalaki, ngunit may ilang medyo kawili-wili na tumagal ng kaunting oras, pakikibaka sa pag-iisip. Nang pumunta ako sa Sun, sigurado ako na gagawa ako ng mabilis na compiler, at isang grupo ng mga nakatatanda ang nagsabi bilang tugon na hinding-hindi ako magtatagumpay. Ngunit sinundan ko ang landas na ito, nagsulat ng isang compiler pababa sa rehistro ng allocator, at ito ay medyo mabilis. Ito ay kasing bilis ng modernong C1, ngunit ang allocator ay mas mabagal noon, at kung titingnan ito ay isang malaking problema sa istruktura ng data. Kailangan ko ito upang magsulat ng isang graphical register allocator at hindi ko naiintindihan ang problema sa pagitan ng code expressiveness at bilis, na umiral sa panahong iyon at napakahalaga. Ito ay lumabas na ang istraktura ng data ay karaniwang lumampas sa laki ng cache sa x86s ng oras na iyon, at samakatuwid, kung una kong ipinapalagay na ang rehistro ng allocator ay gagana ng 5-10 porsyento ng kabuuang oras ng jitter, kung gayon sa katotohanan ito ay naging 50 porsyento.

Sa paglipas ng panahon, ang compiler ay naging mas malinis at mas mahusay, huminto sa pagbuo ng kahila-hilakbot na code sa mas maraming kaso, at ang pagganap ay lalong nagsimulang maging katulad ng kung ano ang ginagawa ng isang C compiler. Maliban kung, siyempre, sumulat ka ng ilang crap na kahit C ay hindi nagpapabilis . Kung sumulat ka ng code tulad ng C, makakakuha ka ng pagganap tulad ng C sa mas maraming kaso. At habang patuloy ka pa, mas madalas kang nakakuha ng code na asymptotically coincided sa level C, nagsimulang magmukhang kumpleto ang register allocator... hindi alintana kung mabilis o mabagal ang iyong code. Nagpatuloy ako sa trabaho sa allocator para gawin itong mas mahusay na mga pagpipilian. Siya ay naging mas mabagal at mas mabagal, ngunit siya ay nagbigay ng mas mahusay at mas mahusay na pagganap sa mga kaso kung saan walang sinuman ang makayanan. Maaari akong sumisid sa isang rehistro allocator, ilibing ang isang buwan ng trabaho doon, at biglang ang buong code ay magsisimulang magsagawa ng 5% na mas mabilis. Nangyayari ito nang pana-panahon at ang tagapag-alok ng rehistro ay naging isang bagay ng isang gawa ng sining - ginusto ito o kinasusuklaman ng lahat, at ang mga tao mula sa akademya ay nagtanong sa paksang "bakit ginagawa ang lahat sa ganitong paraan", bakit hindi line scan, at ano ang pinagkaiba. Ang sagot ay pareho pa rin: ang isang allocator batay sa pangkulay ng graph at napakaingat na trabaho sa buffer code ay katumbas ng isang sandata ng tagumpay, ang pinakamahusay na kumbinasyon na walang sinuman ang maaaring talunin. At ito ay isang medyo hindi halatang bagay. Ang lahat ng bagay na ginagawa ng compiler doon ay medyo pinag-aralan nang mabuti, bagaman dinala din sila sa antas ng sining. Palagi akong gumagawa ng mga bagay na dapat gawing isang gawa ng sining ang compiler. Ngunit wala sa mga ito ay anumang bagay na pambihirang - maliban sa rehistro allocator. Ang daya ay mag-ingat putulin sa ilalim ng pag-load at, kung mangyari ito (maaari kong ipaliwanag nang mas detalyado kung interesado), nangangahulugan ito na maaari kang mag-inline nang mas agresibo, nang walang panganib na mahulog sa isang kink sa iskedyul ng pagganap. Sa mga araw na iyon, mayroong isang grupo ng mga full-scale compiler, na nakabitin na may mga baubles at whistles, na may mga rehistro ng allocator, ngunit walang ibang makakagawa nito.

Ang problema ay kung magdaragdag ka ng mga pamamaraan na napapailalim sa inlining, pagtaas at pagtaas ng inlining area, ang hanay ng mga ginamit na halaga ay agad na lumalampas sa bilang ng mga rehistro, at kailangan mong i-cut ang mga ito. Karaniwang dumarating ang kritikal na antas kapag sumuko ang allocator, at ang isang mahusay na kandidato para sa isang spill ay nagkakahalaga ng isa pa, magbebenta ka ng ilang karaniwang ligaw na bagay. Ang halaga ng inlining dito ay nawalan ka ng bahagi ng overhead, overhead para sa pagtawag at pag-save, maaari mong makita ang mga halaga sa loob at maaari pang i-optimize ang mga ito. Ang halaga ng inlining ay ang isang malaking bilang ng mga live na halaga ay nabuo, at kung ang iyong register allocator ay nasunog nang higit sa kinakailangan, agad kang natalo. Samakatuwid, ang karamihan sa mga allocator ay may problema: kapag ang inlining ay tumatawid sa isang tiyak na linya, ang lahat ng bagay sa mundo ay nagsisimulang maputol at ang pagiging produktibo ay maaaring ma-flush sa banyo. Ang mga nagpapatupad ng compiler ay nagdaragdag ng ilang heuristics: halimbawa, upang ihinto ang inlining, simula sa ilang sapat na laki, dahil ang mga paglalaan ay masisira ang lahat. Ito ay kung paano nabuo ang isang kink sa performance graph - ikaw ay inline, inline, ang pagganap ay dahan-dahang lumalaki - at pagkatapos ay boom! – bumagsak ito na parang swift jack dahil sa sobrang linya mo. Ito ay kung paano nagtrabaho ang lahat bago ang pagdating ng Java. Nangangailangan ang Java ng mas maraming inlining, kaya kinailangan kong gawing mas agresibo ang aking allocator upang ito ay mag-level out sa halip na mag-crash, at kung mag-inline ka nang sobra, ito ay magsisimulang dumaloy, ngunit pagkatapos ay darating pa rin ang "wala nang spilling" na sandali. Ito ay isang kawili-wiling obserbasyon at ito ay dumating sa akin nang wala saan, hindi halata, ngunit ito ay nagbunga ng maayos. Nagsagawa ako ng agresibong inlining at dinala ako sa mga lugar kung saan gumagana ang pagganap ng Java at C nang magkatabi. Ang mga ito ay talagang malapit - maaari akong magsulat ng Java code na mas mabilis kaysa sa C code at mga bagay na katulad nito, ngunit sa karaniwan, sa malaking larawan ng mga bagay, halos maihahambing ang mga ito. Sa tingin ko bahagi ng merito na ito ay ang rehistro allocator, na nagbibigay-daan sa akin upang inline bilang stupidly hangga't maaari. Inline ko lang lahat ng nakikita ko. Ang tanong dito ay kung gumagana nang maayos ang allocator, kung ang resulta ay matalinong gumaganang code. Ito ay isang malaking hamon: upang maunawaan ang lahat ng ito at gawin itong gumana.

Kaunti tungkol sa pagpaparehistro ng paglalaan at multi-core

Vladimir: Ang mga problema tulad ng paglalaan ng rehistro ay tila isang uri ng walang hanggan, walang katapusang paksa. Nagtataka ako kung mayroon bang ideya na tila nangangako at pagkatapos ay nabigo sa pagsasanay?

talampas: Oo naman! Ang alokasyon sa pagpaparehistro ay isang lugar kung saan sinusubukan mong maghanap ng ilang heuristics upang malutas ang isang NP-kumpletong problema. At hindi ka makakamit ang perpektong solusyon, tama ba? Ito ay imposible lamang. Tingnan, Ahead of Time compilation - hindi rin ito gumagana. Ang pag-uusap dito ay tungkol sa ilang karaniwang mga kaso. Tungkol sa tipikal na pagganap, upang maaari kang pumunta at sukatin ang isang bagay na sa tingin mo ay magandang tipikal na pagganap - pagkatapos ng lahat, nagsusumikap ka upang mapabuti ito! Ang pagpaparehistro ng alokasyon ay isang paksa tungkol sa pagganap. Sa sandaling mayroon ka ng unang prototype, ito ay gumagana at nagpinta kung ano ang kinakailangan, ang pagganap ng trabaho ay magsisimula. Kailangan mong matutong magsukat ng mabuti. Bakit ito mahalaga? Kung mayroon kang malinaw na data, maaari kang tumingin sa iba't ibang mga lugar at makita: oo, nakatulong ito dito, ngunit doon nasira ang lahat! May ilang magagandang ideya na lumalabas, nagdagdag ka ng mga bagong heuristics at biglang nagsimulang gumana nang kaunti ang lahat sa karaniwan. O hindi ito nagsisimula. Nagkaroon ako ng isang grupo ng mga kaso kung saan kami ay nakikipaglaban para sa limang porsyento na pagganap na naiiba ang aming pag-unlad mula sa nakaraang allocator. At sa tuwing ganito ang hitsura: kung saan manalo ka, sa isang lugar na matatalo ka. Kung mayroon kang mahusay na mga tool sa pagtatasa ng pagganap, mahahanap mo ang mga nawawalang ideya at maunawaan kung bakit nabigo ang mga ito. Marahil ito ay nagkakahalaga ng pag-iwan sa lahat ng bagay na ito, o maaaring gumawa ng isang mas seryosong diskarte sa fine-tuning, o lumabas at ayusin ang iba pa. Ito ay isang buong grupo ng mga bagay! Ginawa ko ang cool na hack na ito, ngunit kailangan ko rin ang isang ito, at ang isang ito, at ang isang ito - at ang kanilang kabuuang kumbinasyon ay nagbibigay ng ilang mga pagpapabuti. At ang mga loner ay maaaring mabigo. Ito ang katangian ng pagganap ng trabaho sa NP-kumpletong mga problema.

Vladimir: Nadarama ng isang tao na ang mga bagay tulad ng pagpipinta sa mga allocator ay isang problema na nalutas na. Buweno, ito ay napagpasyahan para sa iyo, batay sa kung ano ang iyong sinasabi, kaya sulit ba ito kung gayon...

talampas: Hindi ito naresolba nang ganoon. Ikaw ang dapat na gawing "solved". May mga mahihirap na problema at kailangan nilang lutasin. Kapag tapos na ito, oras na para magtrabaho sa pagiging produktibo. Kailangan mong lapitan ang gawaing ito nang naaayon - gumawa ng mga benchmark, mangolekta ng mga sukatan, ipaliwanag ang mga sitwasyon kung kailan, noong bumalik ka sa isang nakaraang bersyon, nagsimulang gumana muli ang iyong lumang hack (o vice versa, tumigil). At huwag sumuko hangga't hindi mo nakakamit ang isang bagay. Tulad ng sinabi ko na, kung may mga cool na ideya na hindi gumana, ngunit sa larangan ng paglalaan ng mga rehistro ng mga ideya ito ay humigit-kumulang na walang katapusan. Maaari mong, halimbawa, magbasa ng mga publikasyong pang-agham. Bagaman ngayon ang lugar na ito ay nagsimulang gumalaw nang mas mabagal at naging mas malinaw kaysa sa kanyang kabataan. Gayunpaman, mayroong hindi mabilang na mga tao na nagtatrabaho sa larangang ito at ang lahat ng kanilang mga ideya ay sulit na subukan, lahat sila ay naghihintay sa mga pakpak. At hindi mo masasabi kung gaano sila kahusay maliban kung susubukan mo sila. Gaano kahusay ang pagsasama nila sa lahat ng iba pa sa iyong allocator, dahil ang isang allocator ay gumagawa ng maraming bagay, at ang ilang mga ideya sa iyong partikular na allocator ay hindi gagana, ngunit sa ibang allocator ay madali silang gagana. Ang pangunahing paraan upang manalo para sa allocator ay hilahin ang mabagal na bagay sa labas ng pangunahing landas at pilitin itong hatiin sa mga hangganan ng mabagal na landas. Kaya kung gusto mong magpatakbo ng GC, kumuha ng mabagal na landas, i-deoptimize, itapon ang isang exception, lahat ng bagay na iyon - alam mo na ang mga bagay na ito ay medyo bihira. At bihira talaga sila, I checked. Gumagawa ka ng karagdagang trabaho at inaalis nito ang maraming paghihigpit sa mga mabagal na landas na ito, ngunit hindi ito mahalaga dahil mabagal ang mga ito at bihirang bumiyahe. Halimbawa, isang null pointer - hindi ito mangyayari, tama? Kailangan mong magkaroon ng ilang mga landas para sa iba't ibang mga bagay, ngunit hindi sila dapat makagambala sa pangunahing isa. 

Vladimir: Ano sa palagay mo ang tungkol sa mga multi-core, kapag mayroong libu-libong mga core nang sabay-sabay? Ito ba ay isang kapaki-pakinabang na bagay?

talampas: Ang tagumpay ng GPU ay nagpapakita na ito ay lubos na kapaki-pakinabang!

Vladimir: Sila ay medyo dalubhasa. Paano ang tungkol sa mga processor ng pangkalahatang layunin?

talampas: Well, iyon ang modelo ng negosyo ni Azul. Ang sagot ay bumalik sa isang panahon kung kailan talagang gusto ng mga tao ang predictable na pagganap. Mahirap magsulat ng parallel code noon. Ang H2O coding model ay lubos na nasusukat, ngunit hindi ito isang pangkalahatang layunin na modelo. Marahil ay medyo mas pangkalahatan kaysa kapag gumagamit ng GPU. Pinag-uusapan ba natin ang pagiging kumplikado ng pagbuo ng ganoong bagay o ang pagiging kumplikado ng paggamit nito? Halimbawa, itinuro sa akin ni Azul ang isang kawili-wiling aralin, isang medyo hindi halata: ang maliliit na cache ay normal. 

Ang pinakamalaking hamon sa buhay

Vladimir: Paano naman ang mga hindi teknikal na hamon?

talampas: Ang pinakamalaking hamon ay hindi maging... mabait at mabait sa mga tao. At bilang isang resulta, palagi kong nasumpungan ang aking sarili sa mga sitwasyong labis na salungatan. Ang mga kung saan alam kong mali ang nangyayari, ngunit hindi alam kung paano isulong ang mga problemang iyon at hindi ko kayang hawakan ang mga ito. Maraming pangmatagalang problema, na tumatagal ng mga dekada, ang lumitaw sa ganitong paraan. Ang katotohanan na ang Java ay may C1 at C2 compiler ay isang direktang bunga nito. Ang katotohanan na walang multi-level compilation sa Java sa loob ng sampung taon na sunud-sunod ay isa ring direktang kinahinatnan. Malinaw na kailangan natin ang ganitong sistema, ngunit hindi malinaw kung bakit hindi ito umiiral. Nagkaroon ako ng mga problema sa isang engineer... o isang grupo ng mga engineer. Noong unang panahon, noong nagsimula akong magtrabaho sa Sun, ako ay... Okay, hindi lang noon, sa pangkalahatan ay may sarili akong opinyon sa lahat ng bagay. At naisip ko na totoo na maaari mong kunin ang katotohanan mo at sabihin ito nang direkta. Lalo na't nakakagulat na tama ako sa halos lahat ng oras. At kung hindi mo gusto ang diskarteng ito... lalo na kung halatang mali ka at gumagawa ng walang kapararakan... Sa pangkalahatan, kakaunti ang mga tao ang maaaring magparaya sa ganitong paraan ng komunikasyon. Kahit na ang ilan ay kaya, tulad ko. Itinayo ko ang aking buong buhay sa mga meritokratikong prinsipyo. Kung may ipinakita kang mali, agad akong tatalikod at sasabihing: kalokohan ang sinabi mo. Kasabay nito, siyempre, humihingi ako ng paumanhin at lahat ng iyon, papansinin ko ang mga merito, kung mayroon man, at gumawa ng iba pang mga tamang aksyon. Sa kabilang banda, nakakagulat na tama ako tungkol sa isang nakakagulat na malaking porsyento ng kabuuang oras. At hindi ito gumagana nang maayos sa mga relasyon sa mga tao. I’m not trying to be nice, but I’m asking the question bluntly. "Hinding-hindi ito gagana, dahil isa, dalawa at tatlo." At parang, "Oh!" May iba pang mga kahihinatnan na marahil ay mas mahusay na huwag pansinin: halimbawa, ang mga na humantong sa isang diborsyo mula sa aking asawa at sampung taon ng depresyon pagkatapos noon.

Ang hamon ay isang pakikibaka sa mga tao, sa kanilang pang-unawa sa kung ano ang kaya mo o hindi maaaring gawin, kung ano ang mahalaga at kung ano ang hindi. Nagkaroon ng maraming hamon tungkol sa coding style. Sumulat pa rin ako ng maraming code, at noong mga araw na iyon ay kinailangan ko pang maghinay-hinay dahil napakaraming mga parallel na gawain ang ginagawa ko at hindi maganda ang paggawa nito, sa halip na tumuon sa isa. Sa pagbabalik-tanaw, isinulat ko ang kalahati ng code para sa Java JIT command, ang C2 command. Ang susunod na pinakamabilis na coder ay sumulat ng kalahati bilang mabagal, ang susunod na kalahati ay mabagal, at ito ay isang exponential na pagbaba. Ang ikapitong tao sa hanay na ito ay napaka, napakabagal - iyon ay palaging nangyayari! Nahawakan ko ang maraming code. Tiningnan ko kung sino ang nagsulat ng kung ano, nang walang pagbubukod, tinitigan ko ang kanilang code, sinuri ko ang bawat isa sa kanila, at patuloy pa rin akong sumulat nang higit pa kaysa sa alinman sa kanila. Ang diskarteng ito ay hindi masyadong gumagana sa mga tao. Ang ilang mga tao ay hindi gusto ito. At kapag hindi nila kinaya, magsisimula ang lahat ng uri ng reklamo. Halimbawa, minsan akong sinabihan na ihinto ang coding dahil nagsusulat ako ng masyadong maraming code at nalalagay sa alanganin ang team, at parang biro lang sa akin ang lahat: pare, kung mawala ang iba sa team at patuloy akong sumusulat ng code, ikaw kalahating koponan lang ang matatalo. Sa kabilang banda, kung patuloy akong nagsusulat ng code at nawala mo ang kalahati ng koponan, iyon ay parang napakasamang pamamahala. Hindi ko talaga naisip ang tungkol dito, hindi kailanman napag-usapan, ngunit nasa isang lugar pa rin sa aking isipan. Ang pag-iisip ay umiikot sa likod ng aking isipan: "Lahat ba kayo nagloloko sa akin?" Kaya, ang pinakamalaking problema ay ako at ang aking mga relasyon sa mga tao. Ngayon ay mas naiintindihan ko ang aking sarili, ako ay isang pinuno ng koponan para sa mga programmer sa loob ng mahabang panahon, at ngayon direkta kong sinasabi sa mga tao: alam mo, ako kung sino ako, at kailangan mong harapin ako - okay lang ba kung tumayo ako dito? At nang magsimula silang harapin ito, lahat ay gumana. Sa katunayan, hindi ako masama o mabuti, wala akong anumang masamang intensyon o makasariling hangarin, ito ay aking kakanyahan lamang, at kailangan kong mabuhay kasama ito kahit papaano.

Andrew: Kamakailan lamang ay nagsimulang magsalita ang lahat tungkol sa self-awareness para sa mga introvert, at soft skills sa pangkalahatan. Ano ang masasabi mo dito?

talampas: Oo, iyon ang insight at aral na natutunan ko sa aking paghihiwalay sa aking asawa. Ang natutunan ko sa hiwalayan ay ang pag-unawa sa sarili ko. Ito ay kung paano ako nagsimulang maunawaan ang ibang tao. Unawain kung paano gumagana ang pakikipag-ugnayang ito. Nagdulot ito ng sunod-sunod na pagtuklas. Nagkaroon ng kamalayan kung sino ako at kung ano ang kinakatawan ko. Ano ang ginagawa ko: alinman ay abala ako sa gawain, o iniiwasan ko ang salungatan, o iba pa - at ang antas na ito ng kamalayan sa sarili ay talagang nakakatulong upang mapanatili ang aking sarili sa kontrol. Pagkatapos nito, mas madali ang lahat. Ang isang bagay na natuklasan ko hindi lamang sa aking sarili, kundi pati na rin sa iba pang mga programmer ay ang kawalan ng kakayahang magpahayag ng mga saloobin kapag ikaw ay nasa isang estado ng emosyonal na stress. Halimbawa, nakaupo ka doon na nagco-coding, sa isang estado ng daloy, at pagkatapos ay tatakbo sila sa iyo at nagsimulang sumigaw sa hysterics na may nasira at ngayon ay matinding mga hakbang ang gagawin laban sa iyo. At hindi ka makapagsalita dahil nasa estado ka ng emosyonal na stress. Ang nakuha na kaalaman ay nagpapahintulot sa iyo na maghanda para sa sandaling ito, mabuhay ito at magpatuloy sa isang plano sa pag-urong, pagkatapos nito ay maaari kang gumawa ng isang bagay. Kaya oo, kapag sinimulan mong mapagtanto kung paano gumagana ang lahat, ito ay isang malaking kaganapan na nagbabago sa buhay. 
Ako mismo ay hindi mahanap ang tamang mga salita, ngunit naalala ko ang pagkakasunud-sunod ng mga aksyon. Ang punto ay ang reaksyong ito ay kasing dami ng pisikal na ito ay pandiwang, at kailangan mo ng espasyo. Ang nasabing espasyo, sa kahulugan ng Zen. Ito mismo ang kailangang ipaliwanag, at pagkatapos ay agad na tumabi - puro pisikal na lumayo. Kapag nananatiling tahimik ako sa salita, napoproseso ko ang sitwasyon nang emosyonal. Habang ang adrenaline ay umabot sa iyong utak, inilipat ka sa fight o flight mode, wala ka nang masasabi, hindi - ngayon ikaw ay isang tulala, isang whipping engineer, walang kakayahang tumugon o kahit na itigil ang pag-atake, at ang umaatake ay libre sa pag-atake ng paulit-ulit. Kailangan mo munang maging iyong sarili muli, mabawi ang kontrol, umalis sa mode na "fight or flight".

At para dito kailangan namin ng verbal space. Free space lang. Kung sasabihin mo ang anumang bagay, maaari mong sabihin nang eksakto iyon, at pagkatapos ay pumunta at talagang maghanap ng "espasyo" para sa iyong sarili: maglakad-lakad sa parke, i-lock ang iyong sarili sa shower - hindi mahalaga. Ang pangunahing bagay ay pansamantalang idiskonekta mula sa sitwasyong iyon. Sa sandaling i-off mo nang hindi bababa sa ilang segundo, bumalik ang kontrol, magsisimula kang mag-isip nang matino. "Okay, hindi ako tulala, hindi ako gumagawa ng mga hangal na bagay, medyo kapaki-pakinabang akong tao." Kapag nagawa mong kumbinsihin ang iyong sarili, oras na para magpatuloy sa susunod na yugto: pag-unawa sa nangyari. Inatake ka, ang pag-atake ay nagmula sa hindi mo inaasahan, ito ay isang hindi tapat, masamang ambus. Masama ito. Ang susunod na hakbang ay upang maunawaan kung bakit kailangan ito ng umaatake. Talaga bakit? Siguro dahil siya mismo ay galit na galit? Bakit siya galit? Halimbawa, dahil niloko niya ang sarili niya at hindi niya kayang tanggapin ang responsibilidad? Ito ang paraan upang maingat na pangasiwaan ang buong sitwasyon. Ngunit nangangailangan ito ng puwang para sa maneuver, verbal space. Ang pinakaunang hakbang ay putulin ang verbal contact. Iwasan ang talakayan gamit ang mga salita. Kanselahin ito, lumayo nang mabilis hangga't maaari. Kung ito ay isang pag-uusap sa telepono, i-hang up - ito ay isang kasanayang natutunan ko sa pakikipag-usap sa aking dating asawa. Kung hindi maganda ang usapan, magsabi lang ng "paalam" at ibaba ang tawag. Mula sa kabilang panig ng telepono: β€œblah blah blah”, sagot mo: β€œyeah, bye!” at ibaba ang tawag. Tapusin mo na lang ang usapan. Pagkalipas ng limang minuto, kapag bumalik sa iyo ang kakayahang mag-isip nang matino, lumamig ka ng kaunti, nagiging posible na isipin ang lahat, kung ano ang nangyari at kung ano ang susunod na mangyayari. At simulan ang pagbuo ng isang maalalahanin na tugon, sa halip na tumugon lamang dahil sa emosyon. Para sa akin, ang pambihirang tagumpay sa kamalayan sa sarili ay tiyak na sa kaso ng emosyonal na diin hindi ako makapagsalita. Ang pag-alis sa estadong ito, pag-iisip at pagpaplano kung paano tutugon at mabayaran ang mga problema - ito ang mga tamang hakbang sa kaso kapag hindi ka makapagsalita. Ang pinakamadaling paraan ay ang tumakas sa sitwasyon kung saan ang emosyonal na stress ay nagpapakita ng sarili at huminto lamang sa pakikilahok sa stress na ito. Pagkatapos nito ay nakakapag-isip ka, kapag nakapag-isip ka, nakakapagsalita ka, at iba pa.

Sa pamamagitan ng paraan, sa korte, sinusubukan ng kalabang abogado na gawin ito sa iyo - ngayon ay malinaw na kung bakit. Dahil may kakayahan siyang sugpuin ka sa ganoong estado na hindi mo man lang bigkasin ang iyong pangalan, halimbawa. Sa tunay na kahulugan, hindi ka makakapagsalita. Kung nangyari ito sa iyo, at kung alam mo na makikita mo ang iyong sarili sa isang lugar kung saan nagaganap ang mga laban sa salita, sa isang lugar na tulad ng korte, maaari kang sumama sa iyong abogado. Ang abogado ay tatayo para sa iyo at ititigil ang pandiwang pag-atake, at gagawin ito sa ganap na legal na paraan, at ang nawalang Zen space ay babalik sa iyo. Halimbawa, kailangan kong tawagan ang aking pamilya ng ilang beses, ang hukom ay medyo palakaibigan tungkol dito, ngunit ang kalaban na abogado ay sumigaw at sumigaw sa akin, hindi ako makakuha ng isang salita sa gilid. Sa mga kasong ito, pinakamainam para sa akin ang paggamit ng isang tagapamagitan. Ihihinto ng tagapamagitan ang lahat ng presyur na ito na bumubuhos sa iyo sa isang tuluy-tuloy na stream, makikita mo ang kinakailangang espasyo ng Zen, at kasama nito ang kakayahang magsalita ay bumalik. Ito ay isang buong larangan ng kaalaman kung saan maraming dapat pag-aralan, maraming matutuklasan sa loob ng iyong sarili, at lahat ng ito ay nagiging mataas na antas ng mga madiskarteng desisyon na naiiba para sa iba't ibang tao. Ang ilang mga tao ay walang mga problemang inilarawan sa itaas; kadalasan, ang mga taong propesyonal na mga tao sa pagbebenta ay wala nito. Ang lahat ng mga taong ito na nabubuhay sa mga salita - mga sikat na mang-aawit, makata, pinuno ng relihiyon at mga pulitiko, palagi silang may gustong sabihin. Wala silang ganyang problema, pero ako.

Andrew: Ito ay... hindi inaasahan. Mahusay, marami na tayong napag-usapan at oras na para tapusin ang panayam na ito. Tiyak na magkikita kami sa kumperensya at maipagpapatuloy ang dialogue na ito. See you sa Hydra!

Maaari mong ipagpatuloy ang iyong pakikipag-usap kay Cliff sa kumperensya ng Hydra 2019, na gaganapin sa Hulyo 11-12, 2019 sa St. Petersburg. Darating siya na may dalang report "Ang karanasan sa Azul Hardware Transactional Memory". Maaaring mabili ang mga tiket sa opisyal na website.

Pinagmulan: www.habr.com

Magdagdag ng komento