2 poin oleh GN⁺ 2024-05-13 | 1 komentar | Bagikan ke WhatsApp
  • Implementasi alternatif seperti runtime bahasa dan JIT bisa memiliki kinerja yang baik, tetapi adopsinya dapat terbatas karena harus terus mengikuti perubahan implementasi kanonis dan ekspektasi pengguna
  • PyPy, LuaJIT, dan TruffleRuby menunjukkan kinerja eksekusi yang cepat, tetapi kesenjangan kompatibilitas dan beban mengikuti fitur baru menjadi hambatan dalam deployment nyata
  • YJIT memilih jalan untuk masuk ke dalam CRuby alih-alih menjadi implementasi Ruby terpisah, sehingga sejak awal dibuat 100% kompatibel dengan fitur CRuby, dan telah di-deploy di Shopify, Discourse, GitHub, dan lainnya
  • Pilihan seperti Crystal, yang sangat mirip dengan bahasa yang sudah ada tetapi tidak kompatibel dengannya, dapat membuat pengguna terus berhadapan dengan perbedaan “hampir Ruby, tetapi bukan Ruby”
  • Di area seperti parser JSON atau JavaScript, tempat standar publik terpisah dari implementasi, beban implementasi alternatif berkurang; tetapi dalam ekosistem yang implementasi kanonisnya secara de facto menjadi standar, diperlukan strategi lain

Jebakan Implementasi Alternatif yang Berulang

  • Di dunia perangkat lunak, berulang kali terjadi proyek yang berawal sebagai implementasi alternatif yang lebih baik dari sistem yang sudah ada akhirnya terjebak di bawah bayang-bayang implementasi kanonis
  • Implementasi alternatif dibandingkan dengan implementasi kanonis (canonical implementation) yang diterima seperti standar dalam hal fitur, kinerja, ekosistem, dan ekspektasi pengguna
  • Jika implementasi kanonis terus berubah, implementasi alternatif harus menghabiskan banyak energi untuk mengikuti perubahan itu alih-alih menentukan arahnya sendiri
  • Dalam kasus bahasa yang secara tradisional berupa interpreter lalu diberi implementasi JIT, fitur baru cenderung lebih cepat masuk ke interpreter, sehingga beban pelacakan di sisi JIT menjadi lebih besar

Pola yang Terlihat pada PyPy, LuaJIT, dan TruffleRuby

  • PyPy adalah compiler JIT tingkat lanjut untuk Python dan dapat memberikan peningkatan kecepatan besar dibanding CPython, tetapi penggunaan nyatanya sangat sedikit
    • Python adalah target bergerak yang secara rutin menghadirkan versi dan fitur CPython baru
    • PyPy kesulitan mengikutinya, sehingga selalu tertinggal beberapa versi Python
    • Agar perangkat lunak Python kompatibel dengan PyPy, fitur Python yang dapat digunakan menjadi terbatas, dan sebagian besar programmer Python tidak ingin memikirkan hal itu
  • LuaJIT memberikan peningkatan kinerja besar dibanding implementasi Lua berbasis interpreter default, serta memperoleh reputasi tinggi dan sejumlah adopsi nyata
    • Pembuat LuaJIT, Mike Pall, dinilai banyak orang sebagai programmer yang luar biasa
    • Karena bahasa Lua terus menambahkan fitur baru, LuaJIT juga tertinggal beberapa versi
    • Sebagian pengguna Lua enggan memakai LuaJIT karena hal ini
    • Lua dikenal sebagai bahasa yang minimalis, tetapi tidak ada upaya untuk memperlambat penambahan fitur baru atau berkoordinasi dengan Mike Pall
  • TruffleRuby mengedepankan angka kinerja paling mengesankan di antara JIT Ruby, tetapi deployment-nya terbatas
    • Salah satu alasan praktisnya adalah waktu warm-up TruffleRuby jauh lebih lama dibanding CRuby
    • Ketika CRuby terus menambahkan fitur, para kontributor TruffleRuby harus berupaya mengikutinya
    • Pengguna Ruby melihat CRuby sebagai implementasi kanonis, dan menganggap implementasi yang tidak sepenuhnya kompatibel kurang layak dipertimbangkan

