Вялікае інтэрв'ю з Кліфам Клікам - бацькам JIT-кампіляцыі ў Java

Вялікае інтэрв'ю з Кліфам Клікам - бацькам JIT-кампіляцыі ў JavaКліф Клік - CTO кампаніі Cratus (IoT сэнсары для паляпшэння працэсаў), заснавальнік і сузаснавальнік некалькіх стартапаў (уключаючы Rocket Realtime School, Neurensic і H2O.ai) з некалькімі паспяховымі экзітамі. Кліф напісаў свой першы кампілятар у 15 гадоў (Pascal для TRS Z-80)! Найбольш вядомы за працу над С2 у Java (the Sea of ​​Nodes IR). Гэты кампілятар паказаў свету, што JIT можа вырабляць якасны код, што стала адным з фактараў станаўлення Java як адной з асноўных сучасных праграмных платформ. Потым Кліф дапамог кампаніі Azul Systems пабудаваць 864-ядзерны мэйнфрэйм ​​з софтам на чыстай Java, які падтрымліваў паўзы GC на 500-гігабайтнай кучы ў межах 10 мілісекунд. Наогул, Кліф паспеў папрацаваць над усімі аспектамі JVM.

 
Гэты хабрапост - вялікае інтэрв'ю з Кліфам. Мы пагаворым на наступныя тэмы:

  • Пераход да нізкаўзроўневых аптымізацыяў
  • Як рабіць вялікі рэфакторынг
  • Мадэль кошту
  • Навучанне нізкаўзроўневым аптымізацыям
  • Практычныя прыклады паляпшэння прадукцыйнасці
  • Навошта ствараць сваю мову праграмавання
  • Кар'ера перформанс-інжынера
  • Тэхнічныя челленжи
  • Трохі пра алакацыю рэгістраў і шмат'ядравасць
  • Самы вялікі чэленж у жыцці

Інтэрв'ю вядуць:

  • Андрэй Сатарын з Amazon Web Services. У сваёй кар'еры паспеў папрацаваць у зусім розных праектах: тэставаў размеркаваную базу дадзеных NewSQL у Яндэксе, сістэму хмарнага дэтэктавання ў Лабараторыі Касперскага, шматкарыстальніцкую гульню ў Mail.ru і сэрвіс разліку валютных коштаў у Deutsche Bank. Цікавіцца тэставаннем буйнамаштабных backend-і размеркаваных сістэм.
  • Уладзімір Сітнікаў з Netcracker. Дзесяць гадоў працуе над прадукцыйнасцю і маштабаванасцю NetCracker OS – ПЗ, выкарыстоўванага аператарамі сувязі для аўтаматызацыі працэсаў кіравання сеткай і сеткавым абсталяваннем. Захапляецца пытаннямі прадукцыйнасці Java і Oracle Database. Аўтар больш за дзесятак паляпшэнняў прадукцыйнасці ў афіцыйным PostgreSQL JDBC-драйверы.

Пераход да нізкаўзроўневых аптымізацыяў

Андрэй: Вы - вядомы чалавек у свеце JIT-кампіляцыі, у Java і працы над перформансам у цэлым, праўда? 

Кліф: Усё так!

Андрэй: Давайце пачнем з агульных пытанняў аб рабоце над прадукцыйнасцю. Што вы думаеце аб выбары паміж высокаўзроўневымі і нізкаўзроўневымі аптымізацыямі накшталт працы на ўзроўні CPU?

Кліф: Ды тут усё проста. Самы хуткі код - той, які ніколі не запускаецца. Таму заўжды трэба пачынаць з высокага ўзроўню, працаваць над алгарытмамі. Больш добрая О-натацыя паб'е больш дрэнную О-натацыю, хіба што ўмяшаюцца нейкія дастаткова вялікія канстанты. Нізкаўзроўневыя рэчы ідуць самымі апошнімі. Звычайна, калі вы аптымізавалі ўвесь астатні стэк дастаткова добра, і ўсё яшчэ засталося нешта цікавае - вось гэта і ёсць нізкі ўзровень. Але як пачаць з высокага ўзроўню? Як даведацца, што зроблена дастаткова працы на высокім узроўні? Ну… ніяк. Няма гатовых рэцэптаў. Трэба разабрацца ў праблеме, вырашыць, што збіраешся зрабіць (каб не рабіць непатрэбных у далейшым крокаў) і тады ўжо можна расчахляць прафайлер, які можа сказаць што-небудзь карыснае. У нейкі момант вы самі разумееце, што пазбавіліся ад непатрэбных рэчаў і прыйшла сітавіна заняцца тонкай наладай нізкага ўзроўню. Гэта зусім дакладна з'яўляецца асаблівым відам мастацтва. Куча людзей робіць непатрэбныя рэчы, але рухаецца так хутка, што клапаціцца аб прадукцыйнасці ім некалі. Але гэта датуль, пакуль пытанне не ўстае рубам. Звычайна 99% часу нікому не цікава, чым я займаюся, ажно да моманту, калі на крытычным шляху не ўстане важная штука, да якой камусьці ёсць справа. І вось тут усё пачынаюць пілаваць цябе на тэму "а чаму яно з самага пачатку працавала не ідэальна". Увогуле, заўсёды ёсць што палепшыць у перформансе. Але 99% часу ў цябе няма зачэпак! Ты проста спрабуеш прымусіць нешта працаваць і падчас гэтага разумееш, што з'яўляецца важным. Ніколі нельга загадзя ведаць, што вось гэты кавалачак трэба рабіць ідэальным, таму, па сутнасці, даводзіцца быць ідэальным ва ўсім. А гэта немагчыма, і ты так не робіш. Заўсёды ёсць куча рэчаў на папраўку - і гэта зусім нармальна.

Як рабіць вялікі рэфакторынг

Андрэй: Як вы працуеце над перформансам? Гэта ж скразная праблема. Напрыклад, ці даводзілася вам працаваць над праблемамі, якія ўзнікаюць у выніку перасячэння вялікай колькасці ўжо існуючай функцыянальнасці?

Кліф: Я стараюся гэтага пазбягаць. Калі я ведаю, што прадукцыйнасць стане праблемай, то задумваюся да таго, як пачынаю кадзіць, асабліва над структурамі дадзеных. Але часцяком ты выяўляеш усё гэта вельмі пазней. І тады даводзіцца ісці на крайнія меры і рабіць тое, што я называю «перапісвай і пануй»: трэба ўхапіцца за дастаткова вялікі кавалак. Частку кода ўсё роўна давядзецца перапісваць па прычыне праблем з перформансам ці па нечым яшчэ. Якая б прычына перапісвання кода не мела месца быць, амаль заўсёды лепш перапісваць большы кавалак, чым меншы кавалак. У гэты момант усё пачынаюць трэсціся ад страху: "о божа, нельга чапаць так шмат кода!". Але, па факце, такі падыход амаль заўсёды працуе значна лепш. Трэба адразу ўзяцца за вялікую праблему, абмаляваць вакол яе вялікае кола і сказаць: усё, што ўнутры круга, я перапішу. Мяжа бо нашмат менш, чым той кантэнт усярэдзіне яе, які падлягае замене. І калі такое акрэсленне межаў дазволіць зрабіць працу ўсярэдзіне ідэальна - у цябе развязаныя рукі, рабі што хочаш. Як толькі ты зразумеў праблему, працэс перапісвання ідзе куды прасцей, таму адкусвай вялікі кавалак!
У той жа час, калі робіш перапісванне вялікім кавалкам і разумееш, што прадукцыйнасць стане праблемай, можна адразу пачаць аб ёй турбавацца. Звычайна гэта ператвараецца ў простыя рэчы накшталт "не капіюй дадзеныя, кіруй дадзенымі як мага прасцей, рабі іх паменш". У вялікіх перапісвання, ёсць стандартныя спосабы паляпшэння перформансу. І яны амаль заўсёды круцяцца вакол звестак.

Мадэль кошту

Андрэй: У адным з падкастаў вы казалі аб мадэлях кошту ў кантэксце прадукцыйнасці. Можаце растлумачыць, што пад гэтым мелася на ўвазе?

