- Penulis mencoba pendekatan baru saat mempelajari bahasa Zig sambil mengerjakan proyek penulisan ulang indeks AcoustID, dipicu oleh keterbatasan dalam pemrograman jaringan
- Untuk menghadirkan model I/O asinkron dan konkurensi yang sebelumnya digunakan di C++ dan Go ke Zig, ia memutuskan mengembangkan pustakanya sendiri
- Hasilnya, ia membuat pustaka Zio yang mengimplementasikan model konkurensi bergaya Go agar sesuai dengan Zig, sehingga memungkinkan penulisan kode asinkron yang terlihat seperti sinkron tanpa callback
- Zio mendukung I/O jaringan dan berkas asinkron, channel, primitive sinkronisasi, pemantauan sinyal dan lainnya, serta menunjukkan performa lebih cepat daripada Go atau Tokio milik Rust dalam mode single-thread
- Proyek ini menunjukkan kemungkinan menggabungkan performa tingkat sistem Zig dengan model konkurensi modern, dan dinilai sebagai titik balik penting dalam perluasan ekosistem Zig
Bahasa Zig dan motivasi awal
- Penulis sebelumnya telah mengamati Zig, yang awalnya dirancang sebagai bahasa tingkat rendah untuk perangkat lunak audio, tetapi belum merasa ada kebutuhan praktis untuk menggunakannya
- Ia mulai tertarik setelah melihat Andrew Kelley, pencipta Zig, mengimplementasikan ulang algoritme Chromaprint milik penulis dalam Zig
- Ia menjadikan proyek penulisan ulang inverted index di AcoustID sebagai kesempatan untuk belajar Zig, dan pada akhirnya berhasil mencapai implementasi yang lebih cepat dan lebih skalabel dibanding versi C++
- Namun, pada tahap penambahan antarmuka server, ia menghadapi masalah kurangnya dukungan jaringan asinkron
Pendekatan sebelumnya dan keterbatasannya
- Pada versi C++ sebelumnya, framework Qt digunakan untuk menangani I/O asinkron; walaupun berbasis callback, pendekatan ini tetap dapat dipakai berkat dukungannya yang kaya
- Dalam prototipe berikutnya, ia memanfaatkan kemudahan jaringan dan konkurensi dari bahasa Go, tetapi Zig tidak memiliki abstraksi pada tingkat yang setara
- Untuk mengimplementasikan server TCP dan lapisan klaster di Zig, muncul inefisiensi karena harus membuat banyak thread
- Untuk mengatasinya, ia menulis sendiri klien Zig untuk sistem pesan NATS (
nats.zig) sambil mendalami fitur jaringan di Zig
- Untuk mengatasinya, ia menulis sendiri klien Zig untuk sistem pesan NATS (
Munculnya pustaka Zio
- Berdasarkan pengalaman tersebut, ia merilis Zio: pustaka I/O asinkron dan konkurensi untuk Zig
- Zio bertujuan memungkinkan penulisan kode asinkron tanpa callback, dengan struktur yang secara internal bekerja secara asinkron tetapi dari luar terlihat seperti sinkron
- Zio mengimplementasikan model konkurensi bergaya Go secara terbatas agar sesuai dengan Zig
- Task di Zio berbentuk stackful coroutine dengan stack berukuran tetap
- Saat
stream.read()dipanggil, operasi I/O dijalankan di latar belakang, lalu task dilanjutkan kembali setelah selesai untuk mengembalikan hasil
- Pendekatan ini sekaligus memberikan penyederhanaan pengelolaan state dan peningkatan keterbacaan kode
Susunan fitur dan struktur runtime
- Zio mendukung I/O jaringan dan berkas asinkron penuh, primitive sinkronisasi (mutex, condition variable, dll.), channel bergaya Go, pemantauan sinyal OS, dan lainnya
- Task dapat dijalankan dalam mode single-thread maupun multi-thread
- Dalam mode multi-thread, task dapat berpindah antar-thread, sehingga memberikan efek penurunan latensi dan peningkatan penyeimbangan beban
- Zio mengimplementasikan antarmuka Reader/Writer standar untuk memastikan kompatibilitas dengan pustaka eksternal
Performa dan perbandingan
- Penulis belum merilis benchmark resmi, tetapi menyebut telah mengonfirmasi performa yang lebih cepat daripada Go dan Tokio milik Rust dalam mode single-thread
- Biaya context switching turun hingga setara pemanggilan fungsi, sehingga kecepatan perpindahannya nyaris gratis
- Mode multi-thread masih belum sekuat Go/Tokio, tetapi menunjukkan performa serupa atau sedikit lebih cepat
- Di masa depan, penambahan fitur fairness dapat membuat performanya sedikit menurun
Contoh kode dan pemanfaatan
- Dokumentasi mencakup contoh kode server HTTP berbasis Zio
- Menggunakan
zio.net.Streamuntuk menerima koneksi dan menangani tiap koneksi sebagai task terpisah zio.Runtimemengelola eksekusi task dan penjadwalan I/O
- Menggunakan
- Struktur ini memungkinkan penulisan I/O asinkron seperti kode sinkron, sekaligus menghadirkan kontrol alur yang jelas dan pengelolaan pelepasan sumber daya
Rencana ke depan dan makna proyek
- Melalui Zio, penulis menilai bahwa Zig dapat berkembang melampaui sekadar bahasa untuk kode sistem berperforma tinggi, menjadi bahasa untuk pengembangan aplikasi jaringan yang lengkap
- Sebagai langkah berikutnya, ia berencana menulis ulang klien NATS berbasis Zio dan mengembangkan pustaka klien/server HTTP berbasis Zio
- Proyek ini dinilai mendorong perluasan infrastruktur jaringan dan konkurensi dalam ekosistem Zig, serta sebagai upaya membangun model runtime modern yang sebanding dengan Go atau Rust
1 komentar
Komentar Hacker News
Tidak jelas apakah desain async Zig memakai pasangan call/return dari hardware, atau diterjemahkan berbasis lompatan tidak langsung
Untuk membuat benchmark yang benar-benar sempurna, perlu membandingkan total waktu eksekusi antara program yang terus-menerus berpindah di antara dua tugas dan program yang sepenuhnya sinkron. Ini pekerjaan yang cukup sulit
Jika bisa mengendalikan compiler, call/ret pada kode I/O juga bisa diubah menjadi lompatan eksplisit
Dalam jangka panjang, saya berharap CPU mengadopsi meta-predictor agar bisa lebih baik memprediksi coroutine stackful
Tulisan terkait: Zig new async I/O
Saya memakai Zig di lingkungan embedded (ARM Cortex-M4, RAM 256KB), dan menggunakannya untuk memastikan keamanan memori saat interoperasi dengan C
Saya lebih menyukai async berwarna seperti di Rust. Rasanya menyenangkan karena tampak seperti kode sinkron, tetapi di codebase besar masalahnya adalah makin sulit membedakan fungsi mana yang blocking
CPU tidak benar-benar terblokir saat I/O, dan thread OS sendiri adalah coroutine stackful yang diimplementasikan OS
Di level bahasa, kita hanya bisa mewujudkan ilusi ini dengan lebih efisien, tetapi hakikatnya sama
Warna suatu fungsi ditentukan oleh apakah ia melakukan I/O, dan saat pemanggilan status async-nya dinyatakan secara eksplisit
Zig juga menargetkan kemampuan menghitung ukuran stack yang dibutuhkan saat pemanggilan fungsi, sehingga diharapkan dapat mengurangi masalah pemborosan RAM pada coroutine stackful
Namun proyeknya tetap sangat aktif, dan saya menilai positif bahwa mereka memprioritaskan desain yang benar dibanding rilis cepat
Saat ini saya memakai Go atau C sambil menunggu 1.0
Saya juga akan menunggu 0.16 untuk pekerjaan yang berfokus pada I/O
Kode lama tetap berjalan, dan API baru lebih ergonomis serta lebih cepat
Saya juga memindahkan proyek lama saya ke API Reader/Writer baru, dan kodenya jadi jauh lebih rapi
Pendekatan seperti libtask terlihat jauh lebih bersih
Rust juga mengadopsi async berbasis callback, tetapi saya tidak begitu paham alasannya
Referensi: libtask
Namun jika stack ditangani langsung, ini bisa berbenturan dengan penanganan eksepsi, GC, debugger, dan sebagainya
Karena sulit juga menggabungkan perubahan seperti ini di level LLVM, dari sudut pandang perancang bahasa ada banyak batasan praktis
Terlalu kecil akan overflow, terlalu besar memboroskan memori
Ukuran stack yang dibutuhkan juga berbeda di tiap platform, sehingga muncul masalah portabilitas
Jika isu Zig #157 terselesaikan, pendekatan ini tampaknya akan menjadi lebih baik
Artinya, ada tiga cara mengimplementasikan async
Rust diubah menjadi state machine statis lalu runtime melakukan polling
Stackful sangat boros memori, dan pengelolaan ukuran stack-nya sulit
Untuk menghindari itu, Rust memilih struktur stackless, dan Zig nantinya akan memungkinkan memilih kedua pendekatan tersebut
Referensi: kode coroutine zio
setsockoptZig menyediakan lapisan API POSIX
Referensi: dokumentasi setsockopt
Saya sedang membayangkan struktur yang bekerja seperti
asyncio.timeoutdi PythonContoh kode:
Padahal justru itulah bagian yang paling sulit
Referensi: zio.dev
Namun Zig tetap mengesankan karena meskipun bahasa level rendah, ia bisa mengekspresikan API level tinggi dengan rapi
Baik Zig maupun Go sama-sama baru mendapatkan binding Qt
Saya ingin binding untuk Rust. cxx-qt adalah satu-satunya proyek yang masih dipelihara, tetapi saya tidak ingin memakai QML atau CMake. Saya ingin memakai Qt hanya dengan Rust + Cargo