Wawancara hebat dengan Cliff Click, bapak kompilasi JIT di Java

Wawancara hebat dengan Cliff Click, bapak kompilasi JIT di JavaKlik Tebing — CTO Cratus (sensor IoT untuk peningkatan proses), pendiri dan salah satu pendiri beberapa startup (termasuk Rocket Realtime School, Neurensic, dan H2O.ai) dengan beberapa keberhasilan. Cliff menulis kompiler pertamanya pada usia 15 tahun (Pascal untuk TRS Z-80)! Ia terkenal karena karyanya pada C2 di Java (Lautan Node IR). Kompiler ini menunjukkan kepada dunia bahwa JIT dapat menghasilkan kode berkualitas tinggi, yang merupakan salah satu faktor munculnya Java sebagai salah satu platform perangkat lunak modern yang utama. Kemudian Cliff membantu Azul Systems membangun mainframe 864-core dengan perangkat lunak Java murni yang mendukung jeda GC pada heap 500 gigabyte dalam waktu 10 milidetik. Secara umum Cliff berhasil mengerjakan seluruh aspek JVM.

 
Habrapost ini adalah wawancara yang bagus dengan Cliff. Kami akan berbicara tentang topik berikut:

  • Transisi ke pengoptimalan tingkat rendah
  • Bagaimana melakukan refactoring besar-besaran
  • Model Biaya
  • Pelatihan optimasi tingkat rendah
  • Contoh praktis peningkatan kinerja
  • Mengapa membuat bahasa pemrograman Anda sendiri
  • Karir Insinyur Kinerja
  • Tantangan Teknis
  • Sedikit tentang alokasi register dan multi-core
  • Tantangan terbesar dalam hidup

Wawancara dilakukan oleh:

  • Andrey Satarin dari Layanan Web Amazon. Dalam karirnya, ia berhasil bekerja di proyek yang sangat berbeda: ia menguji database terdistribusi NewSQL di Yandex, sistem deteksi cloud di Kaspersky Lab, permainan multipemain di Mail.ru, dan layanan untuk menghitung harga valuta asing di Deutsche Bank. Tertarik untuk menguji sistem backend dan terdistribusi skala besar.
  • Vladimir Sitnikov dari Netcracker. Sepuluh tahun bekerja pada kinerja dan skalabilitas NetCracker OS, perangkat lunak yang digunakan oleh operator telekomunikasi untuk mengotomatisasi proses manajemen jaringan dan peralatan jaringan. Tertarik dengan masalah kinerja Java dan Oracle Database. Penulis lebih dari selusin peningkatan kinerja pada driver resmi PostgreSQL JDBC.

Transisi ke pengoptimalan tingkat rendah

Andrew: Anda adalah nama besar di dunia kompilasi JIT, Java, dan kinerja secara umum, bukan? 

Jurang: Seperti itu!

Andrew: Mari kita mulai dengan beberapa pertanyaan umum tentang kinerja kerja. Apa pendapat Anda tentang pilihan antara optimasi tingkat tinggi dan tingkat rendah seperti bekerja pada tingkat CPU?

Jurang: Ya, semuanya sederhana di sini. Kode tercepat adalah kode yang tidak pernah berjalan. Oleh karena itu, Anda harus selalu memulai dari tingkat yang tinggi, mengerjakan algoritma. Notasi O yang lebih baik akan mengalahkan notasi O yang lebih buruk, kecuali ada konstanta yang cukup besar yang ikut campur. Hal-hal tingkat rendah menjadi yang terakhir. Biasanya, jika Anda telah mengoptimalkan sisa tumpukan Anda dengan cukup baik dan masih ada beberapa hal menarik yang tersisa, itu adalah level yang rendah. Tapi bagaimana memulainya dari level tinggi? Bagaimana Anda tahu bahwa cukup banyak pekerjaan tingkat tinggi yang telah dilakukan? Yah... tidak mungkin. Tidak ada resep yang sudah jadi. Anda perlu memahami masalahnya, memutuskan apa yang akan Anda lakukan (agar tidak mengambil langkah yang tidak perlu di masa depan) dan kemudian Anda dapat mengungkap profiler, yang dapat mengatakan sesuatu yang berguna. Pada titik tertentu, Anda sendiri menyadari bahwa Anda telah menyingkirkan hal-hal yang tidak perlu dan inilah saatnya untuk melakukan penyesuaian tingkat rendah. Ini jelas merupakan jenis seni yang istimewa. Ada banyak orang yang melakukan hal-hal yang tidak perlu, namun bergerak begitu cepat sehingga mereka tidak punya waktu untuk mengkhawatirkan produktivitas. Tapi ini sampai pertanyaan itu muncul secara blak-blakan. Biasanya 99% dari waktu tidak ada yang peduli dengan apa yang saya lakukan, sampai suatu saat ketika suatu hal penting muncul di jalur kritis yang tidak dipedulikan oleh siapa pun. Dan di sini semua orang mulai mengomeli Anda tentang “mengapa ini tidak berfungsi dengan sempurna sejak awal.” Secara umum, selalu ada sesuatu yang perlu ditingkatkan dalam kinerja. Namun 99% dari waktu Anda tidak memiliki petunjuk! Anda hanya mencoba membuat sesuatu berhasil dan dalam prosesnya Anda menemukan apa yang penting. Anda tidak akan pernah tahu sebelumnya bahwa karya ini harus sempurna, jadi sebenarnya Anda harus sempurna dalam segala hal. Tapi ini tidak mungkin dan Anda tidak melakukannya. Selalu ada banyak hal yang harus diperbaiki - dan itu sepenuhnya normal.

Bagaimana melakukan refactoring besar-besaran

Andrew: Bagaimana cara Anda mengerjakan sebuah pertunjukan? Ini adalah masalah lintas sektoral. Misalnya, pernahkah Anda mengerjakan masalah yang timbul dari persilangan banyak fungsi yang ada?

Jurang: Saya mencoba menghindarinya. Jika saya tahu kinerja akan menjadi masalah, saya memikirkannya sebelum memulai pengkodean, terutama dengan struktur data. Namun sering kali Anda mengetahui semua ini di kemudian hari. Dan kemudian Anda harus mengambil tindakan ekstrem dan melakukan apa yang saya sebut "menulis ulang dan menaklukkan": Anda perlu mengambil bagian yang cukup besar. Beberapa kode masih harus ditulis ulang karena masalah kinerja atau hal lainnya. Apapun alasan untuk menulis ulang kode, hampir selalu lebih baik menulis ulang bagian yang lebih besar daripada bagian yang lebih kecil. Pada saat ini, semua orang mulai gemetar ketakutan: "Ya Tuhan, Anda tidak dapat menyentuh begitu banyak kode!" Namun kenyataannya, pendekatan ini hampir selalu bekerja lebih baik. Anda harus segera menangani masalah besar, menggambar lingkaran besar di sekelilingnya dan berkata: Saya akan menulis ulang semua yang ada di dalam lingkaran. Batasnya jauh lebih kecil dibandingkan konten di dalamnya yang perlu diganti. Dan jika penggambaran batasan seperti itu memungkinkan Anda melakukan pekerjaan di dalam dengan sempurna, tangan Anda bebas, lakukan apa pun yang Anda inginkan. Setelah Anda memahami masalahnya, proses penulisan ulang menjadi jauh lebih mudah, jadi cobalah!
Pada saat yang sama, ketika Anda melakukan penulisan ulang besar-besaran dan menyadari bahwa kinerja akan menjadi masalah, Anda dapat segera mulai mengkhawatirkannya. Biasanya hal ini berubah menjadi hal sederhana seperti “jangan copy data, kelola data sesederhana mungkin, kecilkan”. Dalam penulisan ulang yang besar, ada cara standar untuk meningkatkan kinerja. Dan mereka hampir selalu berkisar pada data.

Model Biaya

Andrew: Di salah satu podcast Anda berbicara tentang model biaya dalam konteks produktivitas. Bisakah Anda menjelaskan apa yang Anda maksud dengan ini?

