Bonega intervjuo kun Cliff Click, la patro de JIT-kompilo en Java

Bonega intervjuo kun Cliff Click, la patro de JIT-kompilo en JavaKlifo Klako — CTO de Cratus (IoT-sensiloj por proceza plibonigo), fondinto kaj kunfondinto de pluraj noventreprenoj (inkluzive de Rocket Realtime School, Neurensic kaj H2O.ai) kun pluraj sukcesaj eliroj. Cliff skribis sian unuan kompililon en la aĝo de 15 (Paskalo por la TRS Z-80)! Li estas plej konata pro sia laboro pri C2 en Java (la Maro de Nodoj IR). Ĉi tiu kompililo montris al la mondo, ke JIT povus produkti altkvalitan kodon, kio estis unu el la faktoroj en la apero de Java kiel unu el la ĉefaj modernaj softvarplatformoj. Tiam Cliff helpis al Azul Systems konstrui 864-kernan ĉefkomputilon kun pura Java programaro kiu subtenis GC-paŭzojn sur 500-gigabajta amaso ene de 10 milisekundoj. Ĝenerale, Cliff sukcesis labori pri ĉiuj aspektoj de la JVM.

 
Ĉi tiu habrapost estas bonega intervjuo kun Cliff. Ni parolos pri la jenaj temoj:

  • Transiro al malaltnivelaj optimumoj
  • Kiel fari grandan refaktorigon
  • Kosta Modelo
  • Malaltnivela optimumiga trejnado
  • Praktikaj ekzemploj de agado-plibonigo
  • Kial krei vian propran programlingvon
  • Kariero de Efika Inĝeniero
  • Teknikaj Defioj
  • Iom pri registra asigno kaj plurkernoj
  • La plej granda defio en la vivo

La intervjuo estas farita de:

  • Andrej Satarin de Amazon Web Services. En sia kariero, li sukcesis labori en tute malsamaj projektoj: li testis la distribuitan datumbazon NewSQL en Yandex, nuban detektosistemon en Kaspersky Lab, plurludantan ludon en Mail.ru kaj servon por kalkulado de valutprezoj en Deutsche Bank. Interesas pri testado de grandskalaj backend kaj distribuitaj sistemoj.
  • Vladimir Sitnikov de Netcracker. Dek jaroj da laboro pri la rendimento kaj skaleblo de NetCracker OS, programaro uzata de teleentreprenistoj por aŭtomatigi procezojn pri administrado de retaj kaj retaj ekipaĵoj. Interesas pri agado de problemoj pri Java kaj Oracle Database. Aŭtoro de pli ol dekduo agado-plibonigoj en la oficiala PostgreSQL JDBC-ŝoforo.

Transiro al malaltnivelaj optimumoj

Andrei: Vi estas granda nomo en la mondo de JIT-kompilo, Java, kaj elfara laboro ĝenerale, ĉu ne? 

Klifo: Estas tiel!

Andrei: Ni komencu per kelkaj ĝeneralaj demandoj pri prezenta laboro. Kion vi pensas pri la elekto inter altnivelaj kaj malaltnivelaj optimumigoj kiel labori ĉe la CPU-nivelo?

Klifo: Jes, ĉio estas simpla ĉi tie. La plej rapida kodo estas tiu, kiu neniam funkcias. Sekve, vi ĉiam bezonas komenci de alta nivelo, labori pri algoritmoj. Pli bona O-notacio venkos pli malbonan O-notacion, krom se iuj sufiĉe grandaj konstantoj intervenos. Malaltnivelaj aferoj iras lastaj. Tipe, se vi sufiĉe bone optimumigis la reston de via stako kaj ankoraŭ restas iuj interesaj aferoj, tio estas malalta nivelo. Sed kiel komenci de alta nivelo? Kiel vi scias, ke sufiĉe altnivela laboro estas farita? Nu... neniel. Ne estas pretaj receptoj. Vi devas kompreni la problemon, decidi kion vi faros (por ne fari nenecesajn paŝojn en la estonteco) kaj tiam vi povas malkovri la profililon, kiu povas diri ion utilan. Iam vi mem rimarkas, ke vi forigis nenecesajn aferojn kaj estas tempo fari iom da malaltnivela fajnagordado. Ĉi tio certe estas speciala speco de arto. Estas multaj homoj farantaj nenecesajn aferojn, sed moviĝantaj tiel rapide ke ili ne havas tempon por zorgi pri produktiveco. Sed ĉi tio estas ĝis la demando ekestas malakre. Kutime 99% de la tempo neniu zorgas pri tio, kion mi faras, ĝis la momento, kiam grava afero venas sur la kritika vojo, pri kiu neniu zorgas. Kaj ĉi tie ĉiuj komencas ĉagreni vin pri "kial ĝi ne funkciis perfekte ekde la komenco." Ĝenerale, ĉiam estas io por plibonigi en rendimento. Sed 99% de la tempo vi ne havas kondukojn! Vi nur provas fari ion funkcii kaj en la procezo vi eltrovas kio estas grava. Vi neniam povas scii anticipe, ke ĉi tiu peco devas esti perfekta, do, fakte, vi devas esti perfekta en ĉio. Sed ĉi tio estas neebla kaj vi ne faras ĝin. Ĉiam estas multaj aferoj por ripari – kaj tio estas tute normala.

Kiel fari grandan refaktorigon

Andrei: Kiel vi laboras pri prezentado? Ĉi tio estas transversa problemo. Ekzemple, ĉu vi iam devis labori pri problemoj, kiuj ŝprucas de la intersekco de multaj ekzistantaj funkciaĵoj?

Klifo: Mi provas eviti ĝin. Se mi scias ke rendimento estos problemo, mi pensas pri tio antaŭ ol mi komencas kodigi, precipe kun datumstrukturoj. Sed ofte vi malkovras ĉion ĉi tre poste. Kaj tiam vi devas iri al ekstremaj mezuroj kaj fari tion, kion mi nomas "reskribi kaj konkeri": vi devas kapti sufiĉe grandan pecon. Kelkaj el la kodo ankoraŭ devos esti reverkitaj pro agadoproblemoj aŭ io alia. Kia ajn estas la kialo por reverki kodon, estas preskaŭ ĉiam pli bone reverki pli grandan pecon ol pli malgrandan pecon. En ĉi tiu momento, ĉiuj ektremas pro timo: "Ho mia Dio, vi ne povas tuŝi tiom da kodo!" Sed fakte, ĉi tiu aliro preskaŭ ĉiam funkcias multe pli bone. Vi devas tuj preni grandan problemon, desegni grandan rondon ĉirkaŭ ĝi kaj diri: Mi reverkos ĉion ene de la cirklo. La limo estas multe pli malgranda ol la enhavo en ĝi, kiu devas esti anstataŭigita. Kaj se tia limo de limoj permesas al vi fari la laboron ene perfekte, viaj manoj estas liberaj, faru tion, kion vi volas. Post kiam vi komprenas la problemon, la reverka procezo estas multe pli facila, do prenu grandan mordon!
Samtempe, kiam vi faras grandan reverkon kaj rimarkas, ke agado estos problemo, vi povas tuj komenci zorgi pri ĝi. Ĉi tio kutime iĝas simplaj aferoj kiel "ne kopiu datumojn, administru datumojn kiel eble plej simple, malgrandigu ĝin." En grandaj reverkoj, ekzistas normaj manieroj plibonigi rendimenton. Kaj ili preskaŭ ĉiam rondiras ĉirkaŭ datumoj.

Kosta Modelo

Andrei: En unu el la podkastoj vi parolis pri kostaj modeloj en la kunteksto de produktiveco. Ĉu vi povas klarigi, kion vi volis diri per ĉi tio?