Jalan Berbeda yang Dipilih YJIT

  • YJIT dimulai sebagai satu lagi JIT Ruby, tetapi dibangun di dalam CRuby itu sendiri, bukan sebagai implementasi terpisah
  • Pilihan ini menimbulkan berbagai trade-off desain, tetapi memungkinkan YJIT sejak awal kompatibel 100% dengan semua fitur CRuby
  • Saat ini YJIT adalah JIT “resmi” Ruby dan telah di-deploy di Shopify, Discourse, GitHub, dan lainnya
  • Pengguna yang mengunjungi github.com atau toko Shopify pada dasarnya telah berinteraksi dengan YJIT
  • Sejauh ini, di antara compiler JIT Ruby, YJIT meraih keberhasilan terbesar, dan kompatibilitas memainkan peran kunci dalam keberhasilan itu

“Jika Tidak Bisa Mengalahkan, Bergabunglah” Saja Tidak Cukup

  • Jika memosisikan diri sebagai implementasi alternatif, besar kemungkinan Anda akan terus memainkan permainan mengejar ketertinggalan di bawah bayang-bayang implementasi kanonis
  • Jika proyek kanonis terus berevolusi, implementasi alternatif harus mengikuti dengan hak pengambilan keputusan yang terbatas atas arah proyeknya sendiri
  • Bergabung dengan implementasi kanonis bisa menghasilkan hasil yang lebih baik, tetapi itu saja tidak menyelesaikan semua kasus
  • Crystal dalam ekosistem Ruby adalah bahasa yang dikompilasi secara statis dengan sintaks mirip Ruby dan menggunakan inferensi tipe
    • Crystal sengaja tidak menargetkan kompatibilitas Ruby dan bercabang dari Ruby
    • Bagi Rubyist, Crystal tampak sebagai bahasa “hampir Ruby tetapi tidak sepenuhnya Ruby”, dan dalam praktiknya ada banyak perbedaan halus serta inkompatibilitas
    • Kemiripan ini menggoyahkan ekspektasi pengguna dan menimbulkan kebingungan
    • Crystal mungkin mendapatkan hasil yang lebih baik jika sejak awal tidak dipasarkan sebagai sesuatu yang mirip Ruby

Pilihan untuk Menghindari Persaingan dan Memiliki Arah Sendiri

  • Ungkapan Peter Thiel “competition is for losers” digunakan dalam konteks jangan menempatkan diri sendiri pada posisi yang mengharuskan bersaing secara tidak perlu
  • Jika ingin membuat bahasa pemrograman baru, nasihatnya adalah sebaiknya tidak mencoba membuat subset Python atau sesuatu yang secara permukaan sangat dekat dengan bahasa yang sudah ada
  • Jika membuat sesuatu milik sendiri, Anda dapat mengembangkan sistem dengan tempo dan arah sendiri tanpa terikat ekspektasi untuk menyamai kinerja, kumpulan fitur, dan ekosistem library implementasi lain
  • Nasihat ini berlaku pada situasi ketika sebuah bahasa atau sistem memiliki implementasi kanonis
  • Area dengan standar publik bisa menjadi pengecualian
    • Parser JSON memiliki spesifikasi yang jelas, relatif kecil, dan tidak cepat berubah, sehingga memungkinkan dibuat implementasi sendiri
    • JavaScript memiliki beberapa implementasi berbasis browser, dan ini dimungkinkan karena ada badan standar eksternal yang mengelola spesifikasi JS
    • Para pengelola standar JS memahami bahwa implementasi kompilasi JIT penting bagi kinerja, dan mengarahkan evolusi bahasa sesuai hal itu
    • Mereka tidak sedang memainkan permainan menambahkan banyak fitur baru secepat mungkin