Кліф: Вядома. Я нарадзіўся ў эпоху, калі прадукцыйнасць працэсара была надзвычай важная. І гэтая эра вяртаецца зноў - лёс не пазбаўлена іроніі. Я пачынаў жыць у часы васьмібітных машын, мой першы кампутар працаваў з 256 байтамі. Менавіта байтамі. Усё было вельмі маленькае. Трэба было лічыць інструкцыі і як толькі мы пачалі прасоўвацца ўверх па стэку моў праграмавання, мовы бралі на сябе ўсё больш і больш. Быў Асэмблер, потым Basic, потым C, і C браў на сябе працу са мноствам дэталяў, накшталт размеркавання рэгістраў і падбору інструкцый. Але тамака ўсё было даволі зразумела і калі я зрабіў паказальнік на асобнік зменнай, то я атрымаю load, і ў гэтай інструкцыі кошт вядомая. Жалеза выдае вядомую колькасць машынных цыклаў, так што хуткасць выканання розных штук можна палічыць проста склаўшы ўсе інструкцыі, якія ты сабраўся запускаць. Кожны compare/test/branch/call/load/store можна было скласці і сказаць: вось табе і час выканання. Займаючыся паляпшэннем прадукцыйнасці, ты сапраўды звернеш увагу, што за лікі адпавядаюць дробным гарачым цыклам. 
Але як толькі ты пераключаешся на Java, Python і падобныя штукі, ты вельмі хутка аддаляешся ад нізкаўзроўневага жалеза. Які кошт выкліку гетэра ў Java? Калі JIT у HotSpot усё правільна заінлайніл, гэта будзе load, але, калі ен гэтага не зрабіў – гэта будзе выклік функцыі. Паколькі выклік ляжыць на гарачым цыкле, ён адменіць усе іншыя аптымізацыі ў гэтым цыкле. Таму рэальны кошт будзе нашмат большы. І ты тут жа губляеш здольнасць глядзець на кавалак кода і разумець, што нам варта яго выканаць у тэрмінах тактавай частаты працэсара, выкарыстоўванай памяці і кэша. Усё гэта становіцца цікава толькі калі сапраўды забурыўся ў перфоманс.
Цяпер мы апынуліся ў сітуацыі, калі хуткасці працэсараў ужо дзесяцігоддзе амаль не растуць. Старыя часы вяртаюцца! Вы ўжо не можаце разлічваць на добрую аднаструменную прадукцыйнасць. Але калі раптам заняцца паралельнымі вылічэннямі - гэта вар'яцка складана, усё на цябе глядзяць як на Джэймса Бонда. Дзесяціразовыя паскарэнні тут звычайна ўзнікаюць у тых месцах, дзе нехта нешта прашляпіў. Паралельнасць патрабуе шмат працы. Каб атрымаць тое самае дзесяціразовае паскарэнне, трэба зразумець мадэль кошту. Што і колькі каштуе. А для гэтага трэба зразумець, як мова кладзецца на ніжэйлеглае жалеза.
Марцін Томпсан падабраў выдатнае слова для свайго блога Mechanical Sympathy! Неабходна разумець, што збіраецца рабіць жалеза, як менавіта яно будзе гэта рабіць, і чаму яно ўвогуле робіць тое, што робіць. Карыстаючыся гэтым, даволі проста пачаць лічыць інструкцыі і высвятляць, куды выцякае час выканання. Калі ж у цябе няма адпаведнай падрыхтоўкі, ты проста шукаеш чорную котку ў цёмным пакоі. Я ўвесь час бачу людзей, якія аптымізуюць прадукцыйнасць, у якіх няма ні найменшых ідэй, якога чорта яны наогул робяць. Яны вельмі пакутуюць і не вельмі кудысьці ідуць. І калі я бяру той самы кавалак кода, падсоўваю туды парачку дробных хакаў і атрымліваю пяціразовае ці дзесяціразовае паскарэнне, яны такія: ну, так несумленна, мы і так ведалі, што ты лепшы. Дзіўна. Пра што гэта я… мадэль кошту - гэта пра тое, што за код ты пішаш і як хутка ён у сярэднім працуе ва ўсеагульнай карціне.

Андрэй: І як такі аб'ём утрымаць у галаве? Гэта дасягаецца вялікай колькасцю вопыту, ці? Дзе такі досвед здабываецца?

Кліф: Ну, свой досвед я атрымаў не самым простым шляхам. Я праграмаваў на Асэмблеры яшчэ ў тыя часы, калі можна было разабрацца ў кожнай асобнай інструкцыі. Гэта гучыць дурное, але з таго часу ў мяне ў галаве, у памяці, назаўжды застаўся набор інструкцый Z80. Я не памятаю імёны людзей ужо праз хвіліну пасля размовы, але памятаю код, напісаны 40 гадоў таму. Пацешна, гэта выглядае як сіндром.вучонага ідыёта.

Навучанне нізкаўзроўневым аптымізацыям

Андрэй: Ці ёсць нейкі больш просты спосаб увайсці ў справу?

Кліф: І так і не. Жалеза, якім мы ўсе карыстаемся, за гэты час не так ужо змянілася. Усё выкарыстоўваюць x86, за выключэннем смартфонаў на Arm. Калі ты не займаешся нейкім хардкорным эмбеддзедам, у цябе ўсё тое ж самае. Добра, далей. Інструкцыі таксама стагоддзямі не мяняліся. Трэба пайсці і напісаць што-небудзь на Асэмблеры. Трохі, але дастаткова, каб пачаць разумець. Вы вось усміхаецеся, а я зусім сур'ёзна кажу. Трэба зразумець адпаведнасць мовы і жалеза. Пасля гэтага трэба пайсці, папісаць крыху і зрабіць невялікі цацачны кампілятар для невялікай цацачнай мовы. "Цацачны" азначае, што трэба зрабіць яго за разумны час. Ён можа быць суперпростым, але павінен генераваць інструкцыі. Акт генерацыі інструкцыі дазволіць зразумець мадэль кошту для маста паміж высокаўзроўневым кодам, на якім усё пішуць, і машынным кодам, які выконваецца на жалезе. Гэтая адпаведнасць пражжацца ў мазгах у момант напісання кампілятара. Нават самага прасценькага кампілятара. Пасля гэтага можна пачаць глядзець на Java і тое, што ў яе семантычная прорва куды глыбей, і ўзводзіць па-над ёй масты куды складаней. У Java значна складаней зразумець, ці атрымаўся наш мост добрым ці дрэнным, што прымусіць яго разваліцца і што не. Але табе патрэбна нейкая адпраўная кропка, калі ты глядзіш на код і разумееш: "ага, гэты гетэр павінен інлайніцца кожны раз". А далей аказваецца, што часам так і адбываецца, за выключэннем сітуацыі, калі метад становіцца занадта вялікім, і JIT пачынае інлайнаваць усё запар. Прадукцыйнасць такіх месцаў можна прадказаць імгненна. Звычайна гетэры працуюць добра, але потым ты глядзіш на вялікія гарачыя цыклы і разумееш, што тамака плаваюць нейкія выклікі функцый, якія незразумела што робяць. У гэтым і ёсць праблема з паўсюдным выкарыстаннем гетараў, прычына па якой яны не инлайнятся - незразумела, ці гетэр гэта. Калі ў цябе супермаленькая кодавая база, яе можна проста запомніць і потым сказаць: вось гэта гетэр, а вось гэта сетэр. У вялікай кодавай базе кожная функцыя пражывае сваю ўласную гісторыю, якая нікому, увогуле, не вядомая. Прафайлер кажа, што мы страцілі 24% часу на нейкім цыкле і каб зразумець, што робіць гэты цыкл, трэба паглядзець на кожную функцыю ўнутры. Немагчыма зразумець гэта, не вывучаючы функцыю, і гэта сур'ёзна запавольвае працэс разумення. Таму я і не выкарыстоўваю гетэры і сетэры, я выйшаў на новы ўзровень!
Адкуль узяць мадэль кошту? Ну, можна пачытаць нешта, вядома… Але я думаю, лепшы спосаб - дзейнічаць. Зрабіць невялікі кампілятар і гэта будзе найлепшы спосаб усвядоміць мадэль кошту і змясціць яе ва ўласнай галаве. Невялікі кампілятар, які спатрэбіўся б для праграмавання мікрахвалёўкі - гэта задача для пачаткоўца. Ну, я маю ў выглядзе, што калі ў цябе ўжо ёсць навыкі праграмавання, то іх павінна хапіць. Усе гэтыя штукі накшталт распарсіць радок, які ў цябе будзе якім-небудзь алгебраічным выразам, выцягнуць адтуль інструкцыі матэматычных аперацый у правільным парадку, узяць правільныя значэнні з рэгістраў - усё гэта робіцца на раз. І пакуль ты гэта будзеш рабіць, яно надрукуецца ў мозгу. Думаю, усё ведаюць, чым займаецца кампілятар. І вось гэта дасць разуменне мадэлі кошту.

