क्या Bundler, uv जितना तेज़ हो सकता है?
(tenderlovemaking.com)- Bundler की performance limits का विश्लेषण करते हुए, Python के package manager uv के तेज़ होने के कारणों की तुलना
- uv की speed का कारण Rust language नहीं, बल्कि parallel downloads, global cache, metadata-based dependency handling जैसी structural design choices हैं
- Bundler में download और install process आपस में जुड़े हुए हैं, जिससे parallel processing पर पाबंदी लगती है; इन्हें अलग करने पर बड़ा सुधार संभव है
- global cache integration, hardlink installs, PubGrub resolver integration आदि से RubyGems और Bundler के duplication को कम किया जा सकता है
- language rewrite के बिना भी ज़्यादातर performance gains Ruby code के भीतर हासिल किए जा सकते हैं, और uv-स्तर की speed के काफ़ी करीब पहुँचा जा सकता है
Bundler और uv की performance तुलना
- RailsWorld में उठे “Bundler, uv जितना तेज़ क्यों नहीं है?” सवाल के बाद Bundler के performance bottlenecks की जाँच की गई
- लेखक को भरोसा है कि Bundler, uv-स्तर की speed हासिल कर सकता है, और वे साफ़ कहते हैं कि performance gap भाषा का नहीं, design का मसला है
- Andrew Nesbitt के लेख “How uv got so fast” का हवाला देते हुए, uv की core optimization techniques को Bundler पर लागू किया जा सकता है या नहीं, इसका विश्लेषण किया गया
क्या Rust में rewrite ज़रूरी है?
- यह सही है कि uv, Rust में लिखा गया है, लेकिन उसकी speed का मूल कारण Rust itself नहीं है
- अगर Bundler के bottlenecks हटाने के बाद “अब सिर्फ Rust में rewrite ही अगला सुधार है” वाली स्थिति आ जाए, तो उसे सफलता माना जाएगा
- Rust rewrite से मौजूदा compatibility constraints के बिना experimental design आज़माने की आज़ादी मिलती है, लेकिन यह अनिवार्य नहीं है
Bundler के structural bottlenecks
- Bundler, gem download और install को एक ही method में जोड़ता है, जिससे parallel downloads संभव नहीं हो पाते
- example code में
installmethod,fetch_gem_if_not_cachedऔरinstallको लगातार चलाता है - इसकी वजह से dependency chain वाले gem (
a -> b -> c) सिर्फ sequentially install हो पाते हैं
- example code में
- प्रयोग में पाया गया कि dependencies होने पर 9 सेकंड से ज़्यादा लगते हैं, जबकि independent gems (
d, e, f) parallel download के साथ 4 सेकंड से कम में पूरे हो जाते हैं - download और install को अलग करने पर dependency rules बनाए रखते हुए भी parallel processing संभव हो जाती है
- चार चरणों में विभाजन का प्रस्ताव: download → unpack → compile → install
- pure Ruby gems के लिए dependency install order को और ढीला करके अतिरिक्त speed gain मिल सकता है
cache और install optimization
- uv की global cache और hardlink install approach, Bundler पर भी लागू की जा सकती है
- Bundler और RubyGems अभी Ruby version के हिसाब से अलग-अलग cache इस्तेमाल करते हैं
$XDG_CACHE_HOMEआधारित shared cache में इन्हें एकीकृत करने की ज़रूरत है- hardlink install, cache integration के बाद लागू किया जा सकता है
- Bundler पहले से PubGrub dependency resolver इस्तेमाल करता है, लेकिन RubyGems अब भी molinillo का उपयोग करता है
- दोनों systems के resolver integration को technical debt कम करने की कुंजी माना गया है
Rust-संबंधित optimization तत्वों की लागू-योग्यता
- Zero-copy deserialization को RubyGems के YAML parsing stage में कुछ हद तक लागू किया जा सकता है
- Ruby का GVL(Global VM Lock), IO-केंद्रित कामों में parallel processing पर बहुत बड़ी पाबंदी नहीं लगाता
- IO और ZLIB processing के दौरान GVL release हो जाता है, इसलिए parallel execution संभव है
- लेकिन छोटे file writes में GVL management overhead performance drop का कारण बनता है
- Ruby के भीतर इसे बेहतर बनाने का काम चल रहा है
- version comparison optimization: uv, versions को
u64integer में encode करके comparison तेज़ करता है- Ruby में भी
Gem::Versionको integer-based representation में बदलकर resolver performance सुधारी जा सकती है - इस दिशा में refactoring की कोशिश पहले हुई थी, लेकिन backward compatibility issues की वजह से उसे टाल दिया गया
- Ruby में भी
निष्कर्ष और आगे की योजना
- uv की speed, भाषा से ज़्यादा अनावश्यक काम हटाने वाली design की देन है, और Bundler को भी उसी दिशा में बेहतर बनाया जा सकता है
- RubyGems और Bundler के पास पहले से modern package management structure मौजूद है, इसलिए uv-स्तर की speed हासिल करना व्यावहारिक लक्ष्य है
- सबसे बड़ी चुनौती legacy code और compatibility बनाए रखना है
- Rust में rewrite किए बिना भी 99% performance improvement Ruby code के भीतर संभव है, और बाकी 1% का असर मामूली होगा
- अगली पोस्ट में Bundler और RubyGems की वास्तविक profiling और ठोस bottleneck causes पर चर्चा की जाएगी
2 टिप्पणियां
बातें सस्ती हैं। मुझे code दिखाइए!
Hacker News की राय
मैं Bundler की आंतरिक संरचना को गहराई से नहीं जानता, लेकिन मुझे लगता है कि सबसे बड़ा सुधार uv की cache design को अपनाने से आएगा
uv के तेज़ होने की एक बड़ी वजह उसकी cache संरचना है, और इसे दूसरी भाषाओं या ecosystems में भी दोहराया जा सकता है
हालांकि
requires-pythonकी upper bound को अनदेखा करना performance के लिए नहीं, बल्कि बेहतर dependency resolution के लिए हैउदाहरण के लिए, अगर कोई project Python 3.8 या उससे ऊपर मांगता है, लेकिन कोई dependency
<4की सीमा लगा देती है, तो Python 4 पर install नहीं हो पाएगाuv सभी supported versions के लिए resolve करता है, इसलिए upper bound को अनदेखा करने से समय की बचत लगभग नहीं होती
इससे जुड़ी चर्चा Python Discuss फ़ोरम में देखी जा सकती है
जैसे PEP 658 के बाद Python का Simple Repository API metadata सीधे उपलब्ध कराता है, वैसे ही RubyGems.org भी पहले से मिलती-जुलती जानकारी देता है
लेकिन gem को unpack किए बिना यह पता नहीं चलता कि उसमें native extension है या नहीं
इसलिए सुझाव है कि यह जानकारी RubyGems.org metadata में सीधे जोड़ दी जाए, ताकि dependency install tree को पूरी तरह parallelize किया जा सके
जब मैं RubyGems.org में काम करता था, तब मुझे याद है कि metadata version के हिसाब से extract किया जाता था
पुराने versions के gemspec को फिर से process करना पड़ेगा, और यह risk भरा metadata change हो सकता है
इसलिए पुराने versions पर इसे लागू करना मुश्किल होगा, लेकिन आगे चलकर unpack किए बिना install order पता चल सके, ऐसा सुधार संभव लगता है
मुझे यह पसंद है कि Aaron Bundler को Rust में rewrite करने के बजाय वास्तविक algorithmic improvements पर ध्यान दे रहे हैं
कई version management tools और Ruby versions के आपस में घुल-मिल जाने वाला अव्यवस्थित environment सच में बहुत परेशान करता है
मेरे हिसाब से समस्या सिर्फ speed नहीं, बल्कि control और ecosystem की दिशा भी है
Ruby ने पिछले 10 साल speed पर ज़ोर दिया, लेकिन documentation quality और community management शायद उससे भी ज़्यादा महत्वपूर्ण थे
अब समय है कि भाषा के गिरते जाने की वजहों पर गंभीरता से सोचा जाए और अलग-अलग ideas को आगे बढ़ाया जाए
हाल की संबंधित पोस्ट में How uv got so fast (दिसंबर 2025, 457 comments) भी है
RubyGems को और तेज़ बनाने के लिए हर gem की file list को registry/database में रखना मुख्य बात है
इससे
requireके समय हर बार file system scan करने की ज़रूरत नहीं पड़ेगीअगर gem को सीधे modify किया जाए, तो metadata को फिर से hash करना पड़ेगा, लेकिन वैसे भी manual modification की सिफारिश नहीं की जाती
आज के हिसाब से वह पुराना लग सकता है, लेकिन वह अभी भी मेरा प्रिय mini project है
code: fastup
असली समस्या यह है कि
$LOAD_PATHसभी gems जोड़ देता है और combinatorial explosion पैदा करता हैकई cache projects का मौजूद होना इस बात का सबूत है कि यही असली समस्या है
पहले मेरे एक app को start होने में कई मिनट लगते थे, लेकिन load path को tweak करके मैंने उसे मिनटों के हिसाब से कम किया था
मैंने पहले bootsnap को bundler में integrate करने का सुझाव दिया था, लेकिन उसे ठुकरा दिया गया
RubyGems की संरचना का विवरण दिलचस्प लगा
gem एक tar file होती है, और उसके अंदर मौजूद YAML GemSpec dependencies घोषित करता है
RubyGems.org यह जानकारी API के ज़रिए देता है, इसलिए eval के बिना भी dependencies जाँची जा सकती हैं
हालांकि YAML parsing efficiency के लिहाज़ से कमज़ोर format है, इसलिए JSON या protobuf जैसे विकल्प बेहतर हो सकते हैं
फिर भी, अगर gemserver पहले से dependency information लौटा रहा है, तो शायद यह बहुत बड़ी समस्या नहीं है
उदाहरण: ऐसा ढाँचा जिसमें सिर्फ version, dependencies, hash वगैरह हों
uv के तेज़ होने की एक वजह यही है — dependency calculation package download किए बिना भी संभव है
मैंने पहले gem install के तरीके को बेहतर बनाने पर एक prototype video बनाई थी
how_gems_should_be.mov
Ruby के fibers (या Async library) को अक्सर ज़रूरत से ज़्यादा महत्व दिया जाता है
threads की तरह यहाँ भी connection pool जैसे higher-level coordination issues बने रहते हैं
फिर भी, अगर IO-bound install tasks को asynchronous तरीके से किया जाए, तो अर्थपूर्ण performance improvement मिल सकता है
शायद मैं इस तरह आगे बढ़ूँगा
“global cache जिसे सभी bundler instances साझा करें” वाले विचार की जाँच चल रही है
लंबी अवधि में इससे बड़ा लाभ हो सकता है, लेकिन अभी यह देखा जा रहा है कि कहीं इसमें छिपी हुई complexity तो नहीं है
संबंधित issue: rubygems #7249
Ruby इस समस्या को सबसे पहले हल नहीं कर रहा, इसलिए अब समय है कि वह भी इसका लाभ उठाए
optimization का मूल सिद्धांत सरल है — कुछ भी न करना सबसे तेज़ होता है
अनावश्यक काम को ही न करना असली optimization है