Jurang: Tentu. Saya lahir di era ketika kinerja prosesor sangatlah penting. Dan era ini kembali lagi - takdir bukannya tanpa ironi. Saya mulai hidup di zaman mesin delapan bit; komputer pertama saya bekerja dengan 256 byte. Tepatnya byte. Semuanya sangat kecil. Instruksi harus diperhitungkan, dan saat kami mulai meningkatkan tumpukan bahasa pemrograman, bahasa tersebut menjadi semakin banyak. Ada Assembler, lalu Basic, lalu C, dan C menangani banyak detail, seperti alokasi register dan pemilihan instruksi. Tapi semuanya cukup jelas di sana, dan jika saya membuat pointer ke instance variabel, maka saya akan mendapatkan beban, dan biaya instruksi ini diketahui. Perangkat keras menghasilkan sejumlah siklus mesin tertentu, sehingga kecepatan eksekusi berbagai hal dapat dihitung hanya dengan menjumlahkan semua instruksi yang akan Anda jalankan. Setiap perbandingan/pengujian/cabang/panggilan/muat/penyimpanan dapat ditambahkan dan dikatakan: itulah waktu eksekusi untuk Anda. Saat berupaya meningkatkan kinerja, Anda pasti akan memperhatikan angka-angka yang sesuai dengan siklus panas kecil. 
Namun begitu Anda beralih ke Java, Python, dan sejenisnya, Anda dengan cepat beralih dari perangkat keras tingkat rendah. Berapa biaya memanggil pengambil di Jawa? Jika JIT di HotSpot sudah benar sebaris, itu akan dimuat, tetapi jika tidak, itu akan menjadi pemanggilan fungsi. Karena panggilan tersebut berada dalam hot loop, panggilan tersebut akan mengesampingkan semua pengoptimalan lainnya dalam loop tersebut. Oleh karena itu, biaya sebenarnya akan jauh lebih tinggi. Dan Anda segera kehilangan kemampuan untuk melihat sepotong kode dan memahami bahwa kita harus menjalankannya berdasarkan kecepatan jam prosesor, memori, dan cache yang digunakan. Semua ini menjadi menarik hanya jika Anda benar-benar memahami pertunjukannya.
Sekarang kita berada dalam situasi di mana kecepatan prosesor hampir tidak meningkat selama satu dekade. Masa lalu telah kembali! Anda tidak dapat lagi mengandalkan kinerja single-thread yang bagus. Namun jika Anda tiba-tiba masuk ke komputasi paralel, itu sangat sulit, semua orang memandang Anda seperti James Bond. Akselerasi sepuluh kali lipat di sini biasanya terjadi di tempat di mana seseorang mengacaukan sesuatu. Konkurensi membutuhkan banyak pekerjaan. Untuk mendapatkan percepatan XNUMXx, Anda perlu memahami model biaya. Apa dan berapa biayanya. Dan untuk melakukan ini, Anda perlu memahami bagaimana lidah cocok dengan perangkat keras yang mendasarinya.
Martin Thompson memilih kata yang bagus untuk blognya Simpati Mekanis! Anda perlu memahami apa yang akan dilakukan oleh perangkat keras tersebut, bagaimana tepatnya perangkat tersebut akan melakukannya, dan mengapa perangkat tersebut melakukan hal tersebut. Dengan menggunakan ini, cukup mudah untuk mulai menghitung instruksi dan mencari tahu kemana arah waktu eksekusi. Jika Anda tidak memiliki pelatihan yang sesuai, Anda hanya mencari kucing hitam di ruangan gelap. Saya melihat orang-orang yang mengoptimalkan kinerja sepanjang waktu namun tidak tahu apa yang mereka lakukan. Mereka sangat menderita dan tidak membuat banyak kemajuan. Dan ketika saya mengambil potongan kode yang sama, melakukan beberapa peretasan kecil dan mendapatkan percepatan lima atau sepuluh kali lipat, hasilnya seperti: ya, itu tidak adil, kami sudah tahu Anda lebih baik. Luar biasa. Apa yang saya bicarakan... model biaya adalah tentang jenis kode yang Anda tulis dan seberapa cepat rata-rata berjalan dalam gambaran besarnya.

Andrew: Dan bagaimana Anda bisa menyimpan volume seperti itu di kepala Anda? Apakah ini dicapai dengan lebih banyak pengalaman, atau? Dari manakah pengalaman seperti itu berasal?

Jurang: Ya, saya tidak mendapatkan pengalaman saya dengan cara termudah. Saya memprogram dalam Majelis pada masa ketika Anda dapat memahami setiap instruksi. Kedengarannya bodoh, tapi sejak itu set instruksi Z80 selalu ada di kepala saya, di ingatan saya. Saya tidak ingat nama orang dalam satu menit pembicaraan, tapi saya ingat kode yang ditulis 40 tahun yang lalu. Lucu sekali, sepertinya sebuah sindrom"ilmuwan bodoh'.

Pelatihan optimasi tingkat rendah

Andrew: Apakah ada cara yang lebih mudah untuk masuk?

Jurang: Iya dan tidak. Perangkat keras yang kita gunakan tidak banyak berubah seiring berjalannya waktu. Semua orang menggunakan x86, kecuali smartphone Arm. Jika Anda tidak melakukan penyematan keras, Anda melakukan hal yang sama. Oke, selanjutnya. Petunjuknya juga tidak berubah selama berabad-abad. Anda harus pergi dan menulis sesuatu di Majelis. Tidak banyak, tapi cukup untuk mulai memahami. Anda tersenyum, tetapi saya berbicara dengan sangat serius. Anda perlu memahami korespondensi antara bahasa dan perangkat keras. Setelah itu Anda perlu pergi dan menulis sedikit dan membuat kompiler mainan kecil untuk bahasa mainan kecil. Seperti mainan berarti harus dibuat dalam jangka waktu yang wajar. Ini bisa sangat sederhana, tetapi harus menghasilkan instruksi. Tindakan menghasilkan instruksi akan membantu Anda memahami model biaya untuk menjembatani kode tingkat tinggi yang ditulis semua orang dan kode mesin yang berjalan di perangkat keras. Korespondensi ini akan tertanam di otak pada saat penyusun ditulis. Bahkan kompiler paling sederhana sekalipun. Setelah itu, Anda bisa mulai melihat Java dan fakta bahwa jurang semantiknya jauh lebih dalam, dan jauh lebih sulit untuk membangun jembatan di atasnya. Di Jawa, jauh lebih sulit untuk memahami apakah jembatan kita baik atau buruk, apa yang menyebabkannya runtuh dan apa yang tidak. Namun Anda memerlukan semacam titik awal di mana Anda melihat kode dan memahami: "ya, pengambil ini harus disejajarkan setiap saat." Dan ternyata hal ini terkadang terjadi, kecuali dalam situasi ketika metode menjadi terlalu besar, dan JIT mulai menggariskan semuanya. Kinerja tempat-tempat tersebut dapat diprediksi secara instan. Biasanya getter bekerja dengan baik, tapi kemudian Anda melihat hot loop besar dan menyadari bahwa ada beberapa pemanggilan fungsi yang beredar di sana yang tidak tahu apa yang mereka lakukan. Ini adalah masalah dengan meluasnya penggunaan pengambil, alasan mengapa mereka tidak disejajarkan adalah karena tidak jelas apakah mereka adalah pengambil. Jika Anda memiliki basis kode yang sangat kecil, Anda cukup mengingatnya lalu berkata: ini adalah pengambil, dan ini adalah penyetel. Dalam basis kode yang besar, setiap fungsi memiliki sejarahnya sendiri, yang secara umum tidak diketahui siapa pun. Profiler mengatakan bahwa kita kehilangan 24% waktu pada beberapa loop dan untuk memahami apa yang dilakukan loop ini, kita perlu melihat setiap fungsi di dalamnya. Mustahil untuk memahami hal ini tanpa mempelajari fungsinya, dan ini sangat memperlambat proses pemahaman. Itu sebabnya saya tidak menggunakan getter dan setter, saya telah mencapai level baru!
Di mana mendapatkan model biaya? Ya, Anda bisa membaca sesuatu, tentu saja... Tapi menurut saya cara terbaik adalah bertindak. Membuat kompiler kecil akan menjadi cara terbaik untuk memahami model biaya dan menyesuaikannya dengan pikiran Anda. Kompiler kecil yang cocok untuk memprogram microwave adalah tugas bagi pemula. Maksud saya, jika Anda sudah memiliki keterampilan pemrograman, itu sudah cukup. Semua hal ini seperti mengurai string yang Anda miliki sebagai semacam ekspresi aljabar, mengekstraksi instruksi untuk operasi matematika dari sana dalam urutan yang benar, mengambil nilai yang benar dari register - semua ini dilakukan sekaligus. Dan saat Anda melakukannya, hal itu akan terpatri di otak Anda. Saya rasa semua orang tahu apa yang dilakukan kompiler. Dan ini akan memberikan pemahaman tentang model biaya.

