- लेखक ने Zig भाषा सीखते हुए AcoustID index rewrite प्रोजेक्ट पर काम किया और नेटवर्क प्रोग्रामिंग की सीमाओं के कारण एक नया approach आज़माया
- C++ और Go में इस्तेमाल किए गए asynchronous I/O और concurrency model को Zig में भी लागू करने के लिए उन्होंने अपनी खुद की library बनाने का फैसला किया
- इसके परिणामस्वरूप, Zig के लिए Go-style concurrency model को अनुकूलित करके बनाई गई Zio library तैयार हुई, जिससे callback के बिना synchronous जैसा दिखने वाला asynchronous code लिखा जा सकता है
- Zio asynchronous network·file I/O, channels, synchronization primitives, signal monitoring आदि को support करती है, और single-thread mode में Go या Rust के Tokio से तेज़ performance दिखाती है
- यह प्रोजेक्ट Zig के system-level performance और modern concurrency model के संयोजन की संभावना को दिखाता है, और Zig ecosystem के विस्तार में एक महत्वपूर्ण मोड़ माना जाता है
Zig भाषा और शुरुआती प्रेरणा
- लेखक पहले से Zig पर नज़र रखे हुए थे, जिसे मूल रूप से audio software के लिए low-level language के रूप में डिज़ाइन किया गया था, लेकिन उन्हें इसकी कोई ठोस ज़रूरत महसूस नहीं हुई थी
- Zig के creator Andrew Kelley ने लेखक के Chromaprint algorithm को Zig में reimplement किया था, और इसी से उनकी रुचि बढ़ी
- AcoustID के reverse index rewrite project को उन्होंने Zig सीखने के अवसर के रूप में लिया, और परिणामस्वरूप C++ version की तुलना में ज़्यादा तेज़ और अधिक scalable implementation हासिल की
- लेकिन server interface जोड़ने के चरण में उन्हें asynchronous networking support की कमी की समस्या का सामना करना पड़ा
मौजूदा approach और सीमाएँ
- पहले के C++ version में Qt framework का उपयोग करके asynchronous I/O संभाला जाता था; यह callback-based था, लेकिन समृद्ध support के कारण उपयोगी था
- बाद के prototype में Go भाषा की networking और concurrency की सुविधा का लाभ उठाया गया, लेकिन Zig में उसी स्तर का abstraction मौजूद नहीं था
- Zig में TCP server और cluster layer लागू करने के लिए कई thread बनाने पड़ते, जो अप्रभावी था
- इसे हल करने के लिए उन्होंने NATS messaging system के लिए Zig client (nats.zig) खुद लिखा और Zig की networking capabilities का गहराई से अध्ययन किया
Zio library का आगमन
- इन अनुभवों के आधार पर उन्होंने Zio: Zig के लिए asynchronous I/O और concurrency library जारी की
- Zio का लक्ष्य callback के बिना asynchronous code लिखना है; अंदरूनी रूप से asynchronous I/O चलता है, लेकिन बाहर से इसकी संरचना synchronous जैसी दिखती है
- इसमें Go-style concurrency model को Zig के अनुरूप सीमित रूप में लागू किया गया है
- Zio के task fixed-size stack वाले stackful coroutine के रूप में होते हैं
stream.read() call पर I/O काम background में चलता है, और पूरा होने पर task फिर से resume होकर result लौटाता है
- यह तरीका state management को सरल बनाता है और code readability भी बेहतर करता है
फीचर संरचना और runtime architecture
- Zio पूर्ण asynchronous network और file I/O, synchronization primitives (mutex, condition variable आदि), Go-style channels, OS signal monitoring आदि को support करती है
- Task को single-thread या multi-thread mode में चलाया जा सकता है
- multi-thread mode में task thread के बीच move हो सकते हैं, जिससे latency कम होती है और load balancing बेहतर होती है
- यह standard Reader/Writer interface लागू करती है, जिससे external libraries के साथ compatibility सुनिश्चित होती है
performance और तुलना
- लेखक ने अभी तक कोई official benchmark जारी नहीं किया है, लेकिन उनका कहना है कि single-thread mode में performance Go और Rust के Tokio से तेज़ है
- context switching की लागत function call के स्तर तक कम है, यानी switching speed लगभग मुफ्त जैसी है
- multi-thread mode अभी Go/Tokio जितना robust नहीं है, लेकिन मिलती-जुलती या थोड़ी बेहतर performance दिखाता है
- भविष्य में fairness feature जुड़ने पर performance कुछ कम हो सकती है
उदाहरण code और उपयोग
- दस्तावेज़ में Zio-आधारित HTTP server example code शामिल है
zio.net.Stream का उपयोग करके connection accept किए जाते हैं, और हर connection को अलग task में संभाला जाता है
zio.Runtime task execution और I/O scheduling को manage करता है
- यह संरचना asynchronous I/O को synchronous code की तरह लिखने की सुविधा देती है, और स्पष्ट flow control तथा resource cleanup management को संभव बनाती है
आगे की योजना और महत्व
- लेखक ने Zio के माध्यम से पुष्टि की कि Zig सिर्फ high-performance system code के लिए भाषा नहीं है, बल्कि पूर्ण network application development language के रूप में भी विकसित हो सकती है
- अगले चरण में वे NATS client को Zio-आधारित रूप में फिर से लिखने और Zio-आधारित HTTP client/server library विकसित करने की योजना बना रहे हैं
- यह प्रोजेक्ट Zig ecosystem की networking·concurrency infrastructure के विस्तार को आगे बढ़ाता है, और Go या Rust के समकक्ष modern runtime model बनाने के प्रयास के रूप में देखा जाता है
1 टिप्पणियां
Hacker News राय
यह स्पष्ट नहीं है कि Zig का async डिज़ाइन hardware call/return जोड़ों का उपयोग करता है, या indirect jump आधारित रूप में translate होता है
सही benchmark करने के लिए, दो tasks के बीच लगातार switching वाले program और पूरी तरह synchronous program के कुल execution time की तुलना करनी होगी। यह काफ़ी पेचीदा काम है
अगर compiler को नियंत्रित किया जा सके, तो I/O code के call/ret को explicit jump में बदलना भी संभव है
लंबे समय में, उम्मीद है कि CPU stackful coroutine को बेहतर predict करने के लिए meta-predictor जोड़े
संबंधित लेख: Zig new async I/O
मैं Zig को embedded (ARM Cortex-M4, 256KB RAM) environment में इस्तेमाल कर रहा हूँ, और C के साथ interop में memory safety सुनिश्चित करने के लिए इसका उपयोग करता हूँ
मुझे Rust जैसा colored async ज़्यादा पसंद है। synchronous code जैसा दिखने वाला जादुई एहसास अच्छा लगता है, लेकिन बड़े codebase में यह पहचानना मुश्किल हो जाता है कि कौन-सा function blocking है
CPU वास्तव में I/O पर block नहीं होता, और OS thread खुद OS द्वारा implement किया गया stackful coroutine है
भाषा स्तर पर इस illusion को अधिक कुशलता से implement किया जा सकता है, लेकिन मूल रूप से बात वही है
function I/O करता है या नहीं, उसी आधार पर उसका color तय होगा, और call site पर async होने या न होने को स्पष्ट किया जाएगा
Zig function call के समय आवश्यक stack size की गणना करने की क्षमता भी लक्ष्य बना रहा है, इसलिए stackful coroutine की RAM बर्बादी की समस्या कम होने की उम्मीद है
लेकिन project अभी भी सक्रिय है, और तेज़ रिलीज़ से ज़्यादा सही design को प्राथमिकता देना मुझे सकारात्मक लगता है
अभी मैं Go या C इस्तेमाल कर रहा हूँ और 1.0 का इंतज़ार कर रहा हूँ
मैं भी I/O-केंद्रित काम के लिए 0.16 का इंतज़ार करूँगा
मौजूदा code वैसे ही काम करता रहेगा, और नया API अधिक ergonomic और performant है
मैंने भी अपना मौजूदा project नए Reader/Writer API पर migrate किया, और code काफ़ी साफ़ हो गया
libtask जैसा approach कहीं ज़्यादा साफ़ लगता है
Rust ने भी callback-आधारित async अपनाया, लेकिन वजह समझ नहीं आती
संदर्भ: libtask
लेकिन stack को सीधे संभालने पर exception handling, GC, debugger आदि से टकराव हो सकता है
LLVM स्तर पर ऐसे बदलाव merge करना भी कठिन है, इसलिए भाषा डिज़ाइनर के नज़रिए से व्यावहारिक सीमाएँ बहुत हैं
बहुत छोटा हो तो overflow, बहुत बड़ा हो तो memory waste
platform के अनुसार ज़रूरी stack size अलग होता है, इसलिए portability की समस्या भी होती है
Zig issue #157 हल हो जाए तो यह approach बेहतर हो सकता है
यानी async लागू करने के तीन तरीके हैं
Rust static state machine में बदल जाता है और runtime उसे poll करता है
stackful approach में memory waste ज़्यादा होती है, और stack size manage करना कठिन होता है
Rust इससे बचने के लिए stackless structure अपनाता है, और Zig भविष्य में दोनों विकल्प चुनने देगा
संदर्भ: zio coroutine कोड
setsockoptसे read/write timeout सेट किया जा सकता हैZig POSIX API layer देता है
संदर्भ: setsockopt दस्तावेज़
Python के
asyncio.timeoutजैसा व्यवहार करने वाली संरचना पर विचार चल रहा हैउदाहरण code:
जबकि असल में वही सबसे कठिन हिस्सा है
संदर्भ: zio.dev
लेकिन Zig, low-level language होने के बावजूद high-level API को साफ़-सुथरे ढंग से व्यक्त कर सकता है, यह मुझे प्रभावशाली लगा
Zig और Go, दोनों के लिए नए Qt bindings आए हैं
मुझे Rust के लिए bindings चाहिए। cxx-qt ही एकमात्र maintained project है, लेकिन मैं QML या CMake इस्तेमाल नहीं करना चाहता। मैं केवल Rust + Cargo के साथ Qt इस्तेमाल करना चाहता हूँ