3 poin oleh GN⁺ 2026-01-03 | 2 komentar | Bagikan ke WhatsApp
  • Menganalisis batas performa Bundler sambil membandingkan alasan package manager Python uv bisa sangat cepat
  • Kecepatan uv bukan karena bahasa Rust, melainkan berkat desain struktural seperti unduhan paralel, cache global, dan pemrosesan dependensi berbasis metadata
  • Bundler memiliki keterbatasan paralelisasi karena proses unduh dan instalasi saling terikat, dan memisahkannya berpotensi memberi peningkatan besar
  • Integrasi cache global, instalasi dengan hardlink, dan integrasi solver PubGrub dapat mengurangi duplikasi antara RubyGems dan Bundler
  • Bahkan tanpa menulis ulang ke bahasa lain, sebagian besar peningkatan performa bisa dicapai di dalam kode Ruby, dan berpotensi mendekati kecepatan uv

Perbandingan performa Bundler dan uv

  • Penyelidikan terhadap bottleneck performa Bundler dimulai dari pertanyaan yang muncul di RailsWorld: “mengapa Bundler tidak secepat uv?”
  • Penulis yakin Bundler bisa mencapai kecepatan setara uv, dan menegaskan bahwa perbedaan performa adalah masalah desain, bukan bahasa
  • Dengan mengutip tulisan Andrew Nesbitt “How uv got so fast”, artikel ini menganalisis apakah teknik optimasi utama uv dapat diterapkan ke Bundler

Apakah perlu ditulis ulang ke Rust?

  • Memang benar uv ditulis dalam Rust, tetapi penyebab mendasar kecepatannya bukan Rust itu sendiri
  • Jika bottleneck Bundler berhasil dihilangkan hingga “menulis ulang ke Rust menjadi satu-satunya peningkatan yang tersisa”, itu justru dianggap sebagai keberhasilan
  • Penulisan ulang ke Rust memang memberi kebebasan untuk mencoba desain eksperimental tanpa batasan kompatibilitas lama, tetapi itu bukan syarat mutlak

Bottleneck struktural Bundler

  • Bundler menggabungkan unduhan gem dan instalasi dalam satu method, sehingga unduhan paralel tidak memungkinkan
    • Pada contoh kode, method install menjalankan fetch_gem_if_not_cached lalu install secara berurutan
    • Akibatnya, gem yang memiliki relasi dependensi (a -> b -> c) hanya bisa dipasang secara berurutan
  • Hasil eksperimen menunjukkan bahwa ketika ada dependensi, proses memakan waktu lebih dari 9 detik, sedangkan gem independen (d, e, f) dapat selesai dalam kurang dari 4 detik lewat unduhan paralel
  • Dengan memisahkan unduhan dan instalasi, paralelisasi tetap dimungkinkan tanpa melanggar aturan dependensi
    • Diusulkan pemisahan menjadi empat tahap (unduh → ekstrak → kompilasi → instalasi)
    • Untuk gem Ruby murni, urutan instalasi dependensi bisa dilonggarkan agar ada peningkatan kecepatan tambahan

Optimasi cache dan instalasi

  • Pendekatan cache global dan instalasi hardlink milik uv juga bisa diterapkan ke Bundler
    • Saat ini Bundler dan RubyGems memakai cache terpisah untuk tiap versi Ruby
    • Perlu disatukan ke cache bersama berbasis $XDG_CACHE_HOME
    • Instalasi hardlink bisa diterapkan setelah integrasi cache selesai
  • Bundler sudah memakai solver dependensi PubGrub, tetapi RubyGems masih menggunakan molinillo
    • Penyatuan solver di kedua sistem menjadi kunci untuk mengurangi utang teknis

Penerapan elemen optimasi yang terkait Rust

  • Deserialisasi zero-copy punya kemungkinan diterapkan sebagian pada tahap parsing YAML di RubyGems
  • GVL (Global VM Lock) Ruby tidak terlalu membatasi paralelisasi untuk pekerjaan yang berfokus pada IO
    • IO dan pemrosesan ZLIB melepaskan GVL, sehingga dapat berjalan paralel
    • Namun, pada penulisan file kecil, overhead pengelolaan GVL bisa menjadi faktor penurun performa
    • Sudah ada pekerjaan yang sedang berlangsung di internal Ruby untuk memperbaikinya
  • Optimasi perbandingan versi: uv mempercepat perbandingan dengan mengenkode versi sebagai bilangan bulat u64
    • Di Ruby, Gem::Version juga bisa diubah ke basis bilangan bulat untuk meningkatkan performa solver
    • Upaya refaktor terkait sebenarnya sudah pernah ada, tetapi ditunda karena masalah kompatibilitas mundur