Contoh praktis peningkatan kinerja

Andrew: Apa lagi yang harus Anda perhatikan saat mengerjakan produktivitas?

Jurang: Struktur data. Ngomong-ngomong, ya, saya sudah lama tidak mengajar kelas-kelas ini... Sekolah Roket. Itu menyenangkan, tapi membutuhkan banyak usaha, dan saya juga punya kehidupan! OKE. Jadi, di salah satu kelas besar dan menarik, “Kemana kinerja Anda?” Saya memberi contoh kepada siswa: dua setengah gigabyte data fintech dibaca dari file CSV dan kemudian mereka harus menghitung jumlah produk yang terjual. . Data pasar tick reguler. Paket UDP dikonversi ke format teks sejak tahun 70an. Chicago Mercantile Exchange - segala macam hal seperti mentega, jagung, kedelai, hal-hal seperti itu. Penting untuk menghitung produk-produk ini, jumlah transaksi, rata-rata volume pergerakan dana dan barang, dll. Ini matematika perdagangan yang cukup sederhana: temukan kode produk (1-2 karakter dalam tabel hash), dapatkan jumlahnya, tambahkan ke salah satu set perdagangan, tambahkan volume, tambahkan nilai, dan beberapa hal lainnya. Matematika yang sangat sederhana. Implementasi mainannya sangat mudah: semuanya ada dalam sebuah file, saya membaca file dan menelusurinya, membagi catatan individual ke dalam string Java, mencari hal-hal yang diperlukan di dalamnya dan menjumlahkannya sesuai dengan matematika yang dijelaskan di atas. Dan itu bekerja pada kecepatan rendah.

Dengan pendekatan ini, jelas apa yang terjadi, dan komputasi paralel tidak akan membantu, bukan? Ternyata peningkatan kinerja lima kali lipat dapat dicapai hanya dengan memilih struktur data yang tepat. Dan ini bahkan mengejutkan programmer berpengalaman! Dalam kasus khusus saya, triknya adalah Anda tidak boleh membuat alokasi memori dalam hot loop. Ya, ini tidak sepenuhnya benar, tetapi secara umum - Anda tidak boleh menyorot "sekali dalam X" ketika X cukup besar. Jika X adalah dua setengah gigabyte, Anda tidak boleh mengalokasikan apa pun “sekali per huruf”, atau “sekali per baris”, atau “sekali per bidang”, atau semacamnya. Di sinilah waktu dihabiskan. Bagaimana cara kerjanya? Bayangkan saya menelepon String.split() или BufferedReader.readLine(). Readline membuat string dari sekumpulan byte yang datang melalui jaringan, satu kali untuk setiap baris, untuk setiap ratusan juta baris. Saya mengambil baris ini, menguraikannya dan membuangnya. Kenapa saya membuangnya - ya, saya sudah memprosesnya, itu saja. Jadi, untuk setiap byte yang dibaca dari 2.7G ini, dua karakter akan ditulis dalam satu baris, yaitu sudah 5.4G, dan saya tidak membutuhkannya lagi, jadi dibuang. Jika Anda melihat bandwidth memori, kami memuat 2.7G yang melewati memori dan bus memori di prosesor, dan kemudian dua kali lebih banyak dikirim ke saluran yang ada di memori, dan semua ini rusak ketika setiap saluran baru dibuat. Tapi saya perlu membacanya, perangkat keras membacanya, meskipun nanti semuanya rusak. Dan saya harus menuliskannya karena saya membuat baris dan cache penuh - cache tidak dapat menampung 2.7G. Jadi, untuk setiap byte yang saya baca, saya membaca dua byte lagi dan menulis dua byte lagi, dan pada akhirnya mereka memiliki rasio 4:1 - dalam rasio ini kita membuang-buang bandwidth memori. Dan ternyata jika saya melakukannya String.split() – ini bukan kali terakhir saya melakukan ini, mungkin ada 6-7 kolom lagi di dalamnya. Jadi kode klasik membaca CSV dan kemudian menguraikan string menghasilkan pemborosan bandwidth memori sekitar 14:1 dibandingkan dengan apa yang sebenarnya Anda inginkan. Jika Anda membuang pilihan ini, Anda bisa mendapatkan percepatan lima kali lipat.

Dan itu tidak terlalu sulit. Jika Anda melihat kode dari sudut kanan, semuanya menjadi cukup sederhana setelah Anda menyadari masalahnya. Anda tidak boleh berhenti mengalokasikan memori sama sekali: satu-satunya masalah adalah Anda mengalokasikan sesuatu dan memori tersebut langsung mati, dan dalam prosesnya ia membakar sumber daya penting, yang dalam hal ini adalah bandwidth memori. Dan semua ini mengakibatkan penurunan produktivitas. Pada x86 Anda biasanya perlu membakar siklus prosesor secara aktif, tetapi di sini Anda membakar semua memori jauh lebih awal. Solusinya adalah dengan mengurangi jumlah debit. 
Bagian lain dari masalahnya adalah jika Anda menjalankan profiler ketika strip memori habis, tepat pada saat itu terjadi, Anda biasanya menunggu cache kembali karena penuh dengan sampah yang baru saja Anda hasilkan, semua baris itu. Oleh karena itu, setiap operasi pemuatan atau penyimpanan menjadi lambat, karena menyebabkan hilangnya cache - seluruh cache menjadi lambat, menunggu sampah meninggalkannya. Oleh karena itu, profiler hanya akan menampilkan suara acak hangat yang tersebar di seluruh loop - tidak akan ada instruksi atau tempat panas terpisah dalam kode. Hanya kebisingan. Dan jika Anda melihat siklus GC, semuanya adalah Generasi Muda dan super cepat - maksimum mikrodetik atau milidetik. Bagaimanapun, semua kenangan ini mati seketika. Anda mengalokasikan miliaran gigabyte, dan dia memotongnya, memotongnya, dan memotongnya lagi. Semua ini terjadi dengan sangat cepat. Ternyata ada siklus GC yang murah, suara hangat di sepanjang siklus, tapi kami ingin mendapatkan percepatan 5x. Pada saat ini, sesuatu akan terlintas di kepala Anda dan terdengar: "mengapa ini?!" Luapan strip memori tidak ditampilkan di debugger klasik; Anda perlu menjalankan debugger penghitung kinerja perangkat keras dan melihatnya sendiri dan secara langsung. Namun hal ini tidak bisa dicurigai secara langsung dari ketiga gejala tersebut. Gejala ketiga adalah ketika Anda melihat apa yang Anda soroti, tanyakan pada profiler, dan dia menjawab: "Anda membuat satu miliar baris, tetapi GC bekerja secara gratis." Segera setelah ini terjadi, Anda menyadari bahwa Anda telah membuat terlalu banyak objek dan menghabiskan seluruh jalur memori. Ada cara untuk memecahkan masalah ini, namun tidak jelas. 

Masalahnya ada pada struktur data: struktur kosong yang mendasari semua yang terjadi, terlalu besar, 2.7G pada disk, jadi membuat salinan dari hal ini sangat tidak diinginkan - Anda ingin segera memuatnya dari buffer byte jaringan ke dalam register, agar tidak baca-tulis ke baris bolak-balik sebanyak lima kali. Sayangnya, Java tidak memberi Anda perpustakaan seperti itu sebagai bagian dari JDK secara default. Tapi ini sepele bukan? Pada dasarnya, ini adalah 5-10 baris kode yang akan digunakan untuk mengimplementasikan pemuat string buffer Anda sendiri, yang mengulangi perilaku kelas string, sekaligus membungkus buffer byte yang mendasarinya. Hasilnya, ternyata Anda bekerja hampir seolah-olah dengan string, tetapi sebenarnya penunjuk ke buffer berpindah ke sana, dan byte mentah tidak disalin ke mana pun, dan dengan demikian buffer yang sama digunakan kembali berulang kali, dan sistem operasi dengan senang hati mengambil sendiri hal-hal yang dirancang untuknya, seperti buffering ganda tersembunyi dari buffer byte ini, dan Anda tidak lagi menelusuri aliran data yang tidak diperlukan tanpa henti. Omong-omong, apakah Anda memahami bahwa ketika bekerja dengan GC, dijamin setiap alokasi memori tidak akan terlihat oleh prosesor setelah siklus GC terakhir? Oleh karena itu, semua ini tidak mungkin ada di cache, dan kemudian dijamin 100% terjadi kesalahan. Saat bekerja dengan pointer, pada x86, mengurangi register dari memori membutuhkan 1-2 siklus jam, dan segera setelah ini terjadi, Anda membayar, membayar, membayar, karena semua memori aktif SEMBILAN cache – dan ini adalah biaya alokasi memori. Nilai sesungguhnya.