Практычныя прыклады паляпшэння прадукцыйнасці

Андрэй: На што яшчэ трэба звяртаць увагу пры рабоце над прадукцыйнасцю?

Кліф: Структуры даных. Дарэчы, я ўжо даўно не вёў гэтыя заняткі… Rocket School. Гэта было пацешна, але патрабавала ўкладваць столькі сіл, а ў мяне ж яшчэ і жыццё ёсць! Добра. Дык вось, на адным з вялікіх і цікавых заняткаў, «Куды сыходзіць ваш перформанс», я даваў студэнтам прыклад: два з паловай гігабайта фінтэх-дадзеных чыталіся з CSV файла і далей трэба было палічыць колькасць прадаваных прадуктаў. Звычайныя цікавыя рынкавыя дадзеныя. UDP-пакеты, пераўтвораныя ў тэкставы фармат, пачынаючы з 70-х гадоў. Chicago Mercantile Exchange - усякія штукі накшталт алею, кукурузы, соевых бабоў, і таму падобнага. Трэба было палічыць гэтыя прадукты, колькасць здзелак, сярэдні аб'ём руху сродкаў і тавараў, і г.д. Гэта даволі простая гандлёвая матэматыка: знайсці код прадукта (гэта 1-2 сімвала ў хэш-табліцы), атрымаць суму, дадаць яе ў адзін з набораў здзелак, дадаць аб'ём, дадаць кошт, і пару іншых рэчаў. Вельмі простая матэматыка. Цацачная рэалізацыя была вельмі прамалінейнай: усё ляжыць у файле, я чытаю файл і рухаюся па ім, падзяляючы асобныя запісы на Java-радкі, шукаю ў іх патрэбныя рэчы і складаю паводле вышэйапісанай матэматыкі. І гэта працуе з нейкай невялікай хуткасцю.

З такім падыходам усё відавочна, што адбываецца, і раўналежныя вылічэнні тут не дапамогуць, правільна? Аказваецца, пяціразовага павелічэння прадукцыйнасці можна дабіцца ўсяго толькі выбарам правільных структур дадзеных. І гэта здзіўляе нават дасведчаных праграмістаў! У маім канкрэтным выпадку фокус быў у тым, што не варта рабіць вылучэнняў памяці ў гарачым цыкле. Ну, гэта не ўся праўда, але ў цэлым - не варта вылучаць "раз у X", калі X дастаткова вяліка. Калі X - гэта два з паловай гігабайта, не варта вылучаць нічога "раз за літару", або "раз за радок", або "раз за поле", нічога ў такім родзе. Менавіта на гэта і сыходзіць час. Як гэта ўвогуле працуе? Уявіце, што я раблю выклік String.split() або BufferedReader.readLine(). Readline робіць радок з набору байцікаў, якія прыйшлі па сетцы, адзін раз для кожнага радка, для кожнай з сотняў мільёнаў радкоў. Я бяру гэты радок, аналізую еt і выкідваю. Чаму выкідваю - ну, я ж яе ўжо апрацаваў, усё. Так што, для кожнага байта, прачытаных з гэтых 2.7G, будзе запісана два знака ў радку, гэта значыць ужо 5.4G, і яны мне далей ні для чаго не патрэбныя, таму выкідваюцца. Калі зірнуць на прапускную здольнасць памяці, мы грузім 2.7G, якія ідуць скрозь памяць і шыну памяці ў працэсары, і далей у два разы больш адпраўляюцца ў радок, якая ляжыць памяці, і ўсё гэта пераціраецца пры стварэнні кожнага новага радка. Але мне ж трэба прачытаць яе, жалеза яе чытае, нават калі потым усё будзе перацёртае. І я павінен запісаць яе, таму што я стварыў радок і кэшы перапоўніліся - кэш не можа змясціць у сабе 2.7G. Разам, для кожнага лічанага байта я чытаю яшчэ два дадатковых байта і пішу два дадатковых байта, і ў выніку яны маюць суадносіны 4:1 – у такіх суадносінах мы бяздарна марнуем прапускную здольнасць памяці. А далей аказваецца, што калі я раблю String.split() - то раблю гэта далёка не апошні раз, там усярэдзіне можа быць яшчэ 6-7 палёў. Таму класічны код чытання CSV з наступным парсінгам радкоў прыводзіць да страт прапускной паласы памяці ў раёне 14:1 адносна таго, што вам насамрэч жадалася б мець. Калі выкінуць гэтыя вылучэнні, то можна атрымаць пяціразовае паскарэнне.

І гэта не тое, каб вельмі складана. Калі вы паглядзіце на код пад правільным кутом, усё гэта становіцца даволі проста, адразу ж, як вы ўсвядомілі сутнасць праблемы. Не варта наогул пераставаць вылучаць памяць: праблема толькі ў тым, што вы нешта вылучаеце і яно тут жа памірае, і па шляху спальвае важны рэсурс, які ў дадзеным выпадку - прапускная здольнасць памяці. І ўсё гэта выліваецца ў падзенне прадукцыйнасці. На x86 звычайна трэба актыўна паліць такты працэсара, а тут вы спалілі ўсю памяць куды раней. Рашэнне - трэба зніжаць колькасць вылучэнняў. 
Іншая частка праблемы ў тым, што, калі запусціць прафайлер, калі скончылася паласа памяці, прама ў момант, калі гэта адбываецца, ты звычайна чакаеш вяртання кэша, таму што ён поўны смеццем, які ты толькі што напладзіў, усімі гэтымі радкамі. Таму кожная аперацыя load або store становіцца павольнай, бо яны прыводзяць да промахаў у кэшы - увесь кэш стаў павольным, чакаючы, калі з яго з'едзе смецце. Таму профилировщик усяго толькі пакажа цёплы выпадковы шум, размазаны ўздоўж усяго цыклу - не будзе ніякай асобнай гарачай інструкцыі ці месцы ў кодзе. Толькі шум. І калі вы паглядзіце на цыклы GC, яны ўсё будуць па Young Generation і суперхуткімі - мікрасекунды або мілісекунды максімум. Бо ўся гэта памяць памірае імгненна. Ты вылучаеш мільярды гігабайт, і ён іх зразае, і зразае, і зноў зразае. Усё гэта адбываецца вельмi хутка. Атрымліваецца, маюцца танныя цыклы GC, цёплы шум уздоўж усяго цыклу, але нам жадаецца атрымаць 5-кратнае паскарэнне. У гэты момант і павінна ў галаве нешта замкнуцца і прагучаць: "чаму так?!". Перапаўненне паласы памяці не адлюстроўваецца ў класічным адладчыку, трэба запусціць адладчык апаратных лічыльнікаў прадукцыйнасці і ўбачыць гэта самастойна і напроста. А не наўпрост гэта можна западозрыць з гэтых трох сімптомаў. Трэці сімптом - гэта калі ты глядзіш што вылучаеш, пытаешся ў прафілявальніка, і ён адказвае: "Ты зрабіў мільярд радкоў, але GC адпрацаваў бясплатна". Як толькі гэта адбылося, ты разумееш, што напладзіў занадта шмат аб'ектаў і спаліў усю паласу памяці. Спосаб разабрацца ў гэтым ёсць, але ён не відавочны. 