Klifo: Certe. Mi naskiĝis en epoko, kiam la agado de procesoro estis ege grava. Kaj ĉi tiu epoko denove revenas - la sorto ne estas sen ironio. Mi komencis vivi en la tempo de ok-bitaj maŝinoj; mia unua komputilo funkciis kun 256 bajtoj. Ĝuste bajtoj. Ĉio estis tre malgranda. Instrukcioj devis esti kalkulitaj, kaj kiam ni komencis movi supren laŭ la programlingvo-stako, la lingvoj prenis pli kaj pli. Estis Assembler, tiam Baza, tiam C, kaj C prizorgis multajn detalojn, kiel registro-atribuo kaj instrukcia elekto. Sed ĉio estis sufiĉe klara tie, kaj se mi farus montrilon al okazo de variablo, tiam mi ricevus ŝarĝon, kaj la kosto de ĉi tiu instrukcio estas konata. La aparataro produktas certan nombron da maŝincikloj, do la ekzekutrapideco de malsamaj aferoj povas esti kalkulita simple aldonante ĉiujn instrukciojn, kiujn vi kuros. Ĉiu komparo/testo/branĉo/voko/ŝarĝo/vendejo povus esti aldonita kaj diris: tio estas la ekzekuttempo por vi. Kiam vi laboras por plibonigi rendimenton, vi certe atentos, kiaj nombroj respondas al malgrandaj varmaj cikloj. 
Sed tuj kiam vi ŝanĝas al Java, Python kaj similaj aferoj, vi tre rapide malproksimiĝas de malaltnivela aparataro. Kiom kostas voki ricevilon en Java? Se JIT en HotSpot estas ĝusta enliniita, ĝi ŝarĝos, sed se ĝi ne faris tion, ĝi estos funkciovoko. Ĉar la voko estas sur varma buklo, ĝi superregos ĉiujn aliajn optimumojn en tiu buklo. Tial la reala kosto estos multe pli alta. Kaj vi tuj perdas la kapablon rigardi pecon de kodo kaj kompreni ke ni devus ekzekuti ĝin laŭ procesoro horloĝo rapido, memoro kaj kaŝmemoro uzita. Ĉio ĉi fariĝas interesa nur se vi vere eniras la agadon.
Nun ni trovas nin en situacio, kie procesoraj rapidoj apenaŭ pliiĝis dum jardeko. La malnovaj tagoj revenis! Vi ne plu povas fidi je bona unufadena agado. Sed se vi subite eniras en paralelan komputadon, ĝi estas nekredeble malfacila, ĉiuj rigardas vin kiel James Bond. Dekoblaj akceloj ĉi tie kutime okazas en lokoj kie iu fuŝis ion. Samtempeco postulas multan laboron. Por akiri tiun XNUMXx-rapidecon, vi devas kompreni la kostmodelon. Kio kaj kiom ĝi kostas? Kaj por fari tion, vi devas kompreni kiel la lango konvenas al la subesta aparataro.
Martin Thompson elektis bonegan vorton por sia blogo Mekanika Simpatio! Vi devas kompreni, kion la aparataro faros, kiel ĝuste ĝi faros ĝin, kaj kial ĝi faras tion, kion ĝi faras en la unua loko. Uzante ĉi tion, estas sufiĉe facile komenci kalkuli instrukciojn kaj eltrovi kien iras la ekzekuttempo. Se vi ne havas la taŭgan trejnadon, vi nur serĉas nigran katon en malluma ĉambro. Mi vidas homojn optimumigantajn rendimenton la tutan tempon, kiuj ne havas ideon, kion diable ili faras. Ili multe suferas kaj ne multe progresas. Kaj kiam mi prenas la saman kodon, enŝovas kelkajn malgrandajn hakojn kaj ricevas kvin- aŭ dekoblan plirapidigon, ili estas kiel: nu, tio ne estas justa, ni jam sciis, ke vi estas pli bona. Mirinda. Pri kio mi parolas... la kostmodelo temas pri kia kodo vi skribas kaj kiom rapide ĝi funkcias averaĝe en la granda bildo.

Andrei: Kaj kiel vi povas konservi tian volumon en via kapo? Ĉu tio estas atingita kun pli da sperto, aŭ? De kie venas tia sperto?

Klifo: Nu, mi ne akiris mian sperton en la plej facila maniero. Mi programis en Asembleo en la tagoj, kiam vi povis kompreni ĉiun instrukcion. Ŝajnas stulte, sed ekde tiam la instrukcio Z80 ĉiam restis en mia kapo, en mia memoro. Mi ne memoras la nomojn de homoj post minuto post parolado, sed mi memoras kodon skribitan antaŭ 40 jaroj. Ĝi estas amuza, ĝi aspektas kiel sindromo "idiota sciencisto".

Malaltnivela optimumiga trejnado

Andrei: Ĉu estas pli facila maniero eniri?

Klifo: Jes kaj ne. La aparataro, kiun ni ĉiuj uzas, ne tiom multe ŝanĝiĝis dum la tempo. Ĉiuj uzas x86, escepte de Arm-poŝtelefonoj. Se vi ne faras ian malmolan enkonstruadon, vi faras la samon. Bone, poste. La instrukcioj ankaŭ ne ŝanĝiĝis dum jarcentoj. Vi devas iri kaj skribi ion en Asembleo. Ne multe, sed sufiĉe por komenci kompreni. Vi ridetas, sed mi parolas tute serioze. Vi devas kompreni la korespondadon inter lingvo kaj aparataro. Post tio vi devas iri kaj skribi iomete kaj fari etan ludilkompililon por eta ludila lingvo. Ludilo-simila signifas, ke ĝi devas esti farita en akceptebla tempo. Ĝi povas esti super simpla, sed ĝi devas generi instrukciojn. La ago generi instrukcion helpos vin kompreni la kostan modelon por la ponto inter la altnivela kodo, kiun ĉiuj skribas, kaj la maŝinkodo, kiu funkcias sur la aparataro. Ĉi tiu korespondado estos bruligita en la cerbon en la tempo kiam la kompililo estas skribita. Eĉ la plej simpla kompililo. Post tio, vi povas komenci rigardi Java kaj la fakton, ke ĝia semantika abismo estas multe pli profunda, kaj estas multe pli malfacile konstrui pontojn super ĝi. En Java, estas multe pli malfacile kompreni ĉu nia ponto rezultis bona aŭ malbona, kio kaŭzos ĝin disfali kaj kio ne. Sed vi bezonas ian deirpunkton, kie vi rigardas la kodon kaj komprenas: "jes, ĉi tiu ricevilo devus esti enliniita ĉiufoje." Kaj tiam rezultas, ke foje tio okazas, krom la situacio, kiam la metodo fariĝas tro granda, kaj la JIT komencas enlini ĉion. La agado de tiaj lokoj povas esti antaŭvidita tuj. Kutime riceviloj funkcias bone, sed tiam vi rigardas grandajn varmajn buklojn kaj rimarkas, ke ekzistas iuj funkciovokoj flosantaj ĉi tie, kiuj ne scias, kion ili faras. Ĉi tiu estas la problemo kun la ĝeneraligita uzo de getters, la kialo kial ili ne estas enliniitaj estas ke estas ne klare ĉu ili estas getter. Se vi havas super malgrandan kodan bazon, vi povas simple memori ĝin kaj tiam diri: ĉi tio estas ricevilo, kaj ĉi tio estas setter. En granda kodbazo, ĉiu funkcio vivas sian propran historion, kiu, ĝenerale, ne estas konata de neniu. La profilisto diras, ke ni perdis 24% de la tempo en iu buklo kaj por kompreni, kion faras ĉi tiu buklo, ni devas rigardi ĉiun funkcion ene. Ne eblas kompreni ĉi tion sen studi la funkcion, kaj tio serioze malrapidigas la procezon de kompreno. Tial mi ne uzas getters kaj setters, mi atingis novan nivelon!
Kie akiri la kostan modelon? Nu, oni povas legi ion, kompreneble... Sed mi pensas, ke la plej bona maniero estas agi. Fari malgrandan kompililon estos la plej bona maniero por kompreni la kostmodelon kaj konigi ĝin en vian propran kapon. Eta kompililo, kiu taŭgus por programi mikroondon, estas tasko por komencanto. Nu, mi volas diri, se vi jam havas programajn kapablojn, tiam tio devus sufiĉi. Ĉiuj ĉi aferoj kiel analizi ĉenon, kiun vi havas kiel ia algebra esprimo, ĉerpi de tie instrukciojn por matematikaj operacioj en la ĝusta ordo, preni la ĝustajn valorojn el registroj - ĉio ĉi estas farita tuj. Kaj dum vi faros ĝin, ĝi estos presita en via cerbo. Mi pensas, ke ĉiuj scias, kion faras kompililo. Kaj ĉi tio donos komprenon pri la kostmodelo.