Dengan kata lain, struktur data adalah hal yang paling sulit diubah. Dan begitu Anda menyadari bahwa Anda telah memilih struktur data yang salah yang akan mematikan kinerja di kemudian hari, biasanya ada banyak pekerjaan yang harus dilakukan, namun jika tidak, keadaan akan menjadi lebih buruk. Pertama-tama, Anda perlu memikirkan struktur data, ini penting. Biaya utama di sini ditanggung oleh struktur data yang gemuk, yang mulai digunakan dengan gaya "Saya menyalin struktur data X ke dalam struktur data Y karena saya lebih menyukai bentuk Y." Namun operasi penyalinan (yang tampaknya murah) sebenarnya membuang-buang bandwidth memori dan disitulah semua waktu eksekusi yang terbuang dikuburkan. Jika saya memiliki string JSON raksasa dan saya ingin mengubahnya menjadi pohon DOM POJO terstruktur atau semacamnya, operasi penguraian string tersebut dan membuat POJO, lalu mengakses POJO lagi nanti, akan menghasilkan biaya yang tidak perlu - itu saja tidak murah. Kecuali jika Anda lebih sering menjalankan POJO daripada menjalankan string. Begitu saja, Anda dapat mencoba mendekripsi string dan mengekstrak hanya apa yang Anda perlukan dari sana, tanpa mengubahnya menjadi POJO apa pun. Jika semua ini terjadi pada jalur yang memerlukan kinerja maksimum, tidak ada POJO untuk Anda, Anda harus menggali jalur tersebut secara langsung.

Mengapa membuat bahasa pemrograman Anda sendiri

Andrew: Anda mengatakan bahwa untuk memahami model biaya, Anda perlu menulis bahasa kecil Anda sendiri...

Jurang: Bukan bahasa, tapi kompiler. Bahasa dan kompiler adalah dua hal yang berbeda. Perbedaan terpenting ada di kepala Anda. 

Andrew: Ngomong-ngomong, sejauh yang saya tahu, Anda sedang bereksperimen dengan membuat bahasa Anda sendiri. Untuk apa?

Jurang: Karena aku bisa! Saya setengah pensiun, jadi ini hobi saya. Saya telah menerapkan bahasa orang lain sepanjang hidup saya. Saya juga banyak mengerjakan gaya pengkodean saya. Dan juga karena saya melihat masalah dalam bahasa lain. Saya melihat ada cara yang lebih baik untuk melakukan hal-hal yang biasa dilakukan. Dan saya akan menggunakannya. Saya bosan melihat masalah dalam diri saya sendiri, di Java, di Python, di bahasa lain. Saya sekarang menulis di React Native, JavaScript dan Elm sebagai hobi yang bukan tentang pensiun, tapi tentang kerja aktif. Saya juga menulis dengan Python dan kemungkinan besar akan terus mengerjakan pembelajaran mesin untuk backend Java. Ada banyak bahasa populer dan semuanya memiliki fitur menarik. Setiap orang memiliki kelebihannya masing-masing dan Anda dapat mencoba menggabungkan semua fitur ini. Jadi, saya mempelajari hal-hal yang saya minati, perilaku bahasa, mencoba menghasilkan semantik yang masuk akal. Dan sejauh ini saya berhasil! Saat ini saya kesulitan dengan semantik memori, karena saya ingin memilikinya seperti di C dan Java, dan mendapatkan model memori dan semantik memori yang kuat untuk memuat dan menyimpan. Pada saat yang sama, miliki inferensi tipe otomatis seperti di Haskell. Di sini, saya mencoba menggabungkan inferensi tipe mirip Haskell dengan memori yang berfungsi di C dan Java. Ini yang saya lakukan selama 2-3 bulan terakhir, misalnya.

Andrew: Jika Anda membangun sebuah bahasa yang mengambil aspek yang lebih baik dari bahasa lain, apakah menurut Anda seseorang akan melakukan sebaliknya: mengambil ide Anda dan menggunakannya?

Jurang: Ini adalah bagaimana bahasa-bahasa baru muncul! Mengapa Java mirip dengan C? Karena C memiliki sintaks yang baik yang dipahami semua orang dan Java terinspirasi oleh sintaks ini, menambahkan keamanan tipe, pemeriksaan batas array, GC, dan mereka juga meningkatkan beberapa hal dari C. Mereka menambahkan sendiri. Tapi mereka cukup terinspirasi bukan? Setiap orang berdiri di pundak para raksasa yang datang sebelum Anda - begitulah kemajuan dicapai.

Andrew: Menurut pemahaman saya, bahasa Anda akan aman untuk diingat. Pernahkah Anda berpikir untuk menerapkan sesuatu seperti pemeriksa pinjaman dari Rust? Pernahkah Anda melihatnya, apa pendapat Anda tentang dia?

Jurang: Ya, saya sudah lama menulis C, dengan semua malloc ini dan gratis, dan mengelola seumur hidup secara manual. Tahukah Anda, 90-95% masa hidup yang dikontrol secara manual memiliki struktur yang sama. Dan sangat, sangat menyakitkan melakukannya secara manual. Saya ingin kompiler memberi tahu Anda apa yang terjadi di sana dan apa yang Anda capai dengan tindakan Anda. Untuk beberapa hal, pemeriksa pinjaman melakukan hal ini secara langsung. Dan seharusnya secara otomatis menampilkan informasi, memahami segalanya, dan bahkan tidak membebani saya untuk menyajikan pemahaman tersebut. Ia harus melakukan setidaknya analisis escape lokal, dan hanya jika gagal, maka ia perlu menambahkan anotasi tipe yang akan menggambarkan masa pakai - dan skema seperti itu jauh lebih kompleks daripada pemeriksa pinjaman, atau bahkan pemeriksa memori yang ada. Pilihan antara "semuanya baik-baik saja" dan "Saya tidak mengerti apa pun" - tidak, pasti ada sesuatu yang lebih baik. 
Jadi, sebagai seseorang yang telah menulis banyak kode dalam C, menurut saya memiliki dukungan untuk kontrol otomatis seumur hidup adalah hal yang paling penting. Saya juga muak dengan banyaknya Java yang menggunakan memori dan keluhan utamanya adalah GC. Saat Anda mengalokasikan memori di Java, Anda tidak akan mendapatkan kembali memori lokal pada siklus GC terakhir. Hal ini tidak terjadi pada bahasa dengan manajemen memori yang lebih tepat. Jika Anda menelepon malloc, Anda langsung mendapatkan memori yang biasa digunakan. Biasanya Anda melakukan beberapa hal sementara dengan memori dan segera mengembalikannya kembali. Dan ia segera kembali ke kumpulan malloc, dan siklus malloc berikutnya menariknya keluar lagi. Oleh karena itu, penggunaan memori sebenarnya dikurangi menjadi kumpulan objek hidup pada waktu tertentu, ditambah kebocoran. Dan jika semuanya tidak bocor dengan cara yang tidak senonoh, sebagian besar memori berakhir di cache dan prosesor, dan berfungsi dengan cepat. Namun memerlukan banyak manajemen memori manual dengan malloc dan panggilan gratis dalam urutan yang benar, di tempat yang tepat. Rust dapat menangani hal ini dengan baik, dan dalam banyak kasus memberikan kinerja yang lebih baik, karena konsumsi memori dipersempit menjadi hanya komputasi saat ini - dibandingkan menunggu siklus GC berikutnya untuk mengosongkan memori. Hasilnya, kami mendapat cara yang sangat menarik untuk meningkatkan kinerja. Dan cukup kuat - maksud saya, saya melakukan hal seperti itu saat memproses data untuk fintech, dan ini memungkinkan saya mendapatkan kecepatan sekitar lima kali lipat. Ini merupakan peningkatan yang cukup besar, terutama di dunia di mana prosesor tidak menjadi lebih cepat dan kami masih menunggu peningkatannya.

