Велике інтерв'ю з Кліффом Кліком - батьком 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 робить рядок з набору байтиків, що прийшли по мережі, один раз для кожного рядка, для кожної сотні мільйонів рядків. Я беру цей рядок, аналізую його і викидаю. Чому викидаю – ну, я її вже обробив, все. Отже, для кожного байта, прочитаних із цих 2.7G, буде записано два символи в рядку, тобто вже 5.4G, і вони мені далі ні для чого не потрібні, тому викидаються. Якщо поглянути на пропускну здатність пам'яті, ми вантажимо 2.7G, які йдуть крізь пам'ять і шину пам'яті в процесорі, і далі вдвічі більше відправляються в рядок, що лежить пам'яті, і все це перетирається при створенні кожного нового рядка. Але мені треба прочитати її, залізо її читає, навіть якщо потім все буде перетерто. І я маю записати її, тому що я створив рядок і кеші переповнились - кеш не може вмістити в собі 2.7G. Отже, для кожного ліченого байти я читаю ще два додаткові байти і пишу два додаткові байти, і в результаті вони мають співвідношення 4:1 - в такому співвідношенні ми бездарно витрачаємо пропускну здатність пам'яті. А далі виявляється, що якщо я роблю String.split() – то роблю це далеко не востаннє, там усередині може бути ще 6-7 полів. Тому класичний код читання CSV з наступним парсингом рядків призводить до втрат пропускної смуги пам'яті в районі 14:1 щодо того, що вам дійсно хотілося б мати. Якщо викинути ці виділення, можна отримати п'ятикратне прискорення.

І це не те, щоб дуже складно. Якщо ви подивитеся на код під правильним кутом, все це стає досить просто, відразу ж, як ви усвідомили суть проблеми. Не варто взагалі переставати виділяти пам'ять: проблема тільки в тому, що ви щось виділяєте і воно відразу вмирає, і на шляху спалює важливий ресурс, який у цьому випадку – пропускна спроможність пам'яті. І все це виливається у падіння продуктивності. На x86 зазвичай потрібно активно палити такти процесора, а тут ви спалили всю пам'ять набагато раніше. Рішення – потрібно знижувати кількість виділень. 
Інша частина проблеми в тому, що якщо запустити профайлер, коли закінчилася смуга пам'яті, прямо в момент, коли це відбувається, ти зазвичай чекаєш повернення кешу, тому що він сповнений сміттям, яке ти щойно наплодив, усіма цими рядками. Тому кожна операція load або store стає повільною, адже вони призводять до промахів у кеші – весь кеш став повільним, очікуючи, коли з нього поїде сміття. Тому профільувальник лише покаже теплий випадковий шум, розмазаний уздовж усього циклу - не буде ніякої окремої гарячої інструкції або місця в коді. Лише шум. І якщо ви подивіться на цикли GC, всі вони будуть по Young Generation і супершвидкими – мікросекунди або мілісекунди максимум. Адже вся ця пам'ять вмирає миттєво. Ти виділяєш мільярди гігабайт, і він їх зрізає, і зрізає, і знову зрізає. Все це відбувається дуже швидко. Виходить, є дешеві цикли 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

Додати коментар або відгук