Praktikaj ekzemploj de agado-plibonigo

Andrei: Kion alian vi atentu kiam vi laboras pri produktiveco?

Klifo: Datumaj strukturoj. Cetere, jes, mi delonge ne instruis ĉi tiujn klasojn... Rocket School. Estis amuze, sed necesis multe da peno, kaj mi ankaŭ havas vivon! BONE. Do, en unu el la grandaj kaj interesaj klasoj, "Kien iras via agado", mi donis al studentoj ekzemplon: du kaj duono gigabajtoj da fintech-datumoj estis legitaj de CSV-dosiero kaj tiam ili devis kalkuli la nombron da produktoj venditaj. . Regulaj iksodaj merkataj datumoj. UDP-pakaĵoj konvertitaj al tekstformato ekde la 70-aj jaroj. Ĉikaga Mercantile Exchange - ĉiaj aferoj kiel butero, maizo, sojfaboj, tiaj aferoj. Necesis kalkuli ĉi tiujn produktojn, la nombron da transakcioj, la averaĝan volumon de movado de financoj kaj varoj, ktp. Ĝi estas sufiĉe simpla komerca matematiko: trovu la produktokodon (tio estas 1-2 signoj en la hashtabelo), akiru la kvanton, aldonu ĝin al unu el la komercaj aroj, aldonu volumon, aldonu valoron kaj kelkajn aliajn aferojn. Tre simpla matematiko. La ludila efektivigo estis tre simpla: ĉio estas en dosiero, mi legas la dosieron kaj trapasas ĝin, dividante unuopajn rekordojn en Java-ŝnurojn, serĉante la necesajn aferojn en ili kaj aldonante ilin laŭ la matematiko supre priskribita. Kaj ĝi funkcias je iom da malalta rapido.

Kun ĉi tiu aliro, estas evidente kio okazas, kaj paralela komputado ne helpos, ĉu ne? Rezultas, ke kvinobla pliigo de rendimento povas esti atingita simple elektante la ĝustajn datumstrukturojn. Kaj ĉi tio surprizas eĉ spertajn programistojn! En mia aparta kazo, la lertaĵo estis, ke vi ne faru memorajn asignojn en varma buklo. Nu, ĉi tio ne estas la tuta vero, sed ĝenerale - vi ne devus reliefigi "unufoje en X" kiam X estas sufiĉe granda. Kiam X estas du kaj duono gigabajtoj, vi ne asignu ion ajn "unufoje per letero", aŭ "unufoje per linio", aŭ "unufoje per kampo", ion similan. Jen kie la tempo estas pasigita. Kiel ĉi tio eĉ funkcias? Imagu, ke mi vokas String.split()BufferedReader.readLine(). Readline faras ĉenon el aro da bajtoj kiuj venis tra la reto, unufoje por ĉiu linio, por ĉiu el la centoj da milionoj da linioj. Mi prenas ĉi tiun linion, analizas ĝin kaj forĵetas ĝin. Kial mi forĵetas ĝin - nu, mi jam prilaboris ĝin, jen ĉio. Do, por ĉiu bajto legita de ĉi tiuj 2.7G, du signoj estos skribitaj en la linio, tio estas, jam 5.4G, kaj mi ne bezonas ilin por io plu, do ili estas forĵetitaj. Se vi rigardas la memoran bendolarĝon, ni ŝarĝas 2.7G, kiu trairas la memoron kaj memorbuson en la procesoro, kaj tiam duoble pli estas sendita al la linio kuŝanta en memoro, kaj ĉio ĉi estas frakasita kiam ĉiu nova linio estas kreita. Sed mi devas legi ĝin, la aparataro legas ĝin, eĉ se ĉio estas difektita poste. Kaj mi devas skribi ĝin ĉar mi kreis linion kaj la kaŝmemoroj estas plenaj - la kaŝmemoro ne povas akomodi 2.7G. Do, por ĉiu bajto, kiun mi legas, mi legas du pliajn bajtojn kaj skribas du pliajn bajtojn, kaj finfine ili havas 4:1-proporcion - en ĉi tiu proporcio ni malŝparas memoran bendolarĝon. Kaj tiam rezultas ke se mi faros String.split() – ĉi tio ne estas la lasta fojo, kiam mi faras tion, eble estas aliaj 6-7 kampoj ene. Do la klasika kodo legi CSV kaj poste analizi la ŝnurojn rezultigas memoran bendolarĝan malŝparo de ĉirkaŭ 14:1 rilate al tio, kion vi efektive ŝatus havi. Se vi forĵetas ĉi tiujn elektojn, vi povas akiri kvinoblan plirapidigon.

Kaj ĝi ne estas tiel malfacila. Se vi rigardas la kodon de la ĝusta angulo, ĉio fariĝas sufiĉe simpla post kiam vi rimarkas la problemon. Vi ne devas tute ĉesi asigni memoron: la sola problemo estas, ke vi atribuas ion kaj ĝi tuj mortas, kaj survoje ĝi bruligas gravan rimedon, kiu ĉi-kaze estas memora bendolarĝo. Kaj ĉio ĉi rezultigas malpliiĝon de produktiveco. Sur x86 vi kutime bezonas aktive bruligi procesorajn ciklojn, sed ĉi tie vi bruligis la tutan memoron multe pli frue. La solvo estas redukti la kvanton de malŝarĝo. 
La alia parto de la problemo estas, ke se vi kuras la profililon kiam la memorstrio elĉerpiĝas, ĝuste kiam ĝi okazas, vi kutime atendas ke la kaŝmemoro revenos ĉar ĝi estas plena de rubo, kiun vi ĵus produktis, ĉiuj tiuj linioj. Sekve, ĉiu ŝarĝo aŭ vendeja operacio malrapidiĝas, ĉar ili kondukas al kaŝmemoro maltrafas - la tuta kaŝmemoro malrapidiĝis, atendante ke rubo forlasos ĝin. Tial la profililo nur montros varman hazardan bruon ŝmiritan tra la tuta buklo - ne estos aparta varma instrukcio aŭ loko en la kodo. Nur bruo. Kaj se vi rigardas la GC-ciklojn, ili ĉiuj estas Juna Generacio kaj superrapidaj - mikrosekundoj aŭ milisekundoj maksimume. Post ĉio, ĉi tiu tuta memoro mortas tuj. Vi asignas miliardojn da gigabajtoj, kaj li tranĉas ilin, kaj tranĉas ilin, kaj tranĉas ilin denove. Ĉio ĉi okazas tre rapide. Rezultas, ke estas malmultekostaj GC-cikloj, varma bruo dum la tuta ciklo, sed ni volas akiri 5x-rapidigon. En ĉi tiu momento, io devus fermiĝi en via kapo kaj soni: "Kial ĉi tio estas?!" Memorstrio superfluo ne estas montrata en la klasika erarserĉilo; vi devas ruli la aparataron-efikeckalkulilon kaj vidi ĝin mem kaj rekte. Sed ĉi tio ne povas esti rekte suspektita de ĉi tiuj tri simptomoj. La tria simptomo estas kiam vi rigardas tion, kion vi reliefigas, demandas la profiliston, kaj li respondas: "Vi faris miliardon da vicoj, sed la GC funkciis senpage." Tuj kiam tio okazas, vi rimarkas, ke vi kreis tro multajn objektojn kaj bruligis la tutan memorvojon. Estas maniero eltrovi ĉi tion, sed ĝi ne estas evidenta. 

