- Vale का region borrowing prototype पहली बार सफलतापूर्वक compile हुआ है, जिससे generational references और regions को मिलाकर memory-safe approach को वास्तविक प्रोग्राम में सत्यापित करना संभव हुआ है
- डेवलपर C/C++ के काफ़ी करीब तरीके से code लिख सकते हैं, और केवल ज़रूरी जगहों पर pure और region borrowing लागू करके generation check overhead कम कर सकते हैं
- पहला zero-check Vale program roguelike level generation के लिए Cellular Automata उदाहरण था, और उसका resulting assembly
unsafe_with_bounds mode के लगभग समान स्तर तक पहुँच गया
- benchmark में
safe_fastest ने unsafe_with_bounds की तुलना में कोई observable slowdown नहीं दिखाया, जबकि unsafe_no_bounds दोनों modes से 1.18 ± 0.01 गुना तेज़ था
- यह अभी C/Rust के साथ सीधी तुलना नहीं है, और LLVM optimization noise, prototype की परिपक्वता, तथा inline data के अभाव के कारण Vale-विशिष्ट pre-optimizer और region features की और सफ़ाई बाकी है
Generational references और region borrowing का संयोजन
- Vale का memory-safe approach reference counting, tracing garbage collection, और borrow checking का उपयोग न करने की दिशा में है
- इसकी बुनियादी संरचना में डेवलपर C या C++ के क़रीब तरीके से program लिखते हैं, और Vale के generational references memory safety बनाए रखते हैं
- इसके बाद pure और region borrowing लागू करने पर generation check overhead का अधिकांश हिस्सा हटाया जा सकता है
- linear style तक जोड़ने पर Vale code में generation checks को zero तक लाने की संभावना बताई गई है
- region borrowing पूरी तरह opt-in है, इसलिए पहले आराम से लिखना और बाद में केवल optimization की ज़रूरत वाले हिस्सों में इसे जोड़ना संभव है
- लक्ष्य ऐसी संरचना का है जहाँ एक ही program में कुछ हिस्से Java की तरह लचीले, कुछ Rust की तरह तेज़, या इनके बीच किसी भी बिंदु पर चुने जा सकें
Prototype बनाने के लिए क्या-क्या करना पड़ा
- पिछले कुछ वर्षों में compiler की बुनियाद बनाते हुए region-based borrowing system और generational references को साथ support करने का काम किया गया
- borrowing system ख़ुद जटिल था, और पहले के templates से अधिक शक्तिशाली full generics की ज़रूरत पड़ी
- regions और generational references को स्वाभाविक रूप से साथ काम कराने के लिए compiler में नए चरण की भी आवश्यकता हुई
- अंदरूनी रूप से regions को “pure height” integer में घटाया जाता है
- region generic parameter को negative, base region को 0, और हर pure block को बढ़ते positive मान के रूप में व्यक्त किया जाता है
- कुछ महीने पहले region prototype पूरा हुआ, और अभी काफ़ी rough edges होने के बावजूद पहली बार कुछ सफलतापूर्वक compile किया गया
- इसका परिणाम पहला zero-check Vale program बना
--print_mem_overhead true compiler flag से program में generation checks की संख्या गिनी जा सकती है
पहला zero-check program और assembly तुलना
- पहला program roguelike game level generate करने वाला Cellular Automata उदाहरण था
- compiler code की छोटी-सी गलती भी resulting assembly में अतिरिक्त instructions जोड़कर अंतिम program में कृत्रिम overhead ला सकती है
- समस्या का पता लगाने के लिए resulting assembly की तुलना लगातार Vale के unsafe modes से की गई
unsafe_no_bounds: C की तरह सभी memory safety protections बंद, और generational references की जगह raw pointers का उपयोग
unsafe_with_bounds: Rust की तरह array access पर bounds checking जोड़ी गई
- कई महीनों तक अंतर ट्रैक करने के बाद resulting assembly
unsafe_with_bounds mode के लगभग समान हो गई
- एकमात्र अपेक्षित अंतर यह था कि हर allocation के शीर्ष पर pseudo-random generation number डाला जाता था, जिसे वास्तविक generation checks में पढ़ा नहीं जाता था
- अंदरूनी रूप से इसे तेज़ रखने के लिए monotonically increasing register का उपयोग किया जाता है
- isolates या unique references जुड़ने पर यह अंतर भी हटाया जा सकता है
Benchmark परिणाम और मापन की शर्तें
- benchmark का सारांश इस प्रकार है
Summary
'./build_unsafe_no_bounds/main' ran
1.18 ± 0.01 times faster than './build_unsafe_with_bounds/main'
1.18 ± 0.01 times faster than './build_safe_fastest/main'
- Vale के normal mode
safe_fastest ने केवल bounds checking वाले mode की तुलना में slowdown नहीं दिखाया
- इस measurement में इस approach ने कोई observable overhead नहीं दिखाया
- इसे ख़ुद चलाने के लिए regions branch build किया जा सकता है, benchmarking scripts देखी जा सकती हैं, और discord server पर सवाल पूछे जा सकते हैं
- measurement conditions की कुछ स्पष्ट सीमाएँ हैं
- यह C या Rust जैसी भाषाओं के साथ सीधा benchmark नहीं है
- उन compilers में कई वर्षों के अलग optimization मौजूद हैं, जो प्रयोग के variables को धुंधला कर सकते हैं
- memory-safe approach के अंतर को अलग करने के लिए
unsafe_no_bounds, unsafe_with_bounds से तुलना की गई
- test environment Ubuntu 22.04 चलाने वाला Razer Blade 15" 2018, 512GB SSD था
- measurement tool hyperfine था और इसे cset shield के भीतर चलाया गया
बड़े programs में दिखने वाला optimization noise
- बड़े programs में optimizer noise काफ़ी देखा गया
- यह benchmark noise से अलग है; measurement setup ने
± 0.01 जैसी बहुत consistent run times दिखाईं
- किसी एक हिस्से में छोटा बदलाव measurement को एक दिशा में हिला सकता था
- generation number के आकार बदलने पर लगातार
1.13 ± 0.01 का negative overhead मिलने का मामला भी सामने आया
- program में generation numbers अधिक नहीं थे, इसलिए यह परिणाम अजीब था
- संभव है कि register allocation में बदलाव, semantic अंतर से आने वाले performance अंतर पर भारी पड़ गया हो
- एक बड़े program, यानी छोटे roguelike game, में optimizer if statement के भीतर समान दो branches को merge नहीं कर पाया, और अन्य स्पष्ट optimizations भी छूट गईं
- unread integer की मौजूदगी का क्या प्रभाव पड़ता है यह स्पष्ट नहीं है, और LLVM bug की संभावना भी है
- यह परिणाम संकेत देता है कि Rust के MIR जैसा Vale-विशिष्ट pre-optimizer आवश्यक हो सकता है
- LLVM को अधिक C-केंद्रित सोच के साथ डिज़ाइन किया गया था
- यदि LLVM generational references को जानबूझकर freed memory access करने वाले pattern के रूप में समझे, तो उसे undefined behavior मान सकता है
लागू होने की संभावना और अगला काम
- generational references और regions का संयोजन बहुत तेज़ memory-safe approach दे सकता है
- यह approach उन software domains में अच्छी तरह फिट हो सकती है जहाँ ये शर्तें हों
- tracing garbage collection की तुलना में अधिक predictable latency चाहिए
- reference counting की तुलना में बेहतर performance और cache friendliness चाहिए
- borrow checking की तुलना में आसान prototyping और iteration चाहिए
- Vale की C या C++ से सीधे टक्कर वाली तुलना से पहले अभी कुछ काम बाकी है
- LLVM optimizer को generation और immutability infer करने में समस्या है, इसलिए Vale-विशिष्ट pre-optimizer की आवश्यकता है
- Vale को वर्तमान अस्थायी उपाय, जिसमें सभी structs heap पर रखे जाते हैं, की जगह inline data support करना होगा
- ऊपर वाले benchmark में structs का उपयोग नहीं हुआ, इसलिए inline data की अनुपस्थिति ने उस परिणाम को प्रभावित नहीं किया
- region feature अभी prototype चरण में है, इसलिए rough edges को ठीक करके और technical debt घटाकर इसे main branch में merge करना होगा
- merge के बाद योजना यह है कि standard library को regions का उपयोग करने लायक बनाया जाए, ताकि उपयोगकर्ताओं के main program code में regions सीधे न भी हों तो भी लाभ मिल सके
- मौजूदा measurement यह दिखाती है कि zero-check program संभव है और अपेक्षित speed तक पहुँच सकता है
1 टिप्पणियां
Hacker News की राय
मैंने Vale डाउनलोड करके आज़माया, लेकिन जब पहली बार
valeccompiler चलाया और कोई argument नहीं दिया, तो सीधे "(panic)" प्रिंट हुआ — यह अच्छा impression नहीं देताpanicबहुत तीखा शब्द है, और मुझे लगता है कि सामान्य error handling वाली स्थितियों में इससे बचना चाहिए। किसी program का panic करना ऐसा लगता है जैसे हालत नियंत्रण से बाहर हो गई हो, इसलिए बाद में अच्छा एहसास नहीं रहताफिर मैंने command-line arguments की help देखने की कोशिश की, लेकिन फिलहाल वह लगभग मौजूद नहीं थी। वेबसाइट का Hello World example
hello.vlमें सेव करकेvalec hello.vlचलाया तोUnknown subcommandआयाइसलिए
valec build hello.vlचलाया, तोUnrecognized input: hello.vlके बाद फिर(panic)आया, औरvalec helpभी मददगार नहीं था, इसलिए आखिरकार मैंने छोड़ दिया। समझ नहीं आ रहा कि इसे इस्तेमाल कैसे करना हैvalec-help-build.txtको सीधेcatकरके देखोगे तो जो जानकारी चाहिए वह समझाई गई मिलेगीcompiler अभी काफी rough edges वाली स्थिति में है। अगस्त से मई तक हमने region prototyping पर 100% ध्यान दिया, और अभी जो तुम देख रहे हो वह उसी दौरान जमा हुआ technical debt है। इसमें help system के integration tests न होना भी शामिल है
पिछले 1–2 महीनों से हम यह debt चुका रहे हैं, लेकिन अभी 0.2 release वाले स्तर तक वापस नहीं पहुँचे हैं। अगर और मदद चाहिए तो बताना, या Discord server पर आना — वहाँ मदद करने वाले कई लोग हैं
हालांकि README इस बात को साफ़ नहीं दिखाता और उसमें “Try Vale” लिखा है, इसलिए बात थोड़ी ambiguous है। फिर भी फिलहाल यह research and development / proof of concept के ज्यादा करीब लगता है
user interface या bugs के लिहाज से भी देखें, तो 40 साल पुराने C++ को 35 साल पुराने gdb से debug करने का अनुभव किसी भी experimental language से अच्छी टक्कर ले सकता है। उदाहरण के लिए
funcname()::staticvarnameoutput अजीब interface है और लगभग आधी बार fail हो जाता है। C++ build systems की तो बात ही अलग हैexperimental technology हो तो concept की आलोचना की जा सकती है, लेकिन rough user interface को कुछ हद तक स्वीकार किया जा सकता है
https://github.com/ValeLang/Vale#building-a-vale-program
tracing garbage collection की तुलना में latency ज्यादा predictable हो, reference counting की तुलना में performance और cache-friendliness बेहतर हो, और borrow checking की तुलना में prototyping व iteration आसान हों — यह सुनकर सिर्फ curiosity नहीं, सच में interest पैदा हो गया
RSS feed भी subscribe करना शुरू कर दिया: https://verdagon.dev/rss.xml
Vale को और sponsors की ज़रूरत है
https://github.com/sponsors/ValeLang
जब तक यह post front page पर है, उम्मीद है कि project को $3,000 per month वाले goal तक पहुँचाने में मदद मिलेगी
मैं चाहता हूँ कि Evan यह काम full-time कर सके। मैं खुद भी sponsor हूँ। तेज़, सुरक्षित और फिर भी prototyping में मज़ेदार language support करने लायक है
“Vale-specific pre-optimizer, Rust के Cranelift जैसा कुछ” शायद MIR, यानी mid-level intermediate representation, की बात कर रहा है। इस पर एक अच्छा blog post है: https://blog.rust-lang.org/2016/04/19/MIR.html
Cranelift मुख्य रूप से JIT पर focused compiler backend है, लेकिन theory में यह LLVM की जगह भी ले सकता है। alternative backend पर काम भी चल रहा है, लेकिन कुछ constraints हैं: https://github.com/bjorn3/rustc_codegen_cranelift
ऐसा approach जिसमें ज्यादातर code में memory management की चिंता न करनी पड़े, और सिर्फ hot code paths को zero-cost abstractions से optimize करने का option मिले, दोनों तरफ़ की खूबियाँ देता लगता है
खासकर तब, जब convenience के लिए safety नहीं बल्कि सिर्फ performance का trade-off करना पड़े
shared ownership बुरा concept है, इसलिए smart pointers भी इस्तेमाल नहीं करता
memory management की समस्याएँ कुल मिलाकर मामूली ही लगती हैं
generational references के संदर्भ में सुरक्षित कहने का मतलब क्या है, यह बात मुझे अब भी समझनी है
अगर मैंने सही समझा है, तो इसका मतलब use-after-free और double-free को रोकना है? अगर ऐसा है, तो जब अपेक्षित generation और वास्तविक generation मेल नहीं खाते, तब memory access पर program फिर भी fail हो सकता है
इस लिहाज से यह reference counting, tracing garbage collection और borrow checking से कम सुरक्षित लगता है
अगर reference के ज़रिए freed memory access करने की कोशिश की जाए, तो predictable और सुरक्षित तरीके से segmentation fault या assertion failure आना चाहिए। आगे virtual address space को remap करने वाला सुधार आ जाए तो segmentation fault भी हट सकता है, इसलिए इसे लेकर उम्मीद है
हालांकि अगर generation index हो, तो actual access की कोशिश करने से पहले runtime में यह check करना भी संभव होना चाहिए कि access valid है या नहीं। Vale में यह संभव है या नहीं, पता नहीं
malloc/freeसे ज़्यादा सुरक्षित है, और इसके दूसरे फायदे भी हैंcheckfunction को allocation का generation number चाहिए, इसलिए वह allocation को access करता है। यानी reference उस allocation को access कर सकता है या नहीं, यह verify करने के लिए पहले उसी allocation को access करना पड़ता हैबेशक अगर allocation पहले ही free हो चुका है, तो उस allocation और generation number को access करना ही undefined behavior है, इसलिए यह काम नहीं करेगा
यह इतना obvious लग रहा है कि समझ नहीं आ रहा मैं कोई बड़ी बात miss कर रहा हूं, या यहां “memory safety” का मतलब बिल्कुल अलग है
Vale, V जैसी language नहीं है। V को https://mawfig.github.io/2022/06/18/v-lang-in-2022.html पर बहुत critical review मिला था, और नाम मिलते-जुलते होने की वजह से मैंने उसे गलती से Vale समझ रखा था
अगर किसी और ने भी यही गलती की हो, इसलिए लिख रहा हूं
लेख की बातें अब relevant नहीं हैं, फिर भी वह पड़ा हुआ है, और वह उस blog का इकलौता लेख भी है
अब 2023 है और V भी beta (0.4) में है। ऊपर से वह लेख बनाने वाले व्यक्ति ने one-off GitHub account इस्तेमाल करके review/attack पोस्ट किया, विवाद बनाया और फिर गायब हो गया
उस blog पर डाला गया इकलौता लेख भी V पर हमला ही है, कोई और reviews नहीं हैं। जिन हिस्सों में असल बात थी, वे पहले ही ठीक कर दिए गए हैं[1]
mawfig.githubखोजकर देखें तो HN पर इसे बार-बार फैलाया गया दिखता है, और आम तौर पर बदनाम करने के लिए इस्तेमाल हुआ है[1]: https://github.com/vlang/v/issues/14803
[1]: https://github.com/vlang/v/issues/14787
[1]: https://github.com/vlang/v/issues/14786
Evan को यह milestone हासिल करने पर बधाई। programming language design या compiler का अनुभव नहीं है, लेकिन Vale के लेख पढ़ना अच्छा लगता है
अब Evan का Vale और Adobe Software Technology Lab का Val दोनों हैं, इसलिए related material खोजना काफी मुश्किल हो जाएगा
https://www.val-lang.dev
“Vale तेज़ है: Vale LLVM में AOT compile होता है, static types इस्तेमाल करता है, speed और flexibility वाली memory safety के लिए नई generational references technique इस्तेमाल करता है, और जल्द ही region borrow checking लाकर और तेज़ हो जाएगा”
https://vale.dev/
ऐसा लग रहा है जैसे 5 साल से चल रही दो लोगों की बहस चोरी-छिपे सुन रहा हूं
कोई समझा सकता है कि यहां हो क्या रहा है? लेख बहुत कठिन है
संक्षेप में, Vale एक ज्यादा साफ-सुथरी C++ जैसी language है, और generational references[0] का इस्तेमाल करती है, जो mentally ASan[1] चालू करके run करने जैसा है
generational references में थोड़ा overhead होता है, लेकिन उसे regions[2], और खास तौर पर immutable region borrowing[3] से हटाया जा सकता है। इससे Vale memory safety बनाए रखते हुए high-performance language बनने के लक्ष्य के करीब पहुंचता है
[0] https://verdagon.dev/blog/generational-references
[1] https://github.com/google/sanitizers/wiki/AddressSanitizer
[3] https://verdagon.dev/blog/zero-cost-borrowing-regions-overvi...
[4] https://verdagon.dev/blog/zero-cost-borrowing-regions-part-1...