Karir Insinyur Kinerja

Andrew: Saya juga ingin bertanya tentang karir secara umum. Anda menjadi terkenal dengan pekerjaan JIT Anda di HotSpot dan kemudian pindah ke Azul, yang juga merupakan perusahaan JVM. Namun kami sudah lebih banyak mengerjakan perangkat keras daripada perangkat lunak. Lalu mereka tiba-tiba beralih ke Big Data dan Machine Learning, lalu ke deteksi penipuan. Bagaimana ini bisa terjadi? Ini adalah bidang pembangunan yang sangat berbeda.

Jurang: Saya sudah cukup lama memprogram dan berhasil mengambil banyak kelas yang berbeda. Dan ketika orang berkata: “oh, kamulah yang membuat JIT untuk Java!”, itu selalu lucu. Namun sebelum itu, saya sedang mengerjakan tiruan PostScript - bahasa yang pernah digunakan Apple untuk printer lasernya. Dan sebelumnya saya melakukan implementasi bahasa Keempat. Saya pikir tema umum bagi saya adalah pengembangan alat. Sepanjang hidup saya, saya telah membuat alat yang dapat digunakan orang lain untuk menulis program keren mereka. Namun saya juga terlibat dalam pengembangan sistem operasi, driver, debugger tingkat kernel, bahasa untuk pengembangan OS, yang awalnya sepele, namun seiring berjalannya waktu menjadi semakin kompleks. Namun topik utamanya masih pengembangan alat. Sebagian besar hidupku berlalu antara Azul dan Sun, dan itu tentang Jawa. Namun ketika saya mendalami Big Data dan Machine Learning, saya kembali berpikir dan berkata, “Oh, sekarang kita menghadapi masalah yang tidak sepele, dan ada banyak hal menarik yang terjadi dan orang-orang melakukan sesuatu.” Ini adalah jalur pembangunan yang bagus untuk diambil.

Ya, saya sangat menyukai komputasi terdistribusi. Pekerjaan pertama saya adalah sebagai mahasiswa C, di sebuah proyek periklanan. Ini adalah komputasi terdistribusi pada chip Zilog Z80 yang mengumpulkan data untuk OCR analog, yang dihasilkan oleh penganalisis analog nyata. Itu adalah topik yang keren dan benar-benar gila. Tapi ada masalah, beberapa bagian tidak dikenali dengan benar, jadi Anda harus mengambil gambar dan menunjukkannya kepada orang yang sudah bisa membaca dengan matanya dan melaporkan apa yang dikatakannya, dan oleh karena itu ada pekerjaan dengan data, dan pekerjaan ini memiliki bahasa mereka sendiri. Ada backend yang memproses semua ini - Z80 berjalan secara paralel dengan terminal vt100 berjalan - satu terminal per orang, dan ada model pemrograman paralel pada Z80. Beberapa bagian memori umum yang digunakan bersama oleh semua Z80 dalam konfigurasi bintang; Backplane juga dibagikan, dan separuh RAM dibagikan dalam jaringan, dan separuh lainnya bersifat pribadi atau digunakan untuk hal lain. Sistem terdistribusi paralel yang sangat kompleks dengan memori bersama... semi-bersama. Kapan ini... Saya bahkan tidak ingat, sekitar pertengahan tahun 80an. Sudah lama sekali. 
Ya, mari kita asumsikan bahwa 30 tahun adalah waktu yang cukup lama. Permasalahan terkait komputasi terdistribusi sudah ada sejak lama; masyarakat telah lama bertikai dengan komputasi terdistribusi. Beowulf-cluster. Cluster seperti itu terlihat seperti... Misalnya: ada Ethernet dan x86 cepat Anda terhubung ke Ethernet ini, dan sekarang Anda ingin mendapatkan memori bersama palsu, karena saat itu tidak ada yang bisa melakukan pengkodean komputasi terdistribusi, itu terlalu sulit dan oleh karena itu tidak ada adalah memori bersama palsu dengan halaman memori perlindungan di x86, dan jika Anda menulis ke halaman ini, kami memberi tahu prosesor lain bahwa jika mereka mengakses memori bersama yang sama, itu perlu dimuat dari Anda, dan dengan demikian sesuatu seperti protokol untuk mendukung koherensi cache muncul dan perangkat lunak untuk ini. Konsep yang menarik. Masalah sebenarnya, tentu saja, ada hal lain. Semua ini berhasil, tetapi Anda dengan cepat mendapatkan masalah kinerja, karena tidak ada yang memahami model kinerja pada tingkat yang cukup baik - pola akses memori apa yang ada di sana, bagaimana memastikan bahwa node tidak saling melakukan ping tanpa henti, dan seterusnya.

Apa yang saya temukan di H2O adalah bahwa pengembang sendirilah yang bertanggung jawab untuk menentukan di mana paralelisme tersembunyi dan di mana tidak. Saya menemukan model pengkodean yang membuat penulisan kode berkinerja tinggi menjadi mudah dan sederhana. Tetapi menulis kode yang berjalan lambat itu sulit dan akan terlihat buruk. Anda harus serius mencoba menulis kode lambat, Anda harus menggunakan metode non-standar. Kode pengereman terlihat sekilas. Akibatnya, Anda biasanya menulis kode yang berjalan cepat, namun Anda harus memikirkan apa yang harus dilakukan dalam kasus memori bersama. Semua ini terkait dengan array besar dan perilakunya mirip dengan array besar non-volatil di Java paralel. Maksud saya, bayangkan dua thread menulis ke array paralel, salah satunya menang, dan yang lainnya kalah, dan Anda tidak tahu yang mana. Jika tidak mudah berubah, maka urutannya bisa sesuai keinginan Anda - dan ini bekerja dengan sangat baik. Orang-orang sangat peduli dengan urutan operasi, mereka menempatkan volatil di tempat yang tepat, dan mereka mengharapkan masalah kinerja terkait memori di tempat yang tepat. Jika tidak, mereka hanya akan menulis kode dalam bentuk perulangan dari 1 ke N, di mana N adalah beberapa triliunan, dengan harapan bahwa semua kasus kompleks secara otomatis akan menjadi paralel - dan hal tersebut tidak akan berhasil di sana. Namun di H2O ini bukan Java atau Scala; Anda dapat menganggapnya sebagai "Java minus minus" jika Anda mau. Ini adalah gaya pemrograman yang sangat jelas dan mirip dengan penulisan kode C atau Java sederhana dengan loop dan array. Namun pada saat yang sama, memori dapat diproses dalam terabyte. Saya masih menggunakan H2O. Saya menggunakannya dari waktu ke waktu di berbagai proyek - dan ini masih merupakan yang tercepat, puluhan kali lebih cepat daripada pesaingnya. Jika Anda melakukan Big Data dengan data kolom, sangat sulit mengalahkan H2O.

Tantangan Teknis

Andrew: Apa tantangan terbesar Anda sepanjang karier Anda?

Jurang: Apakah kita membahas masalah teknis atau non-teknis? Menurut saya, tantangan terbesar bukanlah tantangan teknis. 
Adapun tantangan teknis. Saya baru saja mengalahkan mereka. Saya bahkan tidak tahu apa yang terbesar, tapi ada beberapa yang cukup menarik yang membutuhkan cukup banyak waktu, perjuangan mental. Ketika saya pergi ke Sun, saya yakin bahwa saya akan membuat kompiler yang cepat, dan sekelompok senior menjawab bahwa saya tidak akan pernah berhasil. Tapi saya mengikuti jalur ini, menulis kompiler ke pengalokasi register, dan itu cukup cepat. Ini secepat C1 modern, tetapi pengalokasinya jauh lebih lambat pada saat itu, dan jika dipikir-pikir, ini adalah masalah struktur data yang besar. Saya membutuhkannya untuk menulis pengalokasi register grafis dan saya tidak memahami dilema antara ekspresi kode dan kecepatan, yang ada pada era itu dan sangat penting. Ternyata struktur data biasanya melebihi ukuran cache pada x86 pada waktu itu, dan oleh karena itu, jika saya awalnya berasumsi bahwa pengalokasi register akan menghasilkan 5-10 persen dari total waktu jitter, maka kenyataannya ternyata demikian. 50 persen.