La problemo estas en la datumstrukturo: la nuda strukturo subesta ĉio kio okazas, ĝi estas tro granda, ĝi estas 2.7G sur disko, do fari kopion de ĉi tiu afero estas tre nedezirinda - vi volas ŝargi ĝin el la reto-bajta bufro tuj. en la registrojn, por ne legi-skribi al la linio kvinfoje tien kaj reen. Bedaŭrinde, Java ne donas al vi tian bibliotekon kiel parto de la JDK defaŭlte. Sed ĉi tio estas bagatela, ĉu ne? Esence, ĉi tiuj estas 5-10 linioj de kodo, kiuj estos uzataj por efektivigi vian propran bufran ŝnurŝargilon, kiu ripetas la konduton de la kordklaso, estante envolvaĵo ĉirkaŭ la suba bajta bufro. Rezulte, rezultas, ke vi laboras preskaŭ kvazaŭ per ŝnuroj, sed fakte montriloj al la bufro moviĝas tien, kaj la krudaj bajtoj ne estas kopiitaj ie ajn, kaj tiel la samaj bufroj estas reuzataj denove kaj denove, kaj la operaciumo ĝojas preni sur vin la aferojn por kiuj ĝi estas desegnita, kiel kaŝita duobla bufro de ĉi tiuj bajtaj bufroj, kaj vi ne plu muelas tra senfina fluo de nenecesaj datumoj. Cetere, ĉu vi komprenas, ke kiam vi laboras kun GC, estas garantiite, ke ĉiu memor-atribuo ne estos videbla por la procesoro post la lasta GC-ciklo? Tial ĉio ĉi ne povas esti en la kaŝmemoro, kaj tiam 100% garantiita maltrafo okazas. Kiam oni laboras per montrilo, sur x86, subtrahi registron el memoro bezonas 1-2 horloĝajn ciklojn, kaj tuj kiam tio okazas, oni pagas, pagas, pagas, ĉar la memoro estas tute ŝaltita. Naŭ kaŝmemoroj – kaj ĉi tio estas la kosto de memor-atribuo. Reala valoro.

Alivorte, datumstrukturoj estas la plej malfacile ŝanĝi. Kaj post kiam vi rimarkas, ke vi elektis la malĝustan datumstrukturon, kiu mortigos rendimenton poste, kutime estas multe da laboro farenda, sed se vi ne faros, la aferoj plimalboniĝos. Antaŭ ĉio, vi devas pensi pri datumstrukturoj, ĉi tio estas grava. La ĉefa kosto ĉi tie falas sur grasaj datumstrukturoj, kiuj komencas esti uzataj en la stilo de "Mi kopiis datumstrukturon X en datumstrukturon Y ĉar mi ŝatas la formon de Y pli bone." Sed la kopia operacio (kiu ŝajnas malmultekosta) efektive malŝparas memoran bendolarĝon kaj tie estas kie la tuta malŝparita ekzekuttempo estas entombigita. Se mi havas gigantan ĉenon de JSON kaj mi volas turni ĝin en strukturitan DOM-arbon de POJO-oj aŭ io, la operacio de analizo de tiu ĉeno kaj konstruado de la POJO, kaj poste aliri la POJO denove, rezultigos nenecesan koston - ĝi estas ne malmultekosta. Krom se vi kuras ĉirkaŭ POJO-oj multe pli ofte ol vi kuras ĉirkaŭ ŝnuro. Sendepende, vi povas anstataŭe provi malĉifri la ŝnuron kaj ĉerpi nur tion, kion vi bezonas de tie, sen igi ĝin ia POJO. Se ĉio ĉi okazas sur vojo de kiu maksimuma rendimento estas postulata, neniuj POJO-oj por vi, vi devas iel fosi en la linion rekte.

Kial krei vian propran programlingvon

Andrei: Vi diris, ke por kompreni la kostmodelon, vi devas skribi vian propran lingvaĵon...

Klifo: Ne lingvo, sed kompililo. Lingvo kaj kompililo estas du malsamaj aferoj. La plej grava diferenco estas en via kapo. 

Andrei: Cetere, laŭ mia scio, vi eksperimentas krei viajn proprajn lingvojn. Por kio?

Klifo: Ĉar mi povas! Mi estas duonemerita, do ĉi tio estas mia ŝatokupo. Mi efektivigis aliajn lingvojn dum mia tuta vivo. Mi ankaŭ multe laboris pri mia kodstilo. Kaj ankaŭ ĉar mi vidas problemojn en aliaj lingvoj. Mi vidas, ke estas pli bonaj manieroj fari konatajn aferojn. Kaj mi uzus ilin. Mi nur tedas vidi problemojn en mi mem, en Java, en Python, en iu ajn alia lingvo. Mi nun skribas en React Native, JavaScript kaj Elm kiel ŝatokupo, kiu ne temas pri emeritiĝo, sed pri aktiva laboro. Mi ankaŭ skribas en Python kaj, plej verŝajne, daŭre laboros pri maŝinlernado por Java backends. Estas multaj popularaj lingvoj kaj ĉiuj havas interesajn funkciojn. Ĉiu estas bona laŭ sia maniero kaj vi povas provi kunigi ĉiujn ĉi tiujn funkciojn. Do, mi studas aferojn, kiuj interesas min, la konduton de lingvo, provante elpensi racian semantikon. Kaj ĝis nun mi sukcesas! Nuntempe mi luktas kun memorsemantiko, ĉar mi volas havi ĝin kiel en C kaj Java, kaj akiri fortan memormodelon kaj memorsemantikon por ŝarĝoj kaj butikoj. Samtempe, havu aŭtomatan tipan inferencon kiel en Haskell. Ĉi tie, mi provas miksi Haskell-similan inferencon kun memorlaboro en ambaŭ C kaj Java. Jen kion mi faris ekzemple dum la lastaj 2-3 monatoj.

Andrei: Se vi konstruas lingvon, kiu prenas pli bonajn aspektojn de aliaj lingvoj, ĉu vi pensas, ke iu faros la malon: prenu viajn ideojn kaj uzu ilin?

Klifo: Ĝuste jen kiel novaj lingvoj aperas! Kial Java similas al C? Ĉar C havis bonan sintakson, kiun ĉiuj komprenis kaj Java estis inspirita de ĉi tiu sintakso, aldonante tipsekurecon, tabelan kontrolon, GC, kaj ili ankaŭ plibonigis kelkajn aferojn de C. Ili aldonis siajn proprajn. Sed ili estis sufiĉe multe inspiritaj, ĉu ne? Ĉiuj staras sur la ŝultroj de la gigantoj, kiuj venis antaŭ vi — tiel progresas.

Andrei: Kiel mi komprenas ĝin, via lingvo estos memorsekura. Ĉu vi pensis pri efektivigi ion kiel pruntkontrolilon de Rust? Ĉu vi rigardis lin, kion vi pensas pri li?

Klifo: Nu, mi skribas C dum aĝoj, kun ĉio ĉi malloc kaj senpage, kaj mane administranta la vivdaŭron. Vi scias, 90-95% de mane kontrolita vivdaŭro havas la saman strukturon. Kaj estas tre, tre dolorige fari ĝin permane. Mi ŝatus, ke la kompililo simple diru al vi, kio okazas tie kaj kion vi atingis per viaj agoj. Por iuj aferoj, pruntkontrolilo faras tion el la skatolo. Kaj ĝi aŭtomate montru informojn, ĉion komprenu kaj eĉ ne ŝarĝu min per prezento de ĉi tiu kompreno. Ĝi devas fari almenaŭ lokan eskapan analizon, kaj nur se ĝi malsukcesas, tiam ĝi devas aldoni tipajn komentadojn, kiuj priskribos la vivdaŭron - kaj tia skemo estas multe pli kompleksa ol pruntkontrolilo, aŭ ja ajna ekzistanta memorkontrolilo. La elekto inter "ĉio estas bona" ​​kaj "mi komprenas nenion" - ne, devas esti io pli bona. 
Do, kiel iu, kiu skribis multan kodon en C, mi pensas, ke havi subtenon por aŭtomata dumviva kontrolo estas la plej grava afero. Mi ankaŭ satas kiom multe Java uzas memoron kaj la ĉefa plendo estas la GC. Kiam vi asignas memoron en Java, vi ne reakiros la memoron, kiu estis loka ĉe la lasta GC-ciklo. Ĉi tio ne okazas en lingvoj kun pli preciza memoradministrado. Se vi nomas malloc, vi tuj ricevas la memoron, kiu estis kutime ĵus uzita. Kutime vi faras kelkajn provizorajn aferojn kun memoro kaj tuj resendas ĝin. Kaj ĝi tuj revenas al la malloc-naĝejo, kaj la sekva malloc-ciklo eltiras ĝin denove. Tial, fakta memoruzo estas reduktita al la aro de vivantaj objektoj en antaŭfiksita tempo, kaj plie likoj. Kaj se ĉio ne likas en tute maldeca maniero, la plej granda parto de la memoro finiĝas en kaŝmemoroj kaj la procesoro, kaj ĝi funkcias rapide. Sed postulas multan manan memoradministradon kun malloc kaj senpaga vokita en la ĝusta ordo, en la ĝusta loko. Rusto mem povas trakti tion ĝuste kaj en multaj kazoj doni eĉ pli bonan rendimenton, ĉar memorkonsumo estas malvastigita al nur la nunaj kalkuloj - kontraste al atendi la venontan GC-ciklon por liberigi memoron. Kiel rezulto, ni ricevis tre interesan manieron plibonigi rendimenton. Kaj sufiĉe potenca - mi volas diri, mi faris tiajn aferojn dum prilaborado de datumoj por fintech, kaj ĉi tio permesis al mi akceli ĉirkaŭ kvin fojojn. Tio estas sufiĉe granda akcelo, precipe en mondo kie procesoroj ne plirapidiĝas kaj ni ankoraŭ atendas plibonigojn.

