3 पॉइंट द्वारा GN⁺ 2026-01-03 | 2 टिप्पणियां | WhatsApp पर शेयर करें
  • 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 में install method, fetch_gem_if_not_cached और install को लगातार चलाता है
    • इसकी वजह से dependency chain वाले gem (a -> b -> c) सिर्फ sequentially install हो पाते हैं
  • प्रयोग में पाया गया कि 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 को u64 integer में encode करके comparison तेज़ करता है
    • Ruby में भी Gem::Version को integer-based representation में बदलकर resolver performance सुधारी जा सकती है
    • इस दिशा में refactoring की कोशिश पहले हुई थी, लेकिन backward compatibility issues की वजह से उसे टाल दिया गया

निष्कर्ष और आगे की योजना

  • 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 टिप्पणियां

 
iolothebard 2026-01-06

बातें सस्ती हैं। मुझे code दिखाइए!

 
GN⁺ 2026-01-03
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 किया जा सके

    • मैंने भी यही सोचा था, लेकिन gemspec की जानकारी और RubyGems.org metadata अलग हो सकती है
      जब मैं RubyGems.org में काम करता था, तब मुझे याद है कि metadata version के हिसाब से extract किया जाता था
      पुराने versions के gemspec को फिर से process करना पड़ेगा, और यह risk भरा metadata change हो सकता है
      इसलिए पुराने versions पर इसे लागू करना मुश्किल होगा, लेकिन आगे चलकर unpack किए बिना install order पता चल सके, ऐसा सुधार संभव लगता है
  • मुझे यह पसंद है कि Aaron Bundler को Rust में rewrite करने के बजाय वास्तविक algorithmic improvements पर ध्यान दे रहे हैं

    • speed improvement अच्छी बात है, लेकिन मुझे Ruby install को खुद manage करने वाली सुविधा ज़्यादा ज़रूरी लगती है
      कई version management tools और Ruby versions के आपस में घुल-मिल जाने वाला अव्यवस्थित environment सच में बहुत परेशान करता है
    • Aaron Shopify से हैं, शायद इसलिए gem.coop project का ज़िक्र नहीं है, इसे लेकर मेरे मन में मिश्रित भावनाएँ हैं
      मेरे हिसाब से समस्या सिर्फ 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 की सिफारिश नहीं की जाती

    • मैंने पहले इससे मिलता-जुलता code लिखा था; उसमें disk cache नहीं थी, लेकिन on-the-fly hash generation से भी अच्छा speedup मिला था
      आज के हिसाब से वह पुराना लग सकता है, लेकिन वह अभी भी मेरा प्रिय mini project है
      code: fastup
    • “bundle install” optimization पर ध्यान देना गलत दिशा है
      असली समस्या यह है कि $LOAD_PATH सभी gems जोड़ देता है और combinatorial explosion पैदा करता है
      कई cache projects का मौजूद होना इस बात का सबूत है कि यही असली समस्या है
      पहले मेरे एक app को start होने में कई मिनट लगते थे, लेकिन load path को tweak करके मैंने उसे मिनटों के हिसाब से कम किया था
    • मैंने runtime में इसे संभालने की कोशिश की थी, लेकिन Ruby में efficient data structures की कमी के कारण इसे लागू करना कठिन था
    • असल में यह काम bootsnap पहले से करता है
      मैंने पहले bootsnap को bundler में integrate करने का सुझाव दिया था, लेकिन उसे ठुकरा दिया गया
  • RubyGems की संरचना का विवरण दिलचस्प लगा
    gem एक tar file होती है, और उसके अंदर मौजूद YAML GemSpec dependencies घोषित करता है
    RubyGems.org यह जानकारी API के ज़रिए देता है, इसलिए eval के बिना भी dependencies जाँची जा सकती हैं
    हालांकि YAML parsing efficiency के लिहाज़ से कमज़ोर format है, इसलिए JSON या protobuf जैसे विकल्प बेहतर हो सकते हैं
    फिर भी, अगर gemserver पहले से dependency information लौटा रहा है, तो शायद यह बहुत बड़ी समस्या नहीं है

    • YAML खास अच्छा नहीं है, लेकिन सामान्य gemspec sizes पर performance impact बहुत कम होगा, ऐसा लगता है
    • अगर lockfile इंसानों द्वारा संपादित नहीं, सिर्फ review के लिए हो, तो YAML की जटिल सुविधाएँ हटाकर एक simple parser बनाया जा सकता है
      उदाहरण: ऐसा ढाँचा जिसमें सिर्फ version, dependencies, hash वगैरह हों
    • असल में RubyGems और PyPI इस तरह के metadata को पहले से parse करके database में store करते हैं
      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 मिल सकता है

    • pure Ruby में और अधिक निकालना हो, तो
      1. तेज़ parsing वाले index format का उपयोग (संबंधित gist)
      2. शुरुआती download को threads से संभालना
      3. decompression और post-install को fork से अलग करना
        शायद मैं इस तरह आगे बढ़ूँगा
  • global cache जिसे सभी bundler instances साझा करें” वाले विचार की जाँच चल रही है
    लंबी अवधि में इससे बड़ा लाभ हो सकता है, लेकिन अभी यह देखा जा रहा है कि कहीं इसमें छिपी हुई complexity तो नहीं है
    संबंधित issue: rubygems #7249

    • यह पूरी तरह सरल नहीं है, लेकिन दूसरे ecosystems के पहले से मौजूद उदाहरणों को देखें तो यह पूरी तरह संभव लगता है
      Ruby इस समस्या को सबसे पहले हल नहीं कर रहा, इसलिए अब समय है कि वह भी इसका लाभ उठाए
  • optimization का मूल सिद्धांत सरल है — कुछ भी न करना सबसे तेज़ होता है

    • “smart code तेज़ होता है” वाली गलतफहमी छोड़नी होगी
      अनावश्यक काम को ही न करना असली optimization है