Seiring berjalannya waktu, kompiler menjadi lebih bersih dan efisien, berhenti menghasilkan kode yang buruk dalam banyak kasus, dan kinerja semakin menyerupai apa yang dihasilkan oleh kompiler C. Kecuali, tentu saja, Anda menulis beberapa omong kosong yang bahkan C tidak dapat mempercepatnya. . Jika Anda menulis kode seperti C, Anda akan mendapatkan performa seperti C dalam lebih banyak kasus. Dan semakin jauh Anda melangkah, semakin sering Anda mendapatkan kode yang secara asimtotik bertepatan dengan level C, pengalokasi register mulai terlihat seperti sesuatu yang lengkap... terlepas dari apakah kode Anda berjalan cepat atau lambat. Saya terus mengerjakan pengalokasi untuk membuat pilihan yang lebih baik. Dia menjadi lebih lambat dan lebih lambat, tapi dia memberikan kinerja yang lebih baik dan lebih baik dalam kasus-kasus di mana tidak ada orang lain yang bisa mengatasinya. Saya bisa mendalami pengalokasi register, mengubur satu bulan pekerjaan di sana, dan tiba-tiba seluruh kode akan mulai dieksekusi 5% lebih cepat. Hal ini terjadi berulang kali dan pengalokasi register menjadi semacam karya seni - semua orang menyukainya atau membencinya, dan orang-orang dari akademi mengajukan pertanyaan tentang topik “mengapa semuanya dilakukan dengan cara ini”, mengapa tidak pemindaian garis, dan apa bedanya. Jawabannya masih sama: pengalokasi berdasarkan lukisan grafik ditambah kerja yang sangat hati-hati dengan kode buffer sama dengan senjata kemenangan, kombinasi terbaik yang tidak dapat dikalahkan oleh siapa pun. Dan ini adalah hal yang tidak jelas. Segala hal lain yang dilakukan penyusun di sana adalah hal-hal yang telah dipelajari dengan cukup baik, meskipun hal-hal tersebut juga telah dibawa ke tingkat seni. Saya selalu melakukan hal-hal yang seharusnya mengubah kompiler menjadi sebuah karya seni. Namun semua ini bukanlah sesuatu yang luar biasa - kecuali pengalokasi register. Triknya adalah berhati-hati menebang di bawah beban dan, jika ini terjadi (saya dapat menjelaskan lebih detail jika tertarik), ini berarti Anda dapat melakukan inline dengan lebih agresif, tanpa risiko terjatuh dalam jadwal pertunjukan. Pada masa itu, ada sekelompok kompiler skala penuh, penuh dengan pernak-pernik dan peluit, yang memiliki pengalokasi register, namun tidak ada orang lain yang bisa melakukannya.

Masalahnya adalah jika Anda menambahkan metode yang tunduk pada inlining, menambah dan meningkatkan area inlining, himpunan nilai yang digunakan langsung melebihi jumlah register, dan Anda harus memotongnya. Tingkat kritis biasanya muncul ketika pengalokasi menyerah, dan satu kandidat yang baik untuk tumpahan bernilai yang lain, Anda akan menjual beberapa barang yang umumnya liar. Nilai inlining di sini adalah Anda kehilangan sebagian dari overhead, overhead untuk panggilan dan penyimpanan, Anda dapat melihat nilai-nilai di dalamnya dan dapat lebih mengoptimalkannya. Biaya inlining adalah sejumlah besar nilai langsung terbentuk, dan jika pengalokasi register Anda habis lebih dari yang diperlukan, Anda langsung kalah. Oleh karena itu, sebagian besar pengalokasi mempunyai masalah: ketika saluran masuk melewati garis tertentu, segala sesuatu di dunia mulai terpotong dan produktivitas dapat dibuang ke toilet. Mereka yang mengimplementasikan kompiler menambahkan beberapa heuristik: misalnya, untuk menghentikan inlining, memulai dengan ukuran yang cukup besar, karena alokasi akan merusak segalanya. Ini adalah bagaimana kekusutan dalam grafik kinerja terbentuk - Anda sejajar, sejajar, kinerja perlahan-lahan tumbuh - dan kemudian booming! – jatuh seperti dongkrak cepat karena Anda terlalu banyak berbaris. Beginilah cara semuanya bekerja sebelum munculnya Java. Java membutuhkan lebih banyak inline, jadi saya harus membuat pengalokasi saya jauh lebih agresif sehingga menjadi rata daripada crash, dan jika Anda memasukkan terlalu banyak, itu akan mulai tumpah, tetapi momen “tidak ada lagi yang tumpah” masih datang. Ini adalah pengamatan yang menarik dan muncul begitu saja, tidak jelas, tetapi membuahkan hasil yang baik. Saya menggunakan inlining yang agresif dan membawa saya ke tempat di mana kinerja Java dan C bekerja berdampingan. Keduanya sangat mirip - Saya bisa menulis kode Java yang jauh lebih cepat daripada kode C dan sejenisnya, tetapi rata-rata, dalam gambaran besarnya, keduanya kira-kira sebanding. Saya pikir bagian dari manfaat ini adalah pengalokasi register, yang memungkinkan saya untuk melakukan inline sebodoh mungkin. Saya hanya menyejajarkan semua yang saya lihat. Pertanyaannya di sini adalah apakah pengalokasi berfungsi dengan baik, apakah hasilnya adalah kode yang berfungsi dengan baik. Ini merupakan tantangan besar: memahami semua ini dan mewujudkannya.

Sedikit tentang alokasi register dan multi-core

Vladimir: Masalah seperti alokasi register tampak seperti topik yang abadi dan tidak ada habisnya. Saya bertanya-tanya apakah pernah ada ide yang tampak menjanjikan namun kemudian gagal dalam praktiknya?

Jurang: Tentu! Alokasi register adalah area di mana Anda mencoba menemukan beberapa heuristik untuk menyelesaikan masalah NP-complete. Dan Anda tidak akan pernah bisa mencapai solusi yang sempurna, bukan? Ini sungguh mustahil. Lihat, kompilasi Ahead of Time - ini juga berfungsi dengan buruk. Percakapan di sini adalah tentang beberapa kasus rata-rata. Tentang kinerja tipikal, sehingga Anda dapat mengukur sesuatu yang menurut Anda merupakan kinerja tipikal yang baik - lagipula, Anda berupaya memperbaikinya! Alokasi register adalah topik tentang kinerja. Setelah Anda memiliki prototipe pertama, ia berfungsi dan melukiskan apa yang dibutuhkan, pekerjaan pertunjukan dimulai. Anda perlu belajar mengukur dengan baik. Mengapa ini penting? Jika Anda memiliki data yang jelas, Anda dapat melihat area yang berbeda dan melihat: ya, ini membantu di sini, tapi di situlah semuanya rusak! Beberapa ide bagus muncul, Anda menambahkan heuristik baru dan tiba-tiba semuanya mulai bekerja rata-rata sedikit lebih baik. Atau itu tidak dimulai. Saya punya banyak kasus di mana kami memperjuangkan kinerja lima persen yang membedakan pengembangan kami dari pengalokasi sebelumnya. Dan setiap kali terlihat seperti ini: di suatu tempat Anda menang, di suatu tempat Anda kalah. Jika Anda memiliki alat analisis kinerja yang baik, Anda dapat menemukan ide-ide yang hilang dan memahami mengapa ide tersebut gagal. Mungkin ada baiknya membiarkan semuanya apa adanya, atau mungkin mengambil pendekatan yang lebih serius untuk menyempurnakannya, atau keluar dan memperbaiki hal lain. Itu banyak hal! Saya membuat peretasan keren ini, tetapi saya juga memerlukan yang ini, dan yang ini, dan yang ini - dan kombinasi totalnya memberikan beberapa perbaikan. Dan penyendiri bisa gagal. Ini adalah sifat kinerja pekerjaan pada masalah NP-complete.

Vladimir: Ada perasaan bahwa hal-hal seperti mengecat pengalokasi adalah masalah yang sudah terpecahkan. Yah, itu sudah diputuskan untukmu, menilai dari apa yang kamu katakan, jadi apakah itu layak dilakukan...