Kariero de Efika Inĝeniero

Andrei: Mi ankaŭ ŝatus demandi ĉirkaŭe pri karieroj ĝenerale. Vi altiĝis pro via JIT-laboro ĉe HotSpot kaj poste translokiĝis al Azul, kiu ankaŭ estas JVM-firmao. Sed ni jam laboris pli pri aparataro ol pri programaro. Kaj tiam ili subite ŝanĝis al Big Data kaj Maŝina Lernado, kaj poste al fraŭda detekto. Kiel ĉi tio okazis? Ĉi tiuj estas tre malsamaj areoj de evoluo.

Klifo: Mi programas dum sufiĉe longa tempo kaj sukcesis preni multajn malsamajn klasojn. Kaj kiam homoj diras: "ho, vi estas tiu, kiu faris JIT por Java!", ĉiam estas amuza. Sed antaŭ tio, mi laboris pri klono de PostScript - la lingvo, kiun Apple iam uzis por siaj laseraj presiloj. Kaj antaŭ tio mi faris efektivigon de la Forth-lingvo. Mi pensas, ke la komuna temo por mi estas ila disvolviĝo. Dum mia tuta vivo mi kreis ilojn per kiuj aliaj homoj verkas siajn bonegajn programojn. Sed mi ankaŭ okupiĝis pri la disvolvado de operaciumoj, ŝoforoj, kernel-nivelaj erarserĉiloj, lingvoj por disvolviĝo de OS, kiuj komenciĝis bagatelaj, sed kun la tempo fariĝis pli kaj pli kompleksaj. Sed la ĉefa temo ankoraŭ estas la disvolviĝo de iloj. Granda parto de mia vivo pasis inter Azul kaj Suno, kaj temis pri Java. Sed kiam mi eniris Big Data kaj Maŝinlernadon, mi surmetis mian ŝikan ĉapelon kaj diris: "Ho, nun ni havas ne-bagalan problemon, kaj ekzistas multaj interesaj aferoj kaj homoj faras aferojn." Ĉi tio estas bonega disvolva vojo por preni.

Jes, mi tre amas distribuitan komputadon. Mia unua laboro estis kiel studento en C, en reklamprojekto. Tio estis distribuita komputado sur Zilog Z80-fritoj kiuj kolektis datenojn por analoga OCR, produktita per reala analoga analizilo. Ĝi estis mojosa kaj tute freneza temo. Sed estis problemoj, iu parto ne estis ĝuste rekonita, do vi devis eltiri bildon kaj montri ĝin al homo, kiu jam povis legi per siaj okuloj kaj raporti tion, kion ĝi diris, kaj tial estis laboroj kun datumoj, kaj ĉi tiuj laboroj. havis sian propran lingvon. Ekzistis backend kiu prilaboris ĉion ĉi - Z80s kurantaj paralele kun vt100-terminaloj kurantaj - unu por persono, kaj ekzistis paralela programa modelo sur la Z80. Iu komuna peco de memoro dividita de ĉiuj Z80s ene de stelkonfiguracio; La malantaŭa aviadilo ankaŭ estis dividita, kaj duono de la RAM estis dividita ene de la reto, kaj alia duono estis privata aŭ iris al io alia. Senchave kompleksa paralela distribuita sistemo kun komuna... duondividita memoro. Kiam ĉi tio estis... mi eĉ ne memoras, ie meze de la 80-aj jaroj. Antaŭ sufiĉe longa tempo. 
Jes, ni supozu, ke 30 jaroj estas antaŭ sufiĉe longa tempo.Problemoj rilataj al distribuita komputado ekzistas de sufiĉe longa tempo; homoj delonge militas kontraŭ Beowulf-aretoj. Tiaj aretoj aspektas kiel... Ekzemple: ekzistas Eterreto kaj via rapida x86 estas konektita al ĉi tiu Eterreto, kaj nun vi volas akiri falsan komunan memoron, ĉar neniu povis fari distribuitan komputikan kodigon tiam, estis tro malfacila kaj tial tie estis falsa komuna memoro kun protektaj memorpaĝoj sur x86, kaj se vi skribis al ĉi tiu paĝo, tiam ni diris al aliaj procesoroj, ke se ili aliras la saman komunan memoron, ĝi devus esti ŝargita de vi, kaj tiel io kiel protokolo por subteni kaŝmemorkohereco aperis kaj programaro por tio. Interesa koncepto. La vera problemo, kompreneble, estis io alia. Ĉio ĉi funkciis, sed vi rapide ricevis rendimentajn problemojn, ĉar neniu komprenis la agadomodelojn je sufiĉe bona nivelo - kiaj memoraj alirpadronoj estis tie, kiel certigi, ke la nodoj ne senfine pingis unu la alian, ktp.

Kion mi elpensis en H2O estas, ke estas la programistoj mem kiuj respondecas pri determini kie paraleleco estas kaŝita kaj kie ĝi ne estas. Mi elpensis kodan modelon, kiu igis skribi alt-efikecan kodon facila kaj simpla. Sed skribi malrapidan kodon estas malfacila, ĝi aspektos malbone. Vi devas serioze provi skribi malrapidan kodon, vi devos uzi ne-normajn metodojn. La bremsa kodo estas videbla unuavide. Kiel rezulto, vi kutime skribas kodon kiu funkcias rapide, sed vi devas eltrovi kion fari en la kazo de komuna memoro. Ĉio ĉi estas ligita al grandaj tabeloj kaj la konduto tie estas simila al nevolatilaj grandaj tabeloj en paralela Java. Mi volas diri, imagu, ke du fadenoj skribas al paralela tabelo, unu el ili gajnas, kaj la alia, sekve, perdas, kaj vi ne scias, kiu estas kiu. Se ili ne estas volatilaj, tiam la ordo povas esti kio ajn vi volas - kaj ĉi tio funkcias vere bone. Homoj vere zorgas pri ordo de operacioj, ili metas volatilon en la ĝustaj lokoj, kaj ili atendas memor-rilatajn rendimentajn problemojn en la ĝustaj lokoj. Alie, ili simple skribus kodon en formo de bukloj de 1 ĝis N, kie N estas kelkaj duilionoj, kun la espero, ke ĉiuj kompleksaj kazoj aŭtomate fariĝos paralelaj - kaj ĝi ne funkcias tie. Sed en H2O ĉi tio estas nek Java nek Scala; vi povas konsideri ĝin "Java minus minus" se vi volas. Ĉi tio estas tre klara programa stilo kaj similas al skribado de simpla C aŭ Java kodo kun bukloj kaj tabeloj. Sed samtempe, memoro povas esti prilaborita en terabajtoj. Mi ankoraŭ uzas H2O. Mi uzas ĝin de tempo al tempo en malsamaj projektoj - kaj ĝi estas ankoraŭ la plej rapida afero, dekoj da fojoj pli rapida ol ĝiaj konkurantoj. Se vi faras Big Data kun kolonaj datumoj, estas tre malfacile venki H2O.

Teknikaj Defioj

Andrei: Kio estis via plej granda defio en via tuta kariero?