Праблема ў структуры дадзеных: голая структура, якая ляжыць за ўсім, што адбываецца, яна занадта вялікая, гэта 2.7G на дыску, таму рабіць копію гэтай штукі вельмі непажадана - хочацца загрузіць яе з сеткавага байтавага буфера адразу ж у рэгістры, каб не чытаць-пісаць у радок туды-зваротна па пяць разоў. Нажаль, Java па змаўчанні не дае табе такой бібліятэкі ў складзе JDK. Але ж гэта трывіяльна, праўда? Па сутнасці, гэта 5-10 радкоў кода, якія пойдуць на рэалізацыю ўласнага буферызаванага загрузніка радкоў, які паўтарае паводзіны класа радкоў, з'яўляючыся пры гэтым абгорткай вакол ніжэйлеглага байтавага буфера. У выніку аказваецца, што ты працуеш амаль як бы з радкамі, але насамрэч там рухаюцца паказальнікі на буфер, а волкія байты нікуды не капіююцца, і такім чынам перавыкарыстоўваюцца адны і тыя ж буферы, штораз, а аперацыйная сістэма шчаслівая ўзяць на сябе рэчы, для якіх яна прызначаная, накшталт утоенай падвойнай буферызацыі гэтых байтавых буфераў, а ты сам больш не перамолваеш бясконцы струмень непатрэбных дадзеных. Дарэчы, вы ж разумееце, пры працы з GC гарантуецца, што кожнае вылучэнне памяці не будзе бачна працэсару пасля апошняга цыклу GC? Таму, усё гэта ніяк не можа быць у кэшы, і далей здараецца 100%-гарантаваная прамашка. Пры працы з паказальнікам, на x86 адымаць рэгістр з памяці займае 1-2 такту, і як толькі гэта адбываецца, ты плаціш, плаціш, плаціш, таму што памяць уся на NINE кэшах - І вось гэта з'яўляецца коштам вылучэння памяці. Сапраўдным коштам.

Іншымі словамі, структуры дадзеных - гэта тое, што змяняць складаней за ўсё. І як толькі вы ўсвядомілі, што абралі няправільную структуру дадзеных, якая ў далейшым заб'е прадукцыйнасць, звычайна патрабуецца пракруціць істотную працу, але, калі гэтага не зрабіць, далей будзе горш. Перш за ўсё, трэба думаць аб структурах даных, гэта важна. Асноўны кошт тут кладзецца на тоўстыя структуры дадзеных, якія пачынаюць выкарыстоўваць у стылі "я скапіяваў структуру дадзеных X у структуру дадзеных Y, таму што Y мне больш падабаецца па форме". Але аперацыя капіявання (якая здаецца таннай) насамрэч марнуе паласу памяці і вось тут закапана ўвесь страчаны час выканання. Калі ў мяне ёсць гіганцкі радок з JSON і я хачу ператварыць яе ў структураванае DOM-дрэва з POJO ці чагосьці такога, аперацыя парсінгу гэтага радка і пабудовы POJO, і потым новы зварот да POJO у далейшым абярнуцца лішнім коштам - штука нятанная. За выключэннем выпадку, калі вы будзеце бегаць па POJO нашмат часцей, чым па радку. Наўскідку, замест гэтага можна паспрабаваць расшыфраваць радок і выдраць адтуль толькі патрэбнае, не ператвараючы ні ў якія POJO. Калі ўсё гэта адбываецца на шляху, ад якога патрабуецца максімальная прадукцыйнасць, ніякіх табе POJO, - трэба неяк наўпрост капацца ў радку.

Навошта ствараць сваю мову праграмавання

Андрэй: Вы сказалі, што для ўсведамлення мадэлі кошту, трэба напісаць сваю невялікую маленькую мову…

Кліф: Не мова, а кампілятар. Мова і кампілятар - розныя рэчы. Самае галоўнае адрозненне - у сябе ў галаве. 

Андрэй: Дарэчы, наколькі ведаю, вы эксперыментуеце са стварэннем уласных моў. Навошта?

Кліф: Таму што магу! Я напалову выйшаў на пенсію, таму гэта маё хобі. Я ўсё жыццё рэалізоўваў нечыя чужыя мовы. А яшчэ я шмат працаваў над стылем кадавання. А яшчэ таму, што я бачу праблемы ў іншых мовах. Бачу, што ёсць лепшыя спосабы рабіць звыклыя рэчы. І я імі б скарыстаўся. Я проста запарыўся бачыць праблемы ў сабе, у Java, у Python, у любой іншай мове. Я зараз пішу на React Native, JavaScript і Elm у якасці хобі, якое не пра пенсію, а пра актыўную працу. І на Python таксама пішу і, хутчэй за ўсё, буду працягваць працаваць над машынным навучаннем для Java-бэкэндаў. Ёсць мноства папулярных моў і ва ўсіх іх ёсць цікавыя асаблівасці. Кожны добры чымсьці сваім і можна паспрабаваць звесці ўсе гэтыя фішкі разам. Так што я займаюся вывучэннем цікавых для мяне рэчаў, паводзінамі мовы, спрабую прыдумаць разумную семантыку. І пакуль што ў мяне атрымліваецца! У дадзены момант я змагаюся з семантыкай памяці, таму што хочацца мець яе як у C і Java, і атрымаць моцную мадэль памяці і семантыку памяці для лоадаў і бакоў. Пры гэтым мець аўтаматычны вывад тыпаў як у Haskell. Вось, я спрабую змяшаць Haskell-падобны вывад тыпаў з памяццю, якая працуе як у C і Java. Гэтым я займаюся апошнія 2-3 месяцы, напрыклад.

Андрэй: Калі вы будуеце мову, якая бярэ лепш аспекты з іншых моў, ці думалі вы, што нехта зробіць адваротнае: возьме вашыя ідэі і выкарыстоўвае ў сябе?

Кліф: Менавіта так і з'яўляюцца новыя мовы! Чаму Java падобная да C? Таму што ў C быў добры сінтаксіс, які ўсё разумелі і Java натхнілася гэтым сінтаксісам, дадаўшы туды тыпабяспеку, праверкі меж масіваў, GC, а яшчэ яны палепшылі нейкія рэчы з C. Дадалі свае. Але яны натхняліся даволі моцна, праўда? Усе стаяць на плячах гігантаў, якія былі да цябе - менавіта так робіцца прагрэс.

Андрэй: Як я разумею, ваша мова будзе бяспечнай адносна выкарыстання памяці. Ці думалі вы рэалізаваць нешта накшталт borrow checker з Rust? Вы на яго глядзелі, як ён вам?

Кліф: Ну, я пішу на C ужо цэлую вечнасць, з усімі гэтымі malloc і free, і ўручную кірую часам жыцця. Ведаеце, 90-95% уручную кіраванага лайфтайму мае аднолькавую структуру. І гэта вельмі, вельмі балюча займацца гэтым уручную. Хацелася б, каб кампілятар проста казаў, што там адбываецца і чаго ты дабіўся сваімі дзеяннямі. Для нейкіх рэчаў borrow checker робіць гэта са скрынкі. А яшчэ ён павінен аўтаматычна выводзіць інфармацыю, усё разумець і нават не грузіць мяне тым, каб гэтае разуменне выказаць. Ён павінен рабіць як мінімум лакальны эскейп-аналіз, і вось толькі калі ў яго не атрымалася, тады трэба дадаваць анатацыі тыпаў, якія будуць апісваць лайфтайм - і падобная схема куды складаней, чым borrow checker, ці ўвогуле любы існуючы чэкер памяці. Выбар паміж «усё ў парадку» і «я нічога не зразумеў» - не, павінна быць нешта лепшае. 
Так што, як чалавек, які напісаў шмат кода на C, лічу, што мець падтрымку аўтаматычнага кіравання лайфтаймам - гэта найважнейшая рэч. А яшчэ мяне дастала, як моцна Java выкарыстоўвае памяць і асноўная прэтэнзія - у GC. Пры вылучэнні памяці ў Java, табе не вернецца памяць, якая была лакальнай на апошнім цыкле GC. У мовах з больш дакладным кіраваннем памяццю гэта не так. Калі ты клічаш malloc, то тут жа атрымліваеш памяць, якая звычайна толькі што выкарыстоўвалася. Звычайна ты робіш з памяццю нейкія часовыя рэчы і адразу вяртаеш назад. І яна тут жа вяртаецца ў пул malloc-а, і наступны цыкл malloc-а зноў выцягвае яе вонкі. Таму рэальнае выкарыстанне памяці памяншаецца да набору жывых аб'ектаў у пэўны момант часу, плюс уцечкі. І калі ў цябе не цячэ ўсё зусім непрыстойнай выявай, вялікая частка памяці асядае ў кэшах і працэсары, і гэта працуе хутка. Але патрабуе шмат ручнога кіравання памяццю з дапамогай malloc і free, выкліканых у правільным парадку, у правільным месцы. Rust можа сам правільна з гэтым зладзіцца і ў кучы выпадкаў даць нават вялікую прадукцыйнасць, паколькі спажыванне памяці звужаецца толькі да бягучых вылічэнняў у супрацьлегласць чаканню наступнага цыклу GC, які вызваліць памяць. У выніку, мы атрымалі вельмі цікавы спосаб палепшыць прадукцыйнасць. І даволі магутны - у сэнсе, я займаўся такімі штукамі пры апрацоўцы дадзеных для фінтэха, і гэта дазваляла атрымліваць паскарэнне разоў гэтак у пяць. Гэта даволі вялікае паскарэнне, асабліва ў свеце, дзе працэсары не становяцца хутчэй, а мы ўсё таксама працягваем чакаць паляпшэнняў.