Jurang: Hal ini tidak diselesaikan seperti itu. Andalah yang harus mengubahnya menjadi “terpecahkan”. Ada masalah yang sulit dan perlu dipecahkan. Setelah ini selesai, saatnya untuk meningkatkan produktivitas. Anda perlu melakukan pendekatan terhadap pekerjaan ini dengan tepat - lakukan tolok ukur, kumpulkan metrik, jelaskan situasi ketika, saat Anda mengembalikan ke versi sebelumnya, peretasan lama Anda mulai berfungsi kembali (atau sebaliknya, berhenti). Dan jangan menyerah sampai Anda mencapai sesuatu. Seperti yang sudah saya katakan, jika ada ide-ide keren yang tidak berhasil, namun dalam bidang alokasi register ide kira-kira tidak ada habisnya. Misalnya, Anda bisa membaca publikasi ilmiah. Meskipun kini kawasan ini mulai bergerak jauh lebih lambat dan menjadi lebih jelas dibandingkan di masa mudanya. Namun, ada banyak sekali orang yang bekerja di bidang ini dan semua ide mereka patut untuk dicoba, mereka semua menunggu di depan mata. Dan Anda tidak akan tahu seberapa bagusnya kecuali Anda mencobanya. Seberapa baik ide-ide tersebut berintegrasi dengan semua hal lain di pengalokasi Anda, karena pengalokasi melakukan banyak hal, dan beberapa ide tidak akan berfungsi di pengalokasi spesifik Anda, namun di pengalokasi lain ide-ide tersebut akan bekerja dengan mudah. Cara utama untuk memenangkan pengalokasi adalah dengan menarik barang lambat keluar dari jalur utama dan memaksanya untuk berpisah di sepanjang batas jalur lambat. Jadi jika Anda ingin menjalankan GC, ambil jalur lambat, deoptimisasi, berikan pengecualian, semua itu - Anda tahu hal-hal ini relatif jarang terjadi. Dan itu sangat jarang, saya memeriksanya. Anda melakukan pekerjaan ekstra dan ini menghilangkan banyak batasan pada jalur lambat ini, namun itu tidak terlalu menjadi masalah karena jalur tersebut lambat dan jarang dilalui. Misalnya, penunjuk nol - tidak pernah terjadi, bukan? Anda perlu memiliki beberapa jalur untuk berbagai hal, tetapi jalur tersebut tidak boleh mengganggu jalur utama. 

Vladimir: Apa pendapat Anda tentang multi-core, ketika ada ribuan core sekaligus? Apakah ini hal yang bermanfaat?

Jurang: Keberhasilan GPU menunjukkan cukup berguna!

Vladimir: Mereka cukup terspesialisasi. Bagaimana dengan prosesor tujuan umum?

Jurang: Nah, itu tadi model bisnis Azul. Jawabannya muncul di era ketika orang sangat menyukai kinerja yang dapat diprediksi. Sulit untuk menulis kode paralel saat itu. Model pengkodean H2O sangat skalabel, namun ini bukan model tujuan umum. Mungkin sedikit lebih umum dibandingkan saat menggunakan GPU. Apakah kita berbicara tentang kompleksitas pengembangan hal tersebut atau kompleksitas penggunaannya? Misalnya, Azul memberi saya pelajaran menarik, yang agak tidak jelas: cache kecil adalah hal yang normal. 

Tantangan terbesar dalam hidup

Vladimir: Bagaimana dengan tantangan non-teknis?

Jurang: Tantangan terbesarnya bukanlah... bersikap baik dan baik kepada orang lain. Dan sebagai hasilnya, saya terus-menerus menemukan diri saya dalam situasi yang sangat konflik. Yang saya tahu ada yang tidak beres, tetapi tidak tahu bagaimana mengatasi masalah tersebut dan tidak bisa mengatasinya. Banyak permasalahan jangka panjang, yang berlangsung selama beberapa dekade, muncul dengan cara ini. Fakta bahwa Java memiliki kompiler C1 dan C2 adalah konsekuensi langsung dari hal ini. Fakta bahwa tidak ada kompilasi multi-level di Jawa selama sepuluh tahun berturut-turut juga merupakan konsekuensi langsungnya. Jelas bahwa kita membutuhkan sistem seperti itu, namun tidak jelas mengapa sistem tersebut tidak ada. Saya mempunyai masalah dengan satu insinyur... atau sekelompok insinyur. Suatu ketika, ketika saya mulai bekerja di Sun, saya... Oke, tidak hanya itu, saya biasanya selalu punya pendapat sendiri tentang segala hal. Dan saya pikir memang benar bahwa Anda bisa menerima kebenaran Anda ini dan menceritakannya secara langsung. Terutama karena saya sering kali benar. Dan jika Anda tidak menyukai pendekatan ini... terutama jika Anda jelas-jelas salah dan melakukan hal yang tidak masuk akal... Secara umum, hanya sedikit orang yang dapat mentolerir bentuk komunikasi ini. Meskipun ada yang bisa, seperti saya. Saya telah membangun seluruh hidup saya berdasarkan prinsip meritokratis. Jika Anda menunjukkan sesuatu yang salah, saya akan segera berbalik dan berkata: Anda mengatakan omong kosong. Pada saat yang sama, tentu saja, saya meminta maaf dan sebagainya, saya akan mencatat manfaatnya, jika ada, dan mengambil tindakan lain yang benar. Di sisi lain, saya benar sekali mengenai persentase total waktu yang sangat besar. Dan itu tidak berhasil dengan baik dalam hubungan dengan orang lain. Saya tidak berusaha bersikap baik, tapi saya mengajukan pertanyaan terus terang. “Ini tidak akan pernah berhasil, karena satu, dua, dan tiga.” Dan mereka seperti, “Oh!” Ada konsekuensi lain yang mungkin sebaiknya diabaikan: misalnya, konsekuensi yang menyebabkan perceraian dengan istri saya dan sepuluh tahun depresi setelahnya.

Tantangan adalah perjuangan dengan orang-orang, dengan persepsi mereka tentang apa yang bisa atau tidak bisa dilakukan, apa yang penting dan apa yang tidak. Ada banyak tantangan tentang gaya pengkodean. Saya masih menulis banyak kode, dan pada hari-hari itu saya bahkan harus memperlambatnya karena saya melakukan terlalu banyak tugas paralel dan melakukannya dengan buruk, alih-alih fokus pada satu tugas. Melihat ke belakang, saya menulis setengah kode untuk perintah Java JIT, perintah C2. Pembuat kode tercepat berikutnya menulis setengah lambat, setengah berikutnya lambat, dan terjadi penurunan eksponensial. Orang ketujuh di baris ini sangat, sangat lambat - itu selalu terjadi! Saya menyentuh banyak kode. Saya melihat siapa yang menulis apa, tanpa kecuali, saya menatap kode mereka, meninjau masing-masing kode, dan masih terus menulis lebih banyak sendiri daripada mereka. Pendekatan ini tidak berhasil dengan baik pada manusia. Beberapa orang tidak menyukai ini. Dan ketika mereka tidak bisa mengatasinya, segala macam keluhan pun dimulai. Misalnya, saya pernah diminta untuk berhenti membuat kode karena saya menulis terlalu banyak kode dan itu membahayakan tim, dan itu semua terdengar seperti lelucon bagi saya: kawan, jika anggota tim lainnya menghilang dan saya terus menulis kode, Anda hanya akan kehilangan setengah tim. Di sisi lain, jika saya terus menulis kode dan Anda kehilangan separuh tim, itu terdengar seperti manajemen yang sangat buruk. Saya tidak pernah benar-benar memikirkannya, tidak pernah membicarakannya, tetapi hal itu masih ada di kepala saya. Pikiran itu berputar-putar di benak saya: “Apakah kalian semua bercanda?” Jadi, masalah terbesarnya adalah saya dan hubungan saya dengan orang lain. Sekarang saya memahami diri saya jauh lebih baik, saya sudah lama menjadi pemimpin tim programmer, dan sekarang saya langsung memberi tahu orang-orang: Anda tahu, saya adalah saya, dan Anda harus berurusan dengan saya - bolehkah saya berdiri? Di Sini? Dan ketika mereka mulai menanganinya, semuanya berhasil. Sebenarnya, aku tidak jahat dan tidak baik, aku tidak punya niat buruk atau aspirasi egois, itu hanya esensiku, dan aku harus menjalaninya.