1 komentar

 
GN⁺ 2024-05-13
Komentar Hacker News
  • Ada satu poin penting lain yang terlewat oleh OP. Saat membuat implementasi alternatif, biasanya arsitekturnya berbeda dari implementasi acuan, dan sesuatu yang mudah di implementasi acuan bisa menjadi sangat sulit di implementasi kita
    Misalnya ada perangkat lunak proprietari untuk laporan keuangan yang menyimpan dokumen dalam format biner yang aneh. Saat membuat alternatif gratis, kita memilih struktur yang membaca seluruh dokumen ke memori, lalu saat menyimpan menulis ulang seluruh berkas. Namun bisa saja aslinya dibuat pada masa RAM masih kecil, sehingga hanya membaca dan menulis bagian yang sedang dikerjakan pengguna, bahkan dapat memodifikasi di tempat
    Kemudian ketika produk asli menambahkan fitur untuk memasukkan lampiran ke dokumen, berkas besar seperti rekaman telepon investor atau PDF pindaian ratusan halaman tetap berjalan baik berkat pemuatan per bagian. Sebaliknya implementasi kita mendeserialisasi seluruh dokumen, sehingga bermasalah begitu ukuran dokumen melebihi RAM pengguna, dan perubahan yang di produk asli bisa diselesaikan satu pengembang dalam seminggu mungkin memaksa kita mendesain ulang seluruh perangkat lunak

    • Saya bekerja di industri yang membutuhkan dua implementasi independen untuk setiap komponen perangkat lunak inti dan pernah terlibat dalam beberapa reimplementasi. Saya memang pernah melihat fitur yang jelas dilakukan oleh implementasi asli tetapi kurang cocok dengan arsitektur kami. Meski begitu, belum pernah ada fitur yang membuat kami harus mendesain ulang program; biasanya hanya fitur yang sepele di satu sisi tetapi agak lebih banyak pekerjaan di sisi lain
      Sebaliknya, ada juga fitur yang lebih sulit diimplementasikan pada versi asli tetapi menjadi sepele pada implementasi kedua. Desain ulang besar hanya terjadi sekali, dan itu pun pada implementasi asli yang memiliki asumsi yang menyebabkan ledakan eksponensial
      Contoh publiknya adalah masalah menjalankan aplikasi Windows di Linux. Kernel Linux adalah implementasi yang sama sekali berbeda dari NT dan juga tidak bertujuan kompatibel, tetapi menjalankan aplikasi Windows tidak membutuhkan desain ulang seluruh kernel. Cukup beberapa fitur kernel yang relatif umum dan lapisan kompatibilitas di ruang pengguna. Wine membutuhkan banyak upaya untuk ditulis dan dipelihara, tetapi jauh lebih sedikit daripada implementasi Windows itu sendiri, dan berjalan di atas platform yang awalnya tidak menargetkan kompatibilitas Windows. Namun seperti yang disebutkan dalam tulisan, ia harus terus mengejar Windows, dan bahkan muncul kode yang bergantung pada bug di implementasi acuan, sehingga untuk kompatibel sampai ke bug, kita harus terlebih dahulu mencari tahu bug mana yang perlu diimplementasikan
    • Sepertinya penulis sudah membahas poin ini pada bagian tentang perbedaan tingkat kesulitan menerapkan fitur baru di bahasa interpreter dan bahasa terkompilasi
    • Benar. Ini adalah masalah klasik dalam membuat Python menjadi cepat. CPython dimulai sebagai interpreter sederhana yang mengikuti apa pun yang diperintahkan kode tanpa optimisasi; semuanya adalah dictionary dan hampir tidak ada konkurensi nyata
      Karena itu, kode apa pun bisa memperbaiki hal lain saat sedang berjalan. Dalam praktiknya hal itu tidak sering dipakai, tetapi kalau mau dihapus, orang-orang akan ribut. Implementasi yang benar-benar mengompilasi Python harus menangani situasi ketika suatu thread tiba-tiba mengubah sesuatu di bawah thread lain
    • Dari sudut pandang dilema inovator, implementasi referensi bisa saja milik pesaing. Jika sekarang kita sudah lebih memahami pasar dan memilih arsitektur yang lebih baik, kita bisa berada pada posisi untuk menambahkan fitur baru dengan lebih murah dan lebih cepat daripada pesaing
      Ini salah satu cara perusahaan kecil menambah beban perusahaan besar, dan perusahaan yang tidak terlalu membiarkan utang teknis menumpuk juga bisa melakukannya. Ini juga salah satu momen langka untuk memperlihatkan utang teknis secara jelas kepada manajemen, karena kita bisa berkata, “Kita butuh waktu lebih lama daripada Acme untuk mengimplementasikan fitur ini”
    • Perbedaan arsitektur seperti ini kadang memang disengaja. Misalnya, versi GNU dari banyak utilitas dasar Unix memilih kompromi yang sama sekali berbeda dari versi Unix asli dan BSD untuk menghindari tuduhan pelanggaran hak cipta
  • Saya setuju dengan argumen “jangan mencoba membuat subset Python”. Proyek yang dipasarkan sebagai “Python, tetapi X lebih baik” selalu sulit bersaing dengan implementasi acuan, terutama jika X adalah kecepatan. Orang yang memakai bahasa bertipe dinamis pada akhirnya sering kali tidak terlalu peduli dengan kecepatan eksekusi
    Namun implementasi alternatif tidak selalu gagal. MicroPython tampaknya cukup sukses meski hampir tidak mendukung lebih dari level Python 3.4. Itu karena ia dirancang untuk berjalan di mikrokontroler, sehingga bukan bersaing dengan CPython, melainkan dengan lingkungan pemrograman mikrokontroler lain
    Meski begitu, para pemelihara MicroPython sepertinya banyak menerima permintaan fitur Python yang lebih baru. Dulu saya pernah mempertimbangkan implementasi alternatif Python yang ringan dan berfokus pada embedding untuk disematkan di dalam aplikasi; dalam kasus ini pun idenya bersaing dengan Lua, bukan CPython. Tetapi permintaan fitur nomor satunya adalah “Apakah mendukung NumPy?”

    • Menariknya, MicroPython kini juga mendapat perhatian dan permintaan untuk penggunaan web. Ini berkat upaya hebat PyScript. Namun berbeda dari mikrokontroler dengan RAM total 1MB, di web orang berharap kode yang sudah ada bisa berjalan dan mendapat pengalaman serta kompatibilitas CPython yang lengkap, jadi ini jauh lebih sulit
      Pada saat yang sama, tampaknya di sisi CPython juga ada pekerjaan agar lebih baik berjalan di frontend, dan titik sakit utamanya adalah ukuran paket
  • Saat membangun startup, saya mempelajari hal serupa. Jika mengulang lagi, saya akan secara aktif menghindari fitur tiket masuk dasar di bidang kami
    Sebagai gantinya, kami seharusnya hanya membuat minimum yang cukup untuk memberi keyakinan bahwa arsitektur kami dapat mendukung kebutuhan ala enterprise, lalu memusatkan semuanya pada pembeda yang bisa memancing reaksi “oh, saya bisa melihat ini akan berkembang sampai ke mana”. Bukan fitur yang hanya memicu reaksi seperti “ini cuma kloning X”

    • Saya mengerti intinya, tetapi istilah “table stakes” dipakai keliru. Menurut definisi, itu adalah hal-hal yang wajib dipertaruhkan agar bisa ikut bermain
      Strategi yang dimaksud lebih dekat ke mengimplementasikan fitur wajib dasar, tetapi tidak terlalu mendalami fitur perluasan “umum” setelah itu. Caranya adalah membuat orang menghubungi kembali karena fitur yang menarik, sambil tetap memiliki fungsi dasar yang cukup agar tidak gugur karena tidak memenuhi syarat wajib
    • Jika saya memahaminya dengan benar, maksudnya adalah mengimplementasikan minimum yang dibutuhkan pelanggan korporat, yaitu hanya yang diperlukan agar pimpinan mereka menyetujui transaksi, lalu melupakan sisanya dan berfokus pada sesuatu yang sangat baru yang membedakan dari pesaing?
    • Mirip ketika iPhone pertama kali keluar tanpa salin-tempel dan dengan performa nirkabel GSM yang sangat mengecewakan
  • Rasanya mirip untuk semua kode wrapper. Ada kalanya seseorang berkata, “Kita butuh versi internal dari API ini”
    Alasannya beragam, tetapi biasanya semacam “kita tidak bisa percaya orang akan memakai API resmi dengan benar.” Bisa saja begitu, tetapi versi internal biasanya kurang standar dan dokumentasinya lebih buruk
    Kadang alasannya adalah “kita butuh fitur tambahan,” tetapi kalau begitu tidak perlu membungkus seluruh API; cukup tambahkan 3 fungsi saja. Seiring waktu, 99% codebase bisa berubah menjadi polyfill
    Intinya, jika tidak memakai default, itu akan sangat menyusahkan orang yang mewarisi codebase tersebut nanti

    • Menurut saya, wrapper seperti itu akan terbantu jika punya antarmuka untuk berinteraksi langsung dengan library di bawahnya
      Misalnya, library ziggy-pydust untuk menulis modul native Python dengan Zig jelas lebih enak dilihat daripada import Python.h biasa. Meski begitu, ia juga punya .ffi untuk mengakses langsung fungsi yang belum diimplementasikan tetapi ada di Python.h
      Tanpa opsi seperti ini, biasanya orang akan meninggalkan library semacam itu dan lebih memilih yang asli. Namun, ini pun dalam arti tertentu adalah wrapper, yaitu modul native, dan demi kecepatan pengembangan kadang memakai ctypes secara langsung bisa lebih baik
  • Tulisan yang bagus dan banyak pelajaran hebat, tetapi ada satu bahan inti yang hilang. Ini mirip dengan alternatif pesaing untuk suatu produk
    Seperti mengatakan Amazon gagal karena tidak punya toko buku fisik yang sudah akrab bagi orang-orang, padahal kenyataannya tidak begitu
    Alasan alternatif JIT seperti ini gagal dan terus hanya mengejar adalah karena, secara praktis, sebagian besar developer bahasa X tidak terlalu menganggap JIT penting. Lebih tepatnya, mereka menganggap fitur bahasa dan interoperabilitas lebih penting daripada JIT
    Karena itu produk yang “bergabung” alih-alih bersaing yang menang. Sebab alternatif itu tidak bisa menawarkan stabilitas atau interoperabilitas yang lebih tinggi

  • Saya sudah lama berkutat dengan bahasa dan compiler, dan tulisan ini sangat menarik. Jika ide yang sama dikatakan dengan cara lain, bahasa jauh lebih dari sekadar kecepatan kompilasi
    Kecepatan kompilasi sangat penting, dan jelas masuk 10 dimensi teratas. Terutama karena meningkatkan kecepatan kompilasi mempercepat loop umpan balik developer, dan membuat tim inti bisa memperbaiki semua dimensi lain dengan lebih cepat
    Meski begitu, masih ada lebih dari 30 dimensi lain yang sangat penting dalam bahasa pemrograman

    • Tulisan itu tampaknya tentang kecepatan eksekusi. Namun bahkan di sana, melihat popularitas CPython, kecepatan eksekusi jelas bukan faktor nomor satu
  • Kesimpulannya tentang cara proyek seperti ini bisa sukses bagus, tetapi masih ada faktor yang kurang dibahas tentang mengapa banyak proyek tidak naik daun. Kompatibilitas implementasi pengganti sering kali sebenarnya lebih rendah daripada klaimnya, bahkan untuk fitur bahasa yang sudah lama
    Misalnya, aplikasi Ruby dan Python sangat sering punya ekstensi C native di suatu tempat dalam dependensinya, dan setahu saya implementasi pengganti utama tidak pernah mendukung ini. Ada upaya, tetapi tidak berjalan baik karena alasan teknis yang jelas, dan alternatif yang mengharapkan library menyediakan beberapa implementasi juga punya sejarah yang tidak mulus
    Ditambah fakta bahwa bahasa-bahasa ini sering dipakai untuk situs web CRUD, tempat I/O menjadi faktor performa yang lebih besar daripada CPU, daya tarik alternatif yang lebih cepat jadi jauh berkurang

  • Tulisan yang benar-benar bagus. Sosiologi teknologi sangat menarik
    Maintainer implementasi bahasa menginginkan fleksibilitas maksimum saat merancang dan merilis fitur baru yang bermanfaat bagi pengguna. Mereka tidak ingin terhambat karena harus mendapat konsensus dari beberapa implementasi sebelum merilis fitur. Contohnya bisa dilihat dari betapa lambatnya perkembangan JavaScript selama bertahun-tahun, dan betapa cepatnya TypeScript berevolusi secara relatif
    Pada saat yang sama, implementasi pengganti bisa menjadi tanda bahwa ekosistem bahasa itu kuat, jadi ada juga kelebihannya. Jika implementasi pengganti benar-benar bagus, ia bisa menambah nilai nyata bagi ekosistem meski hanya berguna untuk sebagian pengguna dengan kebutuhan niche
    Jadi jika Anda perancang bahasa atau maintainer, Anda mungkin tidak akan secara aktif memusuhi implementasi pengganti, tetapi ada sisi negatifnya. Secara umum, umpan balik dari pengguna akan mengarah pada permintaan untuk merilis fitur baru dan mengembangkan bahasa. Tidak akan banyak permintaan untuk memperlambat laju agar PyPy, IronRuby, LuaJIT, dan lain-lain bisa mengejar
    Saat konsumen bahasa memilih implementasi mana yang akan dibangun di atasnya, prioritas terbesar biasanya keselamatan dan stabilitas. Tidak ada orang yang ingin codebase sejuta baris bergantung pada karakteristik perilaku halus dari implementasi pengganti yang dulu dibuat oleh mahasiswa doktoral yang sangat pintar tetapi kini sudah pindah ke proyek lain. Karena itu pengguna berkumpul ke implementasi yang paling banyak dipakai, dan fakta itu kembali menarik pengguna lain, menciptakan loop umpan balik positif yang kuat
    Akibatnya, jika tidak ada kekuatan besar yang mendorong ke arah sebaliknya, sebagian besar bahasa akan berkumpul pada satu implementasi acuan. Bisa juga dikatakan ini hal yang baik. Hampir semua upaya engineering yang dicurahkan untuk implementasi bahasa tidak terpecah ke beberapa implementasi, melainkan menguntungkan semua pengguna. Tentu sisi negatifnya adalah implementasi bisa terjebak di optimum lokal

  • LuaJIT disebutkan, tetapi itu juga contoh bahwa hasilnya tidak selalu seperti kesimpulan tulisan tersebut. Banyak orang dan proyek secara sengaja memilih LuaJIT dibanding Lua

    • Saya datang ke komentar untuk mengatakan ini. LuaJIT dipakai dengan antusias di komunitas TeX, dan executable luajittex juga mudah didapat berdampingan dengan luatex
  • Ini mungkin bukan pendapat yang populer, tetapi orang-orang kadang perlu memeriksa ego mereka, termasuk diri sendiri. Mungkin lebih mudah membuat proyek paralel yang “milik sendiri” daripada berkontribusi ke proyek open source yang sudah ada, tetapi kita perlu bertanya: untuk siapa ini dilakukan?
    Apakah untuk maintainer proyek, untuk proyek itu sendiri, untuk para pengguna, atau untuk ego diri sendiri? Jika poin terakhir membuat marah, kemungkinan besar itu memang relevan.
    Menambahkan JIT ke bahasa yang sudah ada adalah pekerjaan besar, jadi standar yang diterima oleh implementasi acuan juga pasti tinggi. Meski begitu, menurut saya tujuan seharusnya tetap ke arah sana. Melakukan fork atau membuat yang baru juga memberi kebebasan untuk mengerjakan hal besar, tetapi sering kali sebaiknya dianggap sebagai sesuatu yang sementara.
    Jika tujuannya adalah menunjukkan apa yang bisa saya lakukan, kemungkinan besar itu tidak akan berjalan jauh. Jika tujuannya membuat sesuatu yang lebih baik, kita akan belajar bekerja di dalam batasan orang lain.

    • Anehnya, saya berpikir sebaliknya. Justru orang-orang yang berkhotbah bahwa harus ada satu proyek saja, hanya berkontribusi ke proyek acuan, dan tidak boleh ada implementasi alternatif, merekalah yang perlu memeriksa ego mereka serta belajar tentang keberadaan berbagai pendekatan dan mengapa kompetisi lebih baik daripada monopoli.
      Menurut saya tulisan ini menunjukkan bagaimana Python, Lua, dan Ruby memilih pendekatan seperti itu dan mengecewakan banyak orang. Akibatnya, ribuan developer dan jutaan pengguna harus menerima pengembangan dan software yang lebih lambat. Bukan karena itu mustahil, melainkan karena secara administratif tidak ada insentif untuk melakukannya.
    • Hmm, ada juga kasus ketika implementasi resmi benar-benar terkutuk, sehingga kita perlu membuat kebun kecil sendiri. Bahkan jika itu tumbuh menjadi 20 acre dengan rencana rotasi tanaman, kadang itu lebih baik daripada gunung berapi.