Kesimpulan dan rencana ke depan

  • Kecepatan uv berasal dari desain yang menghilangkan pekerjaan yang tidak perlu, bukan dari bahasanya, dan Bundler pun bisa ditingkatkan ke arah yang sama
  • RubyGems dan Bundler sudah memiliki struktur package management modern, sehingga mencapai kecepatan setara uv adalah target yang realistis
  • Tantangan terbesar adalah mempertahankan kompatibilitas dengan kode lama
  • Bahkan tanpa penulisan ulang ke Rust, 99% peningkatan performa bisa dicapai di dalam kode Ruby, dan sisa 1%-nya relatif kecil
  • Tulisan lanjutan akan membahas profiling nyata Bundler dan RubyGems serta penyebab bottleneck yang lebih spesifik

2 komentar

 
iolothebard 2026-01-06

Omong kosong itu murah. Tunjukkan kodenya!

 
GN⁺ 2026-01-03
Komentar Hacker News
  • Saya tidak terlalu paham struktur internal Bundler, tetapi saya rasa perbaikan terbesar adalah mengadopsi desain cache uv
    Salah satu alasan utama uv bisa cepat ada pada struktur cache-nya, dan ini bisa direplikasi di bahasa atau ekosistem lain
    Namun, bagian yang mengabaikan batas atas requires-python bukan karena performa, melainkan demi resolusi dependensi yang lebih baik
    Misalnya, sebuah proyek membutuhkan Python 3.8 atau lebih baru, tetapi ada dependensi yang memberi batasan <4, maka instalasi akan gagal di Python 4
    uv melakukan resolusi untuk semua versi yang didukung, jadi mengabaikan batas atas itu hampir tidak menghemat waktu
    Diskusi terkait bisa dilihat di forum Python Discuss

  • Seperti Python Simple Repository API yang sejak PEP 658 menyediakan metadata secara langsung, RubyGems.org juga sudah menyediakan informasi serupa
    Namun, kita baru bisa tahu apakah ada native extension setelah gem dibongkar
    Jadi ada usulan: kalau informasi ini ditambahkan langsung ke metadata RubyGems.org, bukankah pohon instalasi dependensi bisa diparalelkan sepenuhnya?

    • Saya juga sempat berpikir begitu, tetapi ada kemungkinan informasi di gemspec dan metadata RubyGems.org berbeda
      Saat dulu bekerja di RubyGems.org, saya ingat metadata diekstrak per versi
      Gemspec versi lama harus diproses ulang, dan ini bisa menjadi perubahan metadata yang berisiko
      Jadi mungkin sulit diterapkan ke versi lama, tetapi ke depannya sepertinya bisa diperbaiki agar urutan instalasi diketahui tanpa perlu unpack
  • Saya suka Aaron fokus pada perbaikan algoritma yang benar-benar substantif alih-alih menulis ulang Bundler dalam Rust

    • Peningkatan kecepatan memang bagus, tetapi saya lebih butuh fitur yang juga mengelola instalasi Ruby itu sendiri
      Lingkungan yang kacau dengan campuran berbagai alat manajemen versi dan versi Ruby benar-benar menjengkelkan
    • Mungkin karena Aaron berasal dari Shopify, jadi tidak ada penyebutan proyek gem.coop, dan itu menimbulkan perasaan campur aduk
      Menurut saya, masalahnya bukan sekadar kecepatan, melainkan kendali dan arah ekosistem
      Ruby selama 10 tahun terakhir berfokus pada kecepatan, padahal kualitas dokumentasi dan pengelolaan komunitas justru lebih penting
      Sudah saatnya benar-benar memikirkan mengapa bahasa ini menurun, dan mendorong berbagai ide yang berbeda
  • Ada tulisan terkait terbaru: How uv got so fast (Desember 2025, 457 komentar)

  • Jika ingin membuat RubyGems lebih cepat, kuncinya adalah mendaftarkan/menyimpan daftar file tiap gem di registri atau database
    Dengan begitu, saat require dijalankan, tidak perlu memindai file system setiap kali
    Jika gem dimodifikasi langsung, metadata memang harus di-hash ulang, tetapi modifikasi manual memang tidak dianjurkan

    • Saya pernah menulis kode yang mirip dengan ini; memang tidak ada cache disk, tetapi bahkan membuat hash secara langsung pun sudah memberi peningkatan performa yang besar
      Mungkin sekarang sudah kuno, tetapi ini tetap proyek mini yang saya sukai
      Kode: fastup
    • Optimisasi “bundle install” adalah pendekatan yang salah arah
      Masalah sebenarnya adalah struktur $LOAD_PATH yang menambahkan semua gem dan menyebabkan ledakan kombinatorial
      Fakta bahwa ada banyak proyek cache adalah bukti bahwa ini memang masalah nyata
      Dulu startup aplikasi bisa makan waktu beberapa menit, dan saya pernah memangkasnya dalam hitungan menit dengan memanipulasi load path
    • Saya pernah mencoba menangani ini di runtime, tetapi Ruby kekurangan struktur data yang efisien sehingga implementasinya sulit
    • Sebenarnya ini sudah dilakukan oleh bootsnap
      Saya dulu pernah mengusulkan agar bootsnap diintegrasikan ke bundler, tetapi ditolak
  • Penjelasan tentang struktur RubyGems menarik
    Sebuah gem adalah file tar, dan YAML GemSpec di dalamnya mendeklarasikan dependensi
    RubyGems.org menyediakan informasi ini lewat API, jadi dependensi bisa diperiksa tanpa eval
    Namun YAML adalah format yang kurang efisien untuk parsing, jadi alternatif seperti JSON atau protobuf mungkin lebih baik
    Meski begitu, kalau gemserver memang sudah mengembalikan informasi dependensi, mungkin ini bukan masalah besar

    • YAML memang kurang ideal, tetapi pada ukuran gemspec umum, dampak performanya tampaknya kecil
    • Jika lockfile hanya untuk ditinjau dan bukan diedit manusia, kita bisa membuat parser sederhana tanpa fitur YAML yang kompleks
      Contoh: struktur yang hanya memuat versi, dependensi, dan hash
    • Sebenarnya metadata seperti ini sudah diparse lebih dulu dan disimpan di database oleh RubyGems atau PyPI
      Itulah salah satu alasan uv cepat — perhitungan dependensi bisa dilakukan tanpa mengunduh paket
  • Dulu saya pernah membuat video prototipe tentang cara instalasi gem seharusnya bekerja
    how_gems_should_be.mov

  • Fibers Ruby (atau library Async) sering kali dilebih-lebihkan
    Sama seperti thread, masalah koordinasi level tinggi seperti connection pool tetap ada
    Meski begitu, jika pekerjaan instalasi yang bersifat IO-bound ditangani secara asinkron, peningkatan performa yang berarti tetap bisa didapat

    • Jika ingin memeras lebih jauh dari Ruby murni,
      1. gunakan format indeks yang cepat diparse (gist terkait)
      2. tangani unduhan awal dengan thread
      3. pisahkan dekompresi dan post-install dengan fork
        Saya mungkin akan mendekatinya seperti itu
  • Ide “cache global dibagikan oleh semua instance bundler” sedang dikaji
    Dalam jangka panjang sepertinya akan sangat menguntungkan, tetapi masih dinilai apakah ada kompleksitas tersembunyi
    Isu terkait: rubygems #7249

    • Tidak sepenuhnya sederhana, tetapi dengan melihat contoh yang sudah lebih dulu ada di ekosistem lain, ini sangat mungkin dilakukan
      Ruby bukan yang pertama menyelesaikan masalah ini, jadi sekarang saatnya menikmati manfaatnya
  • Prinsip dasar optimisasi itu sederhana — tidak melakukan apa pun adalah yang paling cepat

    • Kita harus membuang anggapan bahwa “kode pintar itu cepat”
      Tidak melakukan pekerjaan yang tidak perlu itulah optimisasi yang sesungguhnya