Кар'ера перформанс-інжынера

Андрэй: Яшчэ хацелася б пытацца пра кар'еру ў цэлым. Вы сталі знакамітым дзякуючы працы на JIT у HotSpot, а затым перамясціліся ў Azul - і гэта таксама JVM-кампанія. Але займаліся ўжо больш жалезам, чым софтам. А потым раптам пераключыліся на Big Data і Machine Learning, а потым на fraud detection. Як так атрымалася? Гэта вельмі розныя галіны распрацоўкі.

Кліф: Я ўжо даўнавата займаюся праграмаваннем і паспеў адзначыцца на вельмі розных занятках. І калі людзі кажуць: "о, ты ж той, хто рабіў JIT для Java!", гэта заўсёды пацешна. А бо да гэтага я займаўся клонам PostScript – той мовы, якую Apple калісьці выкарыстоўвала для сваіх лазерных друкарак. А да гэтага рабіў рэалізацыю мовы Forth. Думаю, агульная тэма для мяне - гэта распрацоўка інструментаў. Усё жыццё раблю інструменты, з дапамогай якіх іншыя людзі пішуць свае крутыя праграмы. Але я займаўся і распрацоўкай аперацыйных сістэм, драйвераў, адладчыкаў узроўня ядра, моў для распрацоўкі АС, якія пачыналіся трывіяльна, але з часам усё ўскладняліся і ўскладняліся. Але асноўная тэма, усё ж такі - распрацоўка інструментаў. Вялікі кавалак жыцця прайшоў паміж Azul і Sun, і ен быў пра Java. Але калі я заняўся Big Data і Machine Learning, я зноў надзеў свой парадны капялюш і сказаў: "Ох, а вось зараз у нас з'явілася нетрывіяльная праблема, і тут наогул адбываецца куча цікавых рэчаў і людзей, якія нешта робяць". Гэта выдатны шлях для развіцця, па якім трэба прайсці.

Так, я вельмі люблю размеркаваныя вылічэнні. Мая першая праца была ў студэнцтве на C, над рэкламным праектам. Гэта былі размеркаваныя вылічэнні на чыпах Zilog Z80, якія збіралі дадзеныя для аналагавага аптычнага распазнання тэкстаў, які вырабляецца сапраўдным аналагавым аналізатарам. Гэта была крутая і зусім ненармальная тэма. Але там былі праблемы, нейкая частка не распазнавалася правільна, таму трэба было даставаць карцінку і паказваць яе чалавеку, які ўжо чытаў вачыма і паведамляў, што ж там гаворыцца, і таму там былі джобы з дадзенымі, і ў гэтых джобаў была свая мова . Быў бэкэнд, які ўсё гэта апрацоўваў працавальныя раўналежна Z80 з запушчанымі тэрміналамі vt100 па адным на чалавека, і была мадэль раўналежнага праграмавання на Z80. Нейкі агульны кавалак памяці, які падзялялі ўсе Z80 усярэдзіне канфігурацый тыпу «зорка»; падзяляўся і бэкплэйн, і палова RAM падзялялася ўнутры сеткі, і яшчэ палова была прыватнай або сыходзіла на нешта яшчэ. Асэнсавана складаная раўналежная размеркаваная сістэма з падзялянай… полуразделяемой памяццю. Калі ж гэта было… Ужо і не ўзгадаць, недзе ў сярэдзіне 80-х. Даволі даўно. 
Так, будзем лічыць, што 30 гадоў – гэта дастаткова даўно Задачы, звязаныя з размеркаванымі вылічэннямі, існуюць дастаткова доўга, людзі здаўна ваявалі з Beowulf-кластарамі. Такія кластары выглядаюць як… Напрыклад: есць Ethernet і твой хуткі x86 падлучаны да гэтага Ethernet, і зараз табе хочацца займець fake shared memory, таму што ніхто не мог тады займацца кодынгам размеркаваных вылічэнняў, гэта было занадта складана і таму была fake shared memory з абаронай старонак памяці на x86, і калі ты пісаў у гэтую старонку, то мы казалі астатнім працэсарам, што калі яны атрымаюць доступ да той жа самай shared memory, яе трэба будзе загрузіць з цябе, і такім чынам з'явілася нешта накшталт пратаколу падтрымкі кагерэнтнасці кэшаў і софту для гэтага. Цікавая канцэпцыя. Сапраўдная праблема, вядома, была ў іншым. Усё гэта працавала, але ты хутка атрымліваў праблемы з прадукцыйнасцю, бо ніхто не разумеў мадэлі прадукцыйнасці на дастаткова добрым узроўні - якія там патэрны доступу да памяці, як зрабіць так, каб ноды бясконца не пінгавалі адзін аднаго, і гэтак далей.

У H2O я прыдумаў вось што: самі распрацоўшчыкі адказваюць за тое, каб вызначыць, дзе схаваўся паралелізм і дзе яго няма. Я прыдумаў такую ​​мадэль кадавання, што пісаць высокапрадукцыйны код стала лёгка і проста. А вось напісаць павольна які працуе код складана, ён будзе дрэнна выглядаць. Трэба сур'ёзна пастарацца, каб напісаць павольны код, давядзецца выкарыстоўваць нестандартныя метады. Які тармозіць код відаць з першага погляду. Як следства, звычайна пішацца код, які працуе хутка, але вам даводзіцца разбірацца, што рабіць у выпадку падзялянай памяці. Усё гэта завязана на вялікіх масівах і паводзіны тамака падобна на невалацільныя вялікія масівы ў раўналежнай Java. У сэнсе, прадстаўце, што два струменя пішуць у раўналежны масіў, адзін з іх выйграе, а іншы, адпаведна, прайгравае, і вы не ведаеце хто з іх хто. Калі яны не валацільныя, то парадак можа быць які заўгодна - і гэта сапраўды добра працуе. Людзі сапраўды клапоцяцца аб парадку аперацый, яны правільна расстаўляюць volatile і ў правільных месцах чакаюць праблемы з прадукцыйнасцю, звязаныя з памяццю. У адваротным выпадку яны б проста пісалі код у выглядзе цыклаў ад 1 да N, дзе N - нейкія трыльёны, у надзеі, што ўсе складаныя выпадкі аўтаматычна стануць паралельнымі - і там гэта не працуе. Але ў H2O гэта і не Java, і не Scala, можна лічыць гэта "Java мінус мінус", калі жадаецца. Гэта вельмі зразумелы стыль праграмавання і ён падобны на напісанне простага кода на C ці Java з цыкламі і масівамі. Але пры гэтым памяць можна апрацоўваць тэрабайтамі. Я да гэтага часу выкарыстоўваю H2O. Час ад часу выкарыстоўваю ў розных праектах - і гэта да гэтага часу самая хуткая штука, у дзясяткі разоў апераджальная канкурэнтаў. Калі вы робіце Big Data з калонкавымі дадзенымі, вельмі складана перасягнуць H2O.

Тэхнічныя челленжи

Андрэй: Які ў вас за ўсю кар'еру быў самы вялікі чэленж?