Andrew: Baru-baru ini semua orang mulai berbicara tentang kesadaran diri bagi para introvert, dan soft skill secara umum. Apa yang bisa Anda katakan tentang ini?

Jurang: Ya, itulah wawasan dan hikmah yang saya dapat dari perceraian saya dengan istri saya. Apa yang saya pelajari dari perceraian adalah memahami diri saya sendiri. Dari sinilah saya mulai memahami orang lain. Pahami cara kerja interaksi ini. Hal ini menyebabkan penemuan satu demi satu. Ada kesadaran tentang siapa saya dan apa yang saya wakili. Apa yang saya lakukan: entah saya sibuk dengan tugas, atau saya menghindari konflik, atau hal lain - dan tingkat kesadaran diri ini sangat membantu saya untuk tetap memegang kendali. Setelah ini segalanya menjadi lebih mudah. Satu hal yang saya temukan tidak hanya pada diri saya sendiri, tetapi juga pada programmer lain adalah ketidakmampuan untuk mengungkapkan pikiran secara verbal ketika Anda berada dalam keadaan stres emosional. Misalnya, Anda sedang duduk di sana sambil coding, dalam keadaan mengalir, lalu mereka berlari ke arah Anda dan mulai berteriak histeris bahwa ada sesuatu yang rusak dan sekarang tindakan ekstrem akan diambil terhadap Anda. Dan Anda tidak dapat mengucapkan sepatah kata pun karena Anda sedang dalam keadaan stres emosional. Pengetahuan yang diperoleh memungkinkan Anda untuk mempersiapkan momen ini, bertahan hidup dan melanjutkan ke rencana retret, setelah itu Anda dapat melakukan sesuatu. Jadi ya, ketika Anda mulai menyadari cara kerjanya, itu adalah peristiwa besar yang mengubah hidup. 
Saya sendiri tidak dapat menemukan kata-kata yang tepat, tetapi saya ingat urutan tindakannya. Intinya adalah reaksi ini bersifat fisik dan verbal, dan Anda memerlukan ruang. Ruang seperti itu, dalam pengertian Zen. Inilah yang perlu dijelaskan, dan kemudian segera minggir – menjauh secara fisik. Ketika saya tetap diam secara verbal, saya dapat memproses situasi secara emosional. Saat adrenalin mencapai otak Anda, mengalihkan Anda ke mode bertarung atau lari, Anda tidak bisa lagi berkata apa-apa, tidak - sekarang Anda adalah seorang idiot, insinyur yang mencambuk, tidak mampu memberikan respons yang layak atau bahkan menghentikan serangan, dan penyerangnya bebas. untuk menyerang lagi dan lagi. Pertama-tama Anda harus menjadi diri sendiri lagi, mendapatkan kembali kendali, keluar dari mode “lawan atau lari”.

Dan untuk ini kita memerlukan ruang verbal. Hanya ruang kosong. Jika Anda mengatakan sesuatu, maka Anda bisa mengatakan hal itu dengan tepat, lalu pergi dan benar-benar mencari "ruang" untuk diri Anda sendiri: berjalan-jalan di taman, mengunci diri di kamar mandi - tidak masalah. Hal utama adalah memutuskan hubungan untuk sementara waktu dari situasi itu. Segera setelah Anda mematikan setidaknya beberapa detik, kendali kembali, Anda mulai berpikir jernih. “Oke, aku bukan orang bodoh, aku tidak melakukan hal bodoh, aku orang yang cukup berguna.” Setelah Anda mampu meyakinkan diri sendiri, sekarang saatnya melanjutkan ke tahap berikutnya: memahami apa yang terjadi. Anda diserang, serangan itu datang dari tempat yang tidak Anda duga, itu adalah penyergapan yang tidak jujur ​​dan keji. Ini buruk. Langkah selanjutnya adalah memahami mengapa penyerang membutuhkan ini. Benarkah kenapa? Mungkin karena dia sendiri yang marah? Kenapa dia marah? Misalnya karena dia mengacau dan tidak bisa menerima tanggung jawab? Ini adalah cara untuk menangani seluruh situasi dengan hati-hati. Namun hal ini membutuhkan ruang untuk bermanuver, ruang verbal. Langkah pertama adalah memutuskan kontak verbal. Hindari diskusi dengan kata-kata. Batalkan, pergilah secepat mungkin. Jika itu percakapan telepon, tutup saja - ini adalah keterampilan yang saya pelajari dari komunikasi dengan mantan istri saya. Jika pembicaraan tidak berjalan baik, ucapkan saja "selamat tinggal" dan tutup telepon. Dari sisi lain telepon: “bla bla bla”, Anda menjawab: “ya, sampai jumpa!” dan menutup telepon. Kamu akhiri saja pembicaraannya. Lima menit kemudian, ketika kemampuan berpikir rasional kembali kepada Anda, Anda sudah sedikit tenang, Anda bisa memikirkan segala sesuatu yang terjadi dan apa yang akan terjadi selanjutnya. Dan mulailah merumuskan respons yang bijaksana, bukan hanya bereaksi karena emosi. Bagi saya, terobosan dalam kesadaran diri adalah kenyataan bahwa jika terjadi tekanan emosional, saya tidak dapat berbicara. Keluar dari keadaan ini, memikirkan dan merencanakan bagaimana merespons dan mengkompensasi masalah - ini adalah langkah yang tepat jika Anda tidak dapat berbicara. Cara termudah adalah dengan melarikan diri dari situasi di mana stres emosional terwujud dan berhenti berpartisipasi dalam stres tersebut. Setelah itu Anda menjadi mampu berpikir, ketika Anda dapat berpikir, Anda menjadi mampu berbicara, dan seterusnya.

Ngomong-ngomong, di pengadilan, pengacara lawan mencoba melakukan ini padamu - sekarang sudah jelas alasannya. Karena dia memiliki kemampuan untuk menekan Anda sedemikian rupa sehingga Anda bahkan tidak bisa mengucapkan nama Anda, misalnya. Dalam arti sebenarnya, Anda tidak akan dapat berbicara. Jika ini terjadi pada Anda, dan jika Anda tahu bahwa Anda akan berada di tempat di mana pertengkaran verbal sedang berlangsung, di tempat seperti pengadilan, maka Anda dapat datang bersama pengacara Anda. Pengacara akan membela Anda dan menghentikan serangan verbal, dan akan melakukannya dengan cara yang sepenuhnya legal, dan ruang Zen yang hilang akan kembali kepada Anda. Misalnya, saya harus menelepon keluarga saya beberapa kali, hakimnya cukup ramah dalam hal ini, tetapi pengacara lawan berteriak dan membentak saya, saya bahkan tidak bisa berkata apa-apa. Dalam kasus ini, menggunakan mediator adalah cara terbaik bagi saya. Mediator menghentikan semua tekanan yang mengalir pada Anda secara terus menerus, Anda menemukan ruang Zen yang diperlukan, dan dengan itu kemampuan untuk berbicara kembali. Ini adalah keseluruhan bidang pengetahuan di mana ada banyak hal untuk dipelajari, banyak hal untuk ditemukan dalam diri Anda, dan semua ini berubah menjadi keputusan strategis tingkat tinggi yang berbeda untuk orang yang berbeda. Beberapa orang tidak mengalami masalah yang dijelaskan di atas; biasanya orang yang merupakan tenaga penjualan profesional tidak mengalami masalah tersebut. Semua orang yang mencari nafkah dengan kata-kata - penyanyi terkenal, penyair, pemimpin agama dan politisi, selalu punya sesuatu untuk dikatakan. Mereka tidak punya masalah seperti itu, tapi saya punya.

Andrew: Itu... tidak terduga. Bagus, kita sudah berbicara banyak dan inilah waktunya mengakhiri wawancara ini. Kami pasti akan bertemu di konferensi dan dapat melanjutkan dialog ini. Sampai jumpa di Hydra!

Anda dapat melanjutkan percakapan Anda dengan Cliff di konferensi Hydra 2019 yang akan diadakan pada 11-12 Juli 2019 di St. Dia akan datang membawa laporan "Pengalaman Memori Transaksional Perangkat Keras Azul". Tiket dapat dibeli di situs web resmi.

Sumber: www.habr.com

Tambah komentar