Klifo: Ĉu ni diskutas la teknikan aŭ ne-teknikan parton de la afero? Mi dirus, ke la plej grandaj defioj ne estas teknikaj. 
Koncerne teknikajn defiojn. Mi simple venkis ilin. Mi eĉ ne scias, kio estis la plej granda, sed estis kelkaj sufiĉe interesaj, kiuj prenis sufiĉe da tempo, mensa lukto. Kiam mi iris al Sun, mi estis certa, ke mi faros rapidan kompililon, kaj amaso da maljunuloj respondis, ke mi neniam sukcesos. Sed mi sekvis ĉi tiun vojon, skribis kompililon al la registrilo, kaj ĝi estis sufiĉe rapida. Ĝi estis same rapida kiel moderna C1, sed la alsignilo estis multe pli malrapida tiam, kaj postvide ĝi estis granda datuma strukturo problemo. Mi bezonis ĝin por skribi grafikan registro-asignilon kaj mi ne komprenis la dilemon inter koda esprimkapablo kaj rapideco, kiu ekzistis en tiu epoko kaj estis tre grava. Montriĝis, ke la datumstrukturo kutime superas la kaŝmemorgrandecon sur x86s de tiu tempo, kaj tial, se mi komence supozis, ke la registro-asignilo funkcius 5-10 procentojn de la totala tremtempo, tiam fakte ĝi rezultis esti. 50 procentoj.

Kun la paso de la tempo, la kompililo fariĝis pli pura kaj pli efika, ĉesis generi teruran kodon en pli da kazoj, kaj la rendimento ĉiam pli komencis simili al tio, kion produktas kompililo C. Krom se, kompreneble, vi skribas iun aĉaĵon, kiun eĉ C ne plirapidigas. . Se vi skribas kodon kiel C, vi ricevos rendimenton kiel C en pli da kazoj. Kaj ju pli vi iris, des pli ofte vi ricevis kodon, kiu asimptote koincidis kun nivelo C, la registro-asignilo komencis aspekti kiel io kompleta... sendepende de ĉu via kodo funkcias rapide aŭ malrapide. Mi daŭre laboris pri la alsignilo por ke ĝi faru pli bonajn elektojn. Li iĝis pli kaj pli malrapida, sed li donis pli kaj pli bonan efikecon en kazoj kie neniu alia povis elteni. Mi povus plonĝi en registrilon, enterigi tie monaton da laboro, kaj subite la tuta kodo komenciĝus ekzekuti 5% pli rapide. Ĉi tio okazis fojon post fojo kaj la registrilo fariĝis ia artaĵo - ĉiuj amis ĝin aŭ malamis ĝin, kaj homoj de la akademio demandis pri la temo "kial ĉio estas farita tiel", kial ne. linio-skanado, kaj kio estas la diferenco. La respondo estas ankoraŭ la sama: asignilo bazita sur grafika kolorigo plus tre zorgema laboro kun la bufrokodo estas egala al armilo de venko, la plej bona kombinaĵo, kiun neniu povas venki. Kaj ĉi tio estas iom nekomprenebla afero. Ĉio alia, kion la kompililo faras tie, estas sufiĉe bone studitaj aferoj, kvankam ili ankaŭ estis alportitaj al la nivelo de arto. Mi ĉiam faris aferojn, kiuj devis igi la kompililon en artaĵon. Sed nenio el tio estis io eksterordinara - krom la registro-asignilo. La ruzo estas esti singarda dehakita sub ŝarĝo kaj, se ĉi tio okazas (mi povas klarigi pli detale se interesiĝas), tio signifas, ke vi povas enlinii pli agreseme, sen risko fali super kinco en la agado-horaro. En tiuj tagoj, ekzistis amaso da plenskalaj kompililoj, pendigitaj per bubeloj kaj fajfiloj, kiuj havis registro-asignilojn, sed neniu alia povis fari tion.

La problemo estas, ke se vi aldonas metodojn, kiuj estas submetitaj al enliniado, pliigante kaj pliigante la enlinian areon, la aro de uzataj valoroj tuj superas la nombron da registroj, kaj vi devas tranĉi ilin. La kritika nivelo kutime venas kiam la asignanto rezignas, kaj unu bona kandidato por verŝo valoras alian, vi vendos iujn ĝenerale sovaĝajn aferojn. La valoro de enlinio ĉi tie estas, ke vi perdas parton de la supra kosto, superkoste por voki kaj ŝpari, vi povas vidi la valorojn ene kaj povas plue optimumigi ilin. La kosto de enlinio estas, ke granda nombro da vivaj valoroj formiĝas, kaj se via registro-asignilo brulas pli ol necese, vi tuj perdas. Sekve, la plej multaj alsignantoj havas problemon: kiam enlinio transiras certan linion, ĉio en la mondo komencas esti tranĉita kaj produktiveco povas esti forfluita en la necesejon. Tiuj, kiuj efektivigas la kompililon, aldonas kelkajn heŭristikojn: ekzemple ĉesi enliniadon, komencante per iu sufiĉe granda grandeco, ĉar asignoj ruinigos ĉion. Tiel formiĝas kinco en la agado-grafiko - vi enlinia, enlinia, la agado malrapide kreskas - kaj tiam eksplodo! – ĝi falas malsupren kiel rapida fanto ĉar vi vicis tro multe. Tiel ĉio funkciis antaŭ la apero de Java. Java postulas multe pli da enlinio, do mi devis igi mian aldonilon multe pli agresema, por ke ĝi ebeniĝis prefere ol kraŝas, kaj se vi tro enlinias, ĝi komencas disverŝi, sed tiam la momento "ne plu disverŝado" ankoraŭ venas. Ĉi tio estas interesa observo kaj ĝi ĵus venis al mi de nenie, ne evidenta, sed ĝi bone pagis. Mi prenis agreseman enliniadon kaj ĝi kondukis min al lokoj kie Java kaj C-efikeco funkcias unu apud la alia. Ili estas vere proksimaj - mi povas skribi Java-kodon kiu estas signife pli rapida ol C-kodo kaj tiaj aferoj, sed averaĝe, en la granda bildo de aferoj, ili estas proksimume kompareblaj. Mi pensas, ke parto de ĉi tiu merito estas la registro-asignilo, kiu permesas al mi enliniigi kiel eble plej stulte. Mi nur enlinias ĉion, kion mi vidas. La demando ĉi tie estas ĉu la asignilo funkcias bone, ĉu la rezulto estas inteligente funkcianta kodo. Ĉi tio estis granda defio: kompreni ĉion ĉi kaj igi ĝin funkcii.

Iom pri registra asigno kaj plurkernoj

Vidi: Problemoj kiel registra asignado ŝajnas ia eterna, senfina temo. Mi scivolas, ĉu iam estis ideo, kiu ŝajnis promesplena kaj poste malsukcesis en la praktiko?

Klifo: Certe! Registro-atribuo estas areo en kiu vi provas trovi kelkajn heŭristikojn por solvi NP-kompletan problemon. Kaj vi neniam povas atingi perfektan solvon, ĉu ne? Ĉi tio simple neeblas. Rigardu, Ahead of Time kompilo - ĝi ankaŭ malbone funkcias. La konversacio ĉi tie temas pri kelkaj averaĝaj kazoj. Pri tipa agado, do vi povas iri mezuri ion, kion vi opinias bona tipa agado - ja vi laboras por plibonigi ĝin! Registra atribuo estas temo tute pri agado. Post kiam vi havas la unuan prototipon, ĝi funkcias kaj pentras tion, kion necesas, la agado komenciĝas. Vi devas lerni bone mezuri. Kial ĝi estas grava? Se vi havas klarajn datumojn, vi povas rigardi malsamajn areojn kaj vidi: jes, ĝi helpis ĉi tie, sed tie ĉio rompiĝis! Kelkaj bonaj ideoj aperas, vi aldonas novajn heŭristikojn kaj subite ĉio ekfunkcias iomete pli bone averaĝe. Aŭ ĝi ne komenciĝas. Mi havis amason da kazoj, kie ni batalis por la kvin-procenta agado, kiu diferencigis nian evoluon de la antaŭa alsignilo. Kaj ĉiufoje ĝi aspektas jene: ie oni gajnas, ie oni perdas. Se vi havas bonajn analizajn ilojn, vi povas trovi la perdantajn ideojn kaj kompreni kial ili malsukcesas. Eble indas lasi ĉion kiel ĝi estas, aŭ eble preni pli seriozan aliron al fajnagordado, aŭ eliri kaj ripari ion alian. Estas tuta aro da aferoj! Mi faris ĉi tiun bonegan hakon, sed mi ankaŭ bezonas ĉi tiun, kaj ĉi tiun, kaj ĉi tiun - kaj ilia tuta kombinaĵo donas kelkajn plibonigojn. Kaj solemuloj povas malsukcesi. Ĉi tio estas la naturo de agado de laboro pri NP-kompletaj problemoj.