Кліф: Мы абмяркоўваем тэхнічную ці не тэхнічную частку пытання? Я б сказаў, самыя вялікія челенжи - не тэхнічныя. 
Што тычыцца тэхнічных челенжей. Я іх проста перамог. Я нават не ведаю, які там быў самы вялікі, але было некалькі даволі цікавых, на якія спатрэбілася даволі шмат часу, ментальнай барацьбы. Калі я пайшоў у Sun, я быў упэўнены, што зраблю хуткі кампілятар, а куча сеньёраў у адказ казалі, што нічога ў мяне ніколі не атрымаецца. Але я пайшоў гэтым шляхам, напісаў кампілятар аж да алакатара рэгістраў, і даволі хуткі. Ён быў настолькі ж хуткі, як сучасны C1, але тады алакатар быў нашмат павольней, і аглядаючыся назад - гэта была праблема вялікай структуры дадзеных. Мне яна была патрэбная каб напісаць графічны алакатар рэгістраў і я не разумеў дылемы паміж выразнасцю кода і хуткасцю, якая існавала ў тую эпоху і была вельмі важнай. Аказалася, што структура дадзеных звычайна перавышае памер кэша на x86-х таго часу і таму, калі я першапачаткова меркаваў, што алакатар рэгістраў будзе адпрацоўваць 5-10 працэнтаў усяго часу джытавання, то ў рэальнасці гэта вылілася ў 50 працэнтаў.

Час ішоў, кампілятар станавіўся ўсё больш выразным і прадукцыйным, перастаў генераваць агідны код у большай колькасці выпадкаў, і прадукцыйнасць усё часцей стала быць падобным на тое, што выдае кампілятар C. Калі ты, вядома, не пішаш нейкую дрэнь, якую нават C не паскарае. Калі ты пішаш код як на C, ты атрымліваеш і прадукцыйна як на C у большай колькасці выпадкаў. І чым далей, тым часцей атрымліваўся код, асімптатычна які супадае з узроўнем C, алакатар рэгістраў пачаў быць падобным на нешта завершанае… па-за залежнасцю ад таго, працуе твой код хутка ці павольна. Я працягваў працаваць над алакатарам, каб ён рабіў больш добрыя вылучэнні. Ён станавіўся павольней і павольней, але выдаваў усё больш і больш добрую прадукцыйнасць у тых выпадках, калі ніхто ўжо не спраўляўся. Я мог нырнуць у алакатар рэгістраў, закапаць там месяц працы, і раптам увесь код пачынаў выконвацца на 5% хутчэй. Гэта адбывалася штораз і алакатар рэгістраў стаў нечым накшталт твора мастацтва – усе любілі ці ненавідзелі яго, а людзі з акадэміі задавалі пытанні на тэму “чаму ўсё робіцца менавіта такім чынам”, чаму не лінейнае сканіраванне, і ў чым розніца. Адказ усё той жа самы: алакатар на аснове афарбоўкі графа плюс вельмі акуратная праца з буферным кодам роўна прылада перамогі, лепшая камбінацыя, якую нікому не перамагчы. І гэта даволі невідавочная штука. Усё астатняе, што там робіць кампілятар - гэта даволі вывучаныя рэчы, хоць і таксама даведзены да ўзроўню мастацтва. Я заўсёды рабіў рэчы, якія павінны былі ператварыць кампілятар у твор мастацтва. Але нічога з гэтага не было чымсьці экстраардынарным - за выключэннем алакатара рэгістраў. Фокус у тым, што трэба акуратна спілаваць пад нагрузкай і, калі гэта адбываецца (я магу растлумачыць падрабязней, калі гэта цікава), гэта азначае, што можна інлайнаваць больш агрэсіўна, без рызыкі пераваліцца за залом графіка перформансу. У тыя часы была куча поўнамаштабных кампілятараў, абчэпленых фенечкамі і свісцелкамі, у якіх былі алакатары рэгістраў, але ніхто так больш не змог.

Праблема ў тым, што, калі ты дадаеш метады, якія падлягаюць інлайнінгу, павялічваючы і павялічваючы вобласць інлайнінгу, набор выкарыстоўваных значэнняў імгненна абганяе колькасць рэгістраў, і даводзіцца спілаваць. Крытычны ўзровень звычайна надыходзіць, калі алакатар здаецца, і адзін добры кандыдат на спіллінг стаіць іншага, ты спілуеш нейкія наогул дзікія рэчы. Каштоўнасць інлайнінгу тут у тым, што ты губляеш частку зверху, зверху на выклік і захаванне, ты можаш бачыць значэння ўнутры і можаш іх далей аптымізаваць. Кошт інлайнінгу ў тым, што ўтвараецца вялікая колькасць жывых значэнняў, і калі твой алакатар рэгістраў заспілуе больш, чым трэба, ты тут жа прайграеш. Таму, у большасці алакатараў ёсць праблема: калі інлайнінг пераходзіць нейкую рысу, пачынае спілавацца ўсё на свеце і прадукцыйнасць можна змываць ва ўнітаз. Тыя, хто рэалізуе кампілятар, дадаюць нейкія эўрыстыкі: напрыклад, каб спыніць інлайнінг, пачынаючы з нейкага дастатковага вялікага памеру, паколькі алакацыі ўсё сапсуюць. Так утвараецца залом графіка перформансу - ты інлайніш, інлайніш, перформанс паціху расце - і затым бух! - Ён падае ўніз імклівым дамкратам, таму што ты заінлайніў занадта шмат. Так усё і працавала да з'яўлення Java. Java патрабуе куды больш інлайнінгу, таму мне прыйшлося рабіць свой алакатар куды больш агрэсіўным, каб ён выраўноўваўся, а не падаў, і калі ты занадта шмат заінлайніў - ён пачынае спілаваць, але потым усё роўна надыходзіць момант "няма больш спіллінгу". Гэтае цікавае назіранне і яно прыйшло да мяне проста з ніадкуль, невідавочнае, але добрае акупленае. Я ўзяўся за агрэсіўны інлайнінг і гэта прывяло мяне ў такія месцы, дзе перфоманс Java і C ідуць бок аб бок. Яны сапраўды блізкія - я магу пісаць код на Java, які будзе значна хутчэй кода на C і таму падобнае, але ў сярэднім, у вялікай карціне рэчаў, яны прыкладна параўнальныя. Думаецца, частка гэтай заслугі - алакатар рэгістраў, які дазваляе мне інлайнаваць максімальна тупа. Я проста інлайн усё, што бачу. Пытанне тут у тым, ці працуе алакатар добра, ці атрымліваецца ў выніку разумна які працуе код. Вось гэта быў вялікі чэленж: зразумець усё гэта і прымусіць зарабіць.

Трохі пра алакацыю рэгістраў і шмат'ядравасць

Уладзімір: Праблемы кшталту алакацыі рэгістраў здаюцца нейкай вечнай бясконцай тэмай. Цікава, ці было так, што нейкая ідэя здавалася шматабяцальнай, а потым правалілася на практыцы?

Кліф: Вядома! Алакацыя рэгістраў - гэта вобласць, у якой для рашэння NP-поўнай задачы ты спрабуеш падабраць нейкія эўрыстыкі. І ты ніколі не зможаш дабіцца ідэальнага рашэння, праўда? Гэта проста немагчыма. Глядзіце, Ahead of Time кампіляцыя - яна таксама працуе дрэнна. Размова тут ідзе пра нейкія сярэднія выпадкі. Аб тыповай прадукцыйнасці, так што можна пайсці і вымераць нешта, што ты лічыш добрай тыповай прадукцыйнасцю - у рэшце рэшт, ты ж працуеш над яе паляпшэннем! Алакацыя рэгістраў - гэта тэма, цалкам прысвечаная прадукцыйнасці. Як толькі ў цябе ёсць першы прататып, ён працуе і фарбуе што трэба, надыходзіць праца па перформансе. Трэба навучыцца добра вымяраць. Чаму гэта важна? Калі ёсць выразныя дадзеныя, можна глядзець на розныя ўчасткі і бачыць: ага, гэта дапамагло тут, але вось там усё зламалася! З'яўляюцца нейкія добрыя ідэі, ты дадаеш новую эўрыстыку і раптоўна ўсё пачынае працаваць у сярэднім крыху лепш. Ці не пачынае. У мяне была куча выпадкаў, калі мы змагаліся за пяць працэнтаў прадукцыйнасці, якія адрознівалі нашу распрацоўку ад папярэдняга алакатара. І кожны раз гэта выглядае так: недзе выйграў, недзе прайграў. Калі ў цябе ёсць добрыя прылады аналізу прадукцыйнасці, можна знайсці якія прайгралі ідэі і зразумець, чаму яны прайграваюць. Можа быць, варта пакінуць усё як ёсць, а можа, больш сур'ёзна ўзяцца за тонкую настройку, ці пайсці і паправіць што-небудзь іншае. Гэта цэлы набор рэчаў! Я зрабіў вось гэты круты хак, але патрэбен яшчэ і гэты, і гэты, і гэты - і вось іх сумарная камбінацыя дае некаторыя паляпшэнні. А адзіночкі могуць правальвацца. Такая прырода працы над прадукцыйнасцю NP-поўных задач.