Vidi: Oni havas la senton, ke aferoj kiel pentrado en alsigniloj estas problemo jam solvita. Nu, estas decidite por vi, se juĝante laŭ tio, kion vi diras, ĉu do eĉ valoras ĝin...

Klifo: Ĝi ne estas solvita tiel. Estas vi, kiu devas igi ĝin "solvita". Estas malfacilaj problemoj kaj ili devas esti solvitaj. Post kiam ĉi tio estas farita, estas tempo labori pri produktiveco. Vi devas aliri ĉi tiun laboron laŭe - faru komparnormojn, kolektu metrikojn, klarigu situaciojn kiam, kiam vi revenis al antaŭa versio, via malnova hako denove ekfunkciis (aŭ inverse, ĉesis). Kaj ne rezignu ĝis vi atingos ion. Kiel mi jam diris, se ekzistas bonegaj ideoj, kiuj ne funkciis, sed en la kampo de atribuo de registroj de ideoj ĝi estas proksimume senfina. Vi povas, ekzemple, legi sciencajn eldonaĵojn. Kvankam nun ĉi tiu areo komencis moviĝi multe pli malrapide kaj fariĝis pli klara ol en sia junaĝo. Tamen, estas sennombraj homoj laborantaj en ĉi tiu kampo kaj ĉiuj iliaj ideoj estas provadaj, ili ĉiuj atendas en la flugiloj. Kaj vi ne povas diri kiom bonaj ili estas krom se vi provas ilin. Kiom bone ili integriĝas kun ĉio alia en via alsignilo, ĉar alsignilo faras multajn aferojn, kaj kelkaj ideoj en via specifa alsignilo ne funkcios, sed en alia alsignilo ili facile faros. La ĉefa maniero gajni por la alsignanto estas tiri la malrapidajn aĵojn ekster la ĉefa vojo kaj devigi ĝin disiĝi laŭ la limoj de la malrapidaj vojoj. Do se vi volas ruli GC, prenu la malrapidan vojon, maloptimumigu, ĵetu escepton, ĉion ĉi - vi scias, ke ĉi tiuj aferoj estas relative maloftaj. Kaj ili estas vere maloftaj, mi kontrolis. Vi faras kroman laboron kaj ĝi forigas multajn limigojn sur ĉi tiuj malrapidaj vojoj, sed ne vere gravas, ĉar ili estas malrapidaj kaj malofte vojaĝitaj. Ekzemple, nula montrilo - ĝi neniam okazas, ĉu ne? Vi devas havi plurajn vojojn por malsamaj aferoj, sed ili ne devus malhelpi la ĉefan. 

Vidi: Kion vi pensas pri multkernoj, kiam estas miloj da kernoj samtempe? Ĉu ĉi tio estas utila afero?

Klifo: La sukceso de la GPU montras, ke ĝi estas sufiĉe utila!

Vidi: Ili estas sufiĉe specialigitaj. Kio pri ĝeneraluzeblaj procesoroj?

Klifo: Nu, tio estis la komerca modelo de Azul. La respondo revenis en epoko, kiam homoj vere amis antaŭvideblan agadon. Estis malfacile skribi paralelan kodon tiam. La kodiga modelo de H2O estas tre skalebla, sed ĝi ne estas ĝeneraluzebla modelo. Eble iom pli ĝenerala ol kiam vi uzas GPU. Ĉu ni parolas pri la komplekseco de disvolvado de tia afero aŭ la komplekseco de uzado de ĝi? Ekzemple, Azul instruis al mi interesan lecionon, sufiĉe neevidentan: malgrandaj kaŝmemoroj estas normalaj. 

La plej granda defio en la vivo

Vidi: Kio pri ne-teknikaj defioj?

Klifo: La plej granda defio estis ne esti... afabla kaj afabla al homoj. Kaj kiel rezulto, mi konstante trovis min en ekstreme konfliktaj situacioj. Tiuj, kie mi sciis, ke aferoj misfunkcias, sed ne sciis kiel antaŭeniri kun tiuj problemoj kaj ne povis trakti ilin. Multaj longdaŭraj problemoj, daŭrantaj dum jardekoj, ekestis tiamaniere. La fakto, ke Java havas C1 kaj C2-kompililojn, estas rekta sekvo de tio. La fakto, ke ne estis plurnivela kompilo en Java dum dek jaroj sinsekvaj ankaŭ estas rekta sekvo. Estas evidente, ke ni bezonis tian sistemon, sed ne estas evidente kial ĝi ne ekzistis. Mi havis problemojn kun unu inĝeniero... aŭ grupo de inĝenieroj. Iam, kiam mi eklaboris ĉe Sun, mi estis... Bone, ne nur tiam, mi ĝenerale ĉiam havas propran opinion pri ĉio. Kaj mi pensis, ke estas vere, ke vi povus simple preni ĉi tiun vian veron kaj diri ĝin rekte. Precipe ĉar mi ŝoke pravis plejofte. Kaj se vi ne ŝatas ĉi tiun aliron... precipe se vi evidente eraras kaj faras sensencaĵon... Ĝenerale malmultaj homoj povus toleri ĉi tiun komunikformon. Kvankam iuj povus, kiel mi. Mi konstruis mian tutan vivon sur meritokratiaj principoj. Se vi montros al mi ion malbonan, mi tuj turnos min kaj diros: vi diris sensencaĵon. Samtempe, kompreneble, mi pardonpetas kaj ĉio tio, mi notos la meritojn, se ekzistas, kaj faros aliajn ĝustajn agojn. Aliflanke, mi ŝoke pravas pri ŝoke granda procento de la tuta tempo. Kaj ĝi ne tre bone funkcias en rilatoj kun homoj. Mi ne provas esti afabla, sed mi faras la demandon malakre. "Ĉi tio neniam funkcios, ĉar unu, du kaj tri." Kaj ili estis kiel, "Ho!" Estis aliaj sekvoj, kiujn verŝajne pli bone ignoru: ekzemple tiuj, kiuj kaŭzis eksedziĝon de mia edzino kaj post tio dek jarojn da deprimo.

Defio estas lukto kun homoj, kun ilia percepto pri tio, kion vi povas aŭ ne povas fari, kio estas grava kaj kio ne. Estis multaj defioj pri kodiga stilo. Mi ankoraŭ skribas multe da kodo, kaj en tiuj tagoj mi eĉ devis malrapidiĝi ĉar mi faris tro multajn paralelajn taskojn kaj faris ilin malbone, anstataŭ koncentriĝi al unu. Rerigardante, mi skribis duonon de la kodo por la komando Java JIT, la komando C2. La sekva plej rapida kodisto skribis duonon tiel malrapida, la sekva duono tiel malrapida, kaj ĝi estis eksponenta malkresko. La sepa persono en ĉi tiu vico estis tre, tre malrapida - tio ĉiam okazas! Mi tuŝis multe da kodo. Mi rigardis, kiu skribis tion, senescepte, mi fikse rigardis ilian kodon, reviziis ĉiun el ili, kaj daŭre skribis pli mi mem ol iu el ili. Ĉi tiu aliro ne tre bone funkcias kun homoj. Iuj homoj ne ŝatas ĉi tion. Kaj kiam ili ne povas trakti ĝin, komenciĝas ĉiaj plendoj. Ekzemple, oni iam diris al mi, ke mi ĉesu kodigon, ĉar mi skribis tro da kodo kaj ĝi endanĝerigis la teamon, kaj ĉio sonis kiel ŝerco al mi: ulo, se la resto de la teamo malaperas kaj mi daŭre skribas kodon, vi nur perdos duonteamojn. Aliflanke, se mi daŭre skribas kodon kaj vi perdas duonon de la teamo, tio sonas kiel tre malbona administrado. Mi neniam vere pensis pri ĝi, neniam parolis pri ĝi, sed ĝi estis ankoraŭ ie en mia kapo. La penso turniĝis en la malantaŭo de mia menso: "Ĉu vi ĉiuj ŝercas min?" Do, la plej granda problemo estis mi kaj miaj rilatoj kun homoj. Nun mi multe pli bone komprenas min, mi estis teamgvidanto por programistoj dum longa tempo, kaj nun mi rekte diras al homoj: vi scias, mi estas kiu mi estas, kaj vi devos trakti min - ĉu estas bone se mi staras. ĉi tie? Kaj kiam ili komencis trakti ĝin, ĉio funkciis. Fakte, mi estas nek malbona nek bona, mi ne havas malbonajn intencojn aŭ egoismajn aspirojn, ĝi estas nur mia esenco, kaj mi bezonas vivi kun ĝi iel.