Уладзімір: Складваецца адчуванне, што рэчы накшталт афарбоўкі ў алакатарах - гэта задача ўжо вырашаная. Ну, для вас вырашаная, мяркуючы па тым, што вы расказваеце, так ці варта тады наогул…

Кліф: Яна не вырашана як такая. Гэта ты павінен ператварыць яе ў "вырашаную". Існуюць цяжкія задачы і іх трэба рашаць. Калі гэта зроблена, надыходзіць час працы над прадукцыйнасцю. Да гэтай працы трэба ставіцца адпаведным чынам - рабіць бенчмаркі, збіраць метрыкі, тлумачыць сітуацыі, калі пры адкаце да папярэдняй версіі твой стары хак зноў пачаў працаваць (ці наадварот, перастаў). І не адыходзіць, пакуль нечага не даб'ешся. Як я ўжо казаў, калі крутыя ідэі, якія не спрацавалі, але ў галіне алакацыі рэгістраў ідэй прыкладна бясконца. Можна, напрыклад, чытаць навуковыя публікацыі. Хоць зараз гэтая вобласць стала рухацца куды больш павольна і стала больш яснай, чым у часы сваёй маладосці. Тым не менш, у гэтай галіне працуе цэлая бясконцасць людзей і ўсе іх ідэі варта паспрабаваць, усе яны чакаюць сваёй гадзіны. І ты не можаш сказаць, наколькі яны добрыя, калі не паспрабуеш. Наколькі добра яны інтэгруюцца са ўсім астатнім у тваім алакатары, бо алакатар робіць мноства рэчаў, і нейкія ідэі ў тваім пэўным алакатары не запрацуюць, а ў іншым алакатары - папросту. Асноўны спосаб перамогі для алакатара - выцягнуць павольную бздура за межы асноўнага шляху і прымусова сплітаць па межах павольных шляхоў. Таму, калі ты хочаш запусціць GC, пайсці па павольным шляху, дэаптымізавацца, выкінуць выключэнне, усё ў такім духу - ты ведаеш, што гэтыя штукі адносна рэдкія. І яны сапраўды рэдкія, я правяраў. Ты робіш дадатковую працу і дзякуючы гэтаму знікае мноства абмежаванняў на гэтых павольных шляхах, але гэта не вельмі важна, бо яны павольныя і па іх рэдка ходзяць. Напрыклад, нулявы паказальнік - ён ніколі не здараецца, праўда? Трэба мець некалькі шляхоў пад розныя рэчы, але яны не павінны мяшацца на асноўным. 

Уладзімір: Што вы думаеце пра шмат'ядравасць, калі ядраў адразу тысячы? Гэта карысная штука?

Кліф: Поспех GPU паказвае, што даволі карысная!

Уладзімір: Яны даволі спецыялізаваныя. А што наконт працэсараў агульнага прызначэння?

Кліф: Ну, гэта была бізнес-мадэль Azul. Адказ прыйшоў яшчэ ў эру, калі людзі вельмі любілі прадказальную прадукцыйнасць. Тады было цяжкавата пісаць раўналежны код. Мадэль кадавання H2O добра маштабуецца, але яна не з'яўляецца мадэллю агульнага прызначэння. Няўжо што злёгку больш агульнага, чым пры выкарыстанні GPU. Мы гаворым аб складанасці распрацоўкі падобнай штукі або аб складанасці яе выкарыстання? Напрыклад, Цікавы ўрок паднёс мне Azul, даволі невідавочны: невялікія кэшы - гэта нармальна. 

Самы вялікі чэленж у жыцці

Уладзімір: Што наконт нетэхнічных чэленжаў?

Кліф: Самы вялікі чэленж быў у тым, каб не быць… добрым і добрым з людзьмі. І як следства, я ўвесь час аказваўся ў вельмі канфліктных сітуацыях. Тых, у якіх я ведаў, што ўсё ідзе наперакасяк, але не ведаў, як прасоўвацца наперад у рашэнні гэтых праблем і не змог зладзіцца з імі. Мноства доўгайграючых праблем, якія доўжацца дзясяткамі гадоў, з'явілася менавіта такім чынам. Тое, што ў Java ёсць кампілятары C1 і C2 - прамое следства гэтага. Тое, што ў Java дзясятак гадоў запар не было шматузроўневай кампіляцыі - таксама прамое следства. Відавочна, што нам такая сістэма была патрэбная, але не відавочна - чаму яе не было. У мяне былі праблемы з адным інжынерам… ці групай інжынераў. Калісьці даўно, калі я пачаў працаваць у Sun, я быў… Добра, не толькі тады, у мяне ўвогуле заўсёды на ўсё сваё меркаванне. І я лічыў за ісціну, што можна проста ўзяць гэтую сваю праўду і расказаць у лоб. Тым больш, што я быў шакавальна правоў большую частку часу. І калі табе такі падыход не падабаецца… асабліва калі ты відавочна памыляешся і робіш глупства… Увогуле, нямногія людзі маглі талерантна вытрымліваць такую ​​форму зносін. Хаця некаторыя маглі, напрыклад, я. Я ўсё жыццё пабудаваў на мерытакратычных прынцыпах. Калі вы пакажаце мне нешта няправільнае, я тут жа развярнуся і скажу: ты сказаў глупству. Пры гэтым, вядома, прашу прабачэння, і ўсё такое, адзначу заслугі, калі такія наогул маюцца, і зраблю іншыя правільныя дзеянні. З іншага боку, я шакіруюча правоў шакавальна вялікі працэнт агульнага часу. І гэта не вельмі добрае працуе ў адносінах з людзьмі. Я і не імкнуся быць мілым, а стаўлю пытанне рубам. «Вось гэта ніколі не заробіць, таму што раз, два і тры». І яны такія: "Ох!". Былі і іншыя наступствы, якія лепш, мусіць, прапусціць: напрыклад, якія прывялі да разводу з жонкай і дзясятку гадоў дэпрэсіі пасля гэтага.

Чэленж - гэта барацьба з людзьмі, з іх успрыманнем таго, што ты можаш ці не можаш рабіць, што важна і што не. Было шмат челленжей пра стыль кадавання. Я ўсё яшчэ пішу шмат кода, а ў тыя часы мне прыйшлося нават запаволіцца, таму што я рабіў занадта шмат раўналежных задач і рабіў іх дрэнна, замест таго, каб сфакусавацца на адной. Азіраючыся назад, я напісаў палову кода каманды Java JIT, каманды C2. Наступны па хуткасці кодэр пісаў напалову павольней, наступны - яшчэ ў палову павольней і гэта было экспанентнае падзенне. Сёмы чалавек у гэтым шэрагу быў вельмі, вельмі тармазным - так заўсёды бывае! Я пакратаў кучу кода. Я глядзеў, хто што піша, без выключэнняў, я тарашчыўся ў іх код, ревьюил кожнага з іх, і ўсё яшчэ працягваў сам пісаць больш, чым любы з іх. З людзьмі такі падыход не надта добра працуе. Некаторыя такога не любяць. І калі яны не могуць з гэтым зладзіцца, пачынаюцца разнастайныя прэтэнзіі. Напрыклад, аднойчы мне сказалі перастаць пісаць код, таму што я пішу занадта шмат кода, і гэта ставіць пад пагрозу каманду, і для мяне ўсё гэта гучала як жарт: чувак калі ўся астатняя каманда знікне, і я працягну пісаць код, ты страціш толькі палову каманды. З іншага боку, калі я працягну пісаць код і ты страціш палову каманды - гэта гучыць як вельмі дрэнны менеджмент. Я ніколі асабліва не задумваўся пра гэта, ніколі не казаў пра гэта, але яно ўсё роўна было недзе ў маёй галаве. На задворках свядомасці круцілася думка: «Ды вы ўсё жартуеце ці што?». Так што самай вялікай праблемай быў я і мае адносіны з людзьмі. Цяпер я разумею сябе значна лепш, я доўга тымлідзіў у праграмістаў, і цяпер я шчыра кажу людзям: ведаеш, я такі які ёсць, і вам давядзецца са мной мець справу - нічога што я тут пастаю? І калі яны з гэтым сталі спраўляцца, усё зарабіла. Я ж, насамрэч, ні дрэнны, ні добры, у мяне няма ніякіх дрэнных намераў ці эгаістычных памкненняў, гэта проста мая сутнасць, і трэба з гэтым неяк жыць.