Andrei: Ĵus ĉiuj komencis paroli pri memkonscio por introvertitoj, kaj mildaj kapabloj ĝenerale. Kion vi povas diri pri ĉi tio?

Klifo: Jes, tio estis la kompreno kaj leciono, kiun mi lernis de mia eksedziĝo de mia edzino. Kion mi lernis el la eksedziĝo estis kompreni min mem. Jen kiel mi komencis kompreni aliajn homojn. Komprenu kiel ĉi tiu interago funkcias. Tio kondukis al malkovroj unu post alia. Estis konscio pri kiu mi estas kaj kion mi reprezentas. Kion mi faras: aŭ mi okupas min pri la tasko, aŭ mi evitas konflikton, aŭ ion alian – kaj ĉi tiu nivelo de memkonscio vere helpas teni min en kontrolo. Post ĉi tio ĉio iras multe pli facila. Unu afero, kiun mi malkovris ne nur en mi mem, sed ankaŭ en aliaj programistoj, estas la nekapablo vortigi pensojn kiam vi estas sub emocia streso. Ekzemple, vi sidas tie kodante, en stato de fluo, kaj tiam ili kuras al vi kaj ekkrias histerie, ke io estas rompita kaj nun ekstremaj mezuroj estos prenitaj kontraŭ vi. Kaj vi ne povas diri eĉ vorton ĉar vi estas en stato de emocia streĉo. La akirita scio permesas vin prepari por ĉi tiu momento, travivi ĝin kaj pluiri al retiriĝa plano, post kiu vi povas fari ion. Do jes, kiam vi ekkomprenas kiel ĉio funkcias, ĝi estas grandega vivŝanĝa evento. 
Mi mem ne povis trovi la ĝustajn vortojn, sed mi rememoris la sinsekvon de agoj. La afero estas, ke ĉi tiu reago estas tiom fizika kiel parola, kaj vi bezonas spacon. Tia spaco, en la Zena senco. Ĝuste ĉi tio necesas klarigi, kaj poste tuj flankenpaŝi — pure fizike foriru. Kiam mi silentas parole, mi povas procesi la situacion emocie. Dum la adrenalino atingas vian cerbon, ŝanĝas vin al batala aŭ flugreĝimo, vi ne plu povas diri ion ajn, ne - nun vi estas idioto, vipa inĝeniero, nekapabla de deca respondo aŭ eĉ ĉesigi la atakon, kaj la atakanto estas libera. ataki denove kaj denove. Vi devas unue fariĝi vi mem denove, reakiri kontrolon, eliri el la "batalo aŭ flugo" reĝimo.

Kaj por tio ni bezonas vortan spacon. Nur libera spaco. Se vi diras ion ajn, tiam vi povas diri ĝuste tion, kaj tiam iri kaj vere trovi "spacon" por vi mem: promeni en la parko, ŝlosi vin en la duŝo - ne gravas. La ĉefa afero estas provizore malkonekti de tiu situacio. Tuj kiam vi malŝaltas almenaŭ kelkajn sekundojn, kontrolo revenas, vi komencas sobre pensi. "Bone, mi ne estas ia idioto, mi ne faras stultajn aferojn, mi estas sufiĉe utila persono." Post kiam vi povis konvinki vin, estas tempo transiri al la sekva etapo: kompreni kio okazis. Vi estis atakita, la atako venis de kie vi ne atendis ĝin, ĝi estis malhonesta, malnobla embusko. Ĉi tio estas malbona. La sekva paŝo estas kompreni kial la atakanto bezonis ĉi tion. Vere, kial? Eble ĉar li mem estas furioza? Kial li frenezas? Ekzemple, ĉar li fiŝis sin mem kaj ne povas akcepti respondecon? Jen la maniero zorge trakti la tutan situacion. Sed ĉi tio postulas spacon por manovro, parolan spacon. La unua paŝo estas ĉesigi parolan kontakton. Evitu diskuton per vortoj. Nuligi ĝin, foriru kiel eble plej rapide. Se temas pri telefona konversacio, simple haltigu - ĉi tio estas kapablo, kiun mi lernis komunikinte kun mia eksedzino. Se la konversacio ne iras bone, nur diru "adiaŭ" kaj haltigu. De la alia flanko de la telefono: "bla bla bla", vi respondas: "jes, adiaŭ!" kaj pendigu. Vi nur finas la konversacion. Kvin minutojn poste, kiam la kapablo pensi prudente revenas al vi, vi iomete malvarmiĝis, fariĝas eble pensi pri ĉio, kio okazis kaj kio okazos poste. Kaj komencu formuli pripenseman respondon, prefere ol nur reagi pro emocio. Por mi, la trarompo en memkonscio estis ĝuste la fakto, ke en kazo de emocia streĉo mi ne povas paroli. Eliri el ĉi tiu stato, pensi kaj plani kiel respondi kaj kompensi problemojn - ĉi tiuj estas la ĝustaj paŝoj en la kazo, kiam vi ne povas paroli. La plej facila maniero estas forkuri de la situacio en kiu emocia streso manifestiĝas kaj simple ĉesi partopreni ĉi tiun streson. Post tio oni iĝas kapabla pensi, kiam oni povas pensi, oni iĝas kapabla paroli, ktp.

Cetere, en tribunalo, la kontraŭa advokato provas fari tion al vi - nun estas klare kial. Ĉar li havas la kapablon subpremi vin ĝis tia stato, ke vi eĉ ne povas prononci vian nomon, ekzemple. En tre reala senco, vi ne povos paroli. Se ĉi tio okazas al vi, kaj se vi scias, ke vi trovos vin en loko kie furiozas parolaj bataloj, en loko kiel tribunalo, tiam vi povas veni kun via advokato. La advokato staros por vi kaj ĉesigos la vortan atakon, kaj faros ĝin en tute laŭleĝa maniero, kaj la perdita Zen-spaco revenos al vi. Ekzemple, mi devis telefoni al mia familio kelkajn fojojn, la juĝisto estis sufiĉe amika pri tio, sed la kontraŭa advokato kriegis kaj kriegis al mi, mi eĉ ne povis ricevi vorton en rando. En ĉi tiuj kazoj, uzi peranto funkcias plej bone por mi. La mediaciisto ĉesigas ĉi tiun tutan premon, kiu verŝas sur vin en kontinua fluo, vi trovas la necesan Zen-spacon, kaj kun ĝi revenas la kapablo paroli. Ĉi tio estas tuta kampo de scio, en kiu estas multe por studi, multe por malkovri en vi mem, kaj ĉio ĉi fariĝas altnivelaj strategiaj decidoj, kiuj estas malsamaj por malsamaj homoj. Iuj homoj ne havas la problemojn priskribitajn supre; kutime homoj, kiuj estas profesiaj vendantoj, ne havas ilin. Ĉiuj ĉi homoj, kiuj vivtenas per vortoj - famaj kantistoj, poetoj, religiestroj kaj politikistoj, ili ĉiam havas ion por diri. Ili ne havas tiajn problemojn, sed mi havas.

Andrei: Estis... neatendita. Bonege, ni jam multe parolis kaj estas tempo fini ĉi tiun intervjuon. Ni certe renkontiĝos en la konferenco kaj povos daŭrigi ĉi tiun dialogon. Ĝis revido ĉe Hydra!

Vi povas daŭrigi vian konversacion kun Cliff ĉe la Hydra 2019-konferenco, kiu okazos la 11-12-an de julio 2019 en Sankt-Peterburgo. Li venos kun raporto "La Azul Hardware Transactional Memory-sperto". Biletoj estas aĉeteblaj en la oficiala retejo.

fonto: www.habr.com

Aldoni komenton