Андрэй: Зусім нядаўна ўсе сталі гаварыць аб самасвядомасці для інтравертаў, і ўвогуле аб софт-скілах. Што можна пра гэта сказаць?

Кліф: Так, гэта было разуменне і ўрок, які я дастаў з разводу з жонкай. Што я дастаў з разводу - гэта разуменне сябе. Дык я пачаў разумець іншых людзей. Разумець, як гэтае ўзаемадзеянне працуе. Гэта пацягнула за сабой адкрыцці адно за адным. З'явілася ўсведамленне хто я такі і што з сябе ўяўляю. Што я раблю: ці я заклапочаны задачай, ці пазбягаю канфлікту, ці яшчэ нешта - і падобны ўзровень самаўсведамлення сапраўды дапамагае трымаць сябе ў руках. Пасля гэтага ўсё ідзе куды прасцей. Адна штука, якую я выявіў не толькі ў сябе, але і ў іншых праграмістаў - немагчымасць вербалізаваць думкі, калі ты знаходзішся ў стане эмацыйнага стрэсу. Напрыклад, сядзіш ты такі кадзіш, знаходзішся ў стане патоку, і тут да цябе звяртаюцца і пачынаюць крычаць у істэрыцы, што нешта там зламалася, і зараз да цябе будуць прымяняцца крайнія меры. І ты не можаш і слова сказаць, таму што знаходзішся ў стане эмацыйнага стрэсу. Набытыя веды дазваляюць падрыхтавацца да гэтага моманту, перажыць яго і перайсці да плана адступлення, пасля якога можна ўжо нешта зрабіць. Так што так, калі ты пачынаеш усведамляць, як усё гэта працуе - гэта велізарная падзея, якое змяняе жыццё. 
Сам я не змог падабраць правільныя словы, але запомніў паслядоўнасць дзеянняў. Сутнасць у тым, што гэтая рэакцыя - настолькі ж фізічная, наколькі і вербальная, і табе патрэбна прастора. Такая прастора, у дзэнскім сэнсе. Менавіта гэта і трэба растлумачыць, а потым адразу адысці ў бок - чыста фізічна адысці. Калі я маўчу на словах, я магу апрацаваць сітуацыю ў частцы эмоцый. Па меры таго, як адрэналін дабіраецца да мозгу, перамыкае цябе ў рэжым «бі ці бяжы», ты ўжо не можаш нічога казаць, не - зараз ты ідыёт, інжынер для біцця, няздольны на годны адказ ці на тое, каб хоць бы спыніць атаку , і атакавалы можа свабодна атакаваць зноў і зноў. Трэба спачатку зноў стаць сабой, вярнуць кантроль, выйсці з рэжыму «бі ці бяжы».

І вось для гэтага неабходна вербальная прастора. Проста вольная прастора. Калі наогул нешта казаць, то можна менавіта гэта і заявіць, а потым пайсці і рэальна знайсці сабе "прастора": пайсці пагуляць па парку, замкнуцца ў душы - усё роўна. Галоўнае - часова адключыцца ад той сітуацыі. Як толькі ты хаця б на некалькі секунд адключаешся, кантроль вяртаецца, ты пачынаеш думаць цвяроза. "Добра, я ж не ідыёт нейкі, я не раблю тупыя рэчы, я даволі карысны чалавек". Як толькі ты здолеў пераканаць самога сябе, час пераходзіць да наступнага этапу: зразумець, што адбылося. Цябе атакавалі, атака прыйшла адкуль не чакалі, гэта была несумленная подлая засада. Гэта дрэнна. Наступны крок - зразумець, навошта атакаваламу гэта было трэба. Сапраўды, навошта? Можа таму, што ён сам у шаленстве? Чаму ён у шаленстве? Напрыклад, таму што ён сам аблажаўся і не можа прыняць адказнасць? Вось такім спосабам трэба акуратна апрацаваць усю сітуацыю. Але для гэтага патрэбна прастора для манеўру, вербальная прастора. Самы першы крок - разарваць вербальны кантакт. Адысці ад абмеркавання на словах. Адмяніць яго, сысці прэч як мага хутчэй. Калі гэта тэлефонная размова - проста пакладзяце трубку - гэта навык, які я атрымаў з зносін з былой жонкай. Калі размова не вядзе ні да чаго добрага, проста кажы «да спаткання» і вешай трубку. З таго боку трубкі: "бла-бла-бла", ты адказваеш: "ага, пакуль!" і кладзеш трубку. Проста абрываеш размову. Пяццю хвілінамі пазней, калі да цябе вяртаецца здольнасць разумна думаць, ты крыху астыў, становіцца магчыма ўсё абдумаць, што ж наогул адбылося і што далей будзе. І пачаць фармуляваць прадуманы адказ, а не проста рэагаваць на эмоцыях. Для мяне прарывам у самаўсьведамленьні стала менавіта тое, што ў выпадку эмацыйнага стрэсу я не магу казаць. Выйсці з гэтага стану, абдумаць і спланаваць як адказваць і кампенсаваць праблемы - вось гэта правільныя крокі ў выпадку, калі не можаш казаць. Самы просты спосаб - уцячы ад сітуацыі, у якой выяўляецца эмацыйны стрэс і проста перастаць у гэтым стрэсе ўдзельнічаць. Пасля гэтага ты здабываеш здольнасць думаць, калі ты можаш думаць, з'яўляецца магчымасць казаць, і гэтак далей.

Дарэчы, у судзе адвакат супрацьлеглага боку спрабуе рабіць гэта з табой - зараз ужо зразумела, чаму. Таму што ён мае магчымасць здушыць цябе да такога стану, што ты не зможаш нават імя сваё вымавіць, напрыклад. У самым прамым сэнсе, не зможаш казаць. Калі з табой гэта адбываецца і калі ты ведаеш, што апынешся ў месцы, дзе кіпяць славесныя бітвы, у месцы накшталт суда, то можна прыйсці са сваім юрыстам. Юрыст заступіцца за цябе і спыніць слоўную атаку, і зробіць гэта цалкам законным шляхам, і да цябе вернецца страчаная дзэнская прастора. Напрыклад, мне пару разоў трэба было патэлефанаваць сям'і, суддзя паставіўся да гэтага суцэль прыязна, але вось адвакат процілеглага боку крычаў і крычаў на мяне, я нават словы ўставіць не мог. У такіх выпадках для мяне найлепш працуе выкарыстанне пасярэдніка. Пасрэднік спыняе ўвесь гэты ціск, які льецца на цябе бесперапынным струменем, ты выяўляеш неабходную дзэнскую прастору, разам з ім вяртаецца здольнасць казаць. Гэта цэлая вобласць ведаў, у якой трэба шмат чаго вывучыць, шматлікае выявіць усярэдзіне сябе, і ўсё гэта ператвараецца ў высокаўзроўневыя стратэгічныя рашэнні, розныя для розных людзей. У некаторых няма вышэйапісаных праблем, як правіла, іх няма ў людзей, якія прафесійна займаюцца продажамі. Усе гэтыя людзі, якія зарабляюць сабе на жыццё словамі - вядомыя спевакі, паэты, рэлігійныя дзеячы і палітыкі, у іх заўсёды ёсць што сказаць. У іх няма такіх праблем, а ў мяне яны ёсьць.

Андрэй: Гэта было… нечакана. Выдатна, мы ўжо даволі шмат прагаварылі і час завяршаць гэтае інтэрв'ю. Мы абавязкова сустрэнемся на канферэнцыі і зможам прадоўжыць гэты дыялог. Сустрэнемся на Hydra!

Працягнуць зносіны з Кліфам можна будзе на канферэнцыі Hydra 2019, якая пройдзе 11-12 ліпеня 2019 года ў Санкт-Пецярбургу. Ён прыедзе з дакладам "The Azul Hardware Transactional Memory experience". Білеты можна набыць на афіцыйным сайце.

Крыніца: habr.com

Дадаць каментар