1 poin oleh GN⁺ 2025-04-24 | 1 komentar | Bagikan ke WhatsApp
  • Bahasa Go hampir tidak memiliki perilaku tak terdefinisi dan memiliki semantik GC (garbage collection) yang sederhana
  • Di Go, manajemen memori manual dimungkinkan, dan ini dapat dilakukan dengan bekerja sama dengan GC
  • Arena adalah struktur data untuk mengalokasikan memori secara efisien dengan masa hidup yang sama, dan artikel ini menjelaskan cara mengimplementasikannya di Go
  • Menjelaskan bagaimana GC mengelola memori melalui algoritme Mark and Sweep
  • Dengan menggunakan Arena, kinerja alokasi memori dapat ditingkatkan, dan hal ini dimungkinkan melalui berbagai optimisasi
  • Mencoba meningkatkan performa dan meminimalkan beban GC melalui penghapusan write barrier, penggunaan ulang memori, chunk pooling, dan lain-lain
  • Menyajikan pola yang aman dan cepat untuk pemrosesan memori skala besar di dunia nyata melalui fitur seperti implementasi realloc, penggunaan ulang Arena, dan inisialisasi ulang (Reset)

Ikhtisar alokasi memori manual berbasis Arena di Go

  • Go adalah bahasa yang aman berkat perilaku GC yang jelas dan hampir tidak adanya Undefined Behavior
  • Dengan memanfaatkan paket unsafe, dimungkinkan melakukan kontrol memori langsung yang disesuaikan dengan implementasi internal GC
  • Artikel ini menjelaskan cara membuat allocator memori berbasis struktur Arena di Go yang dapat bekerja sama dengan GC

Definisi dan kebutuhan Arena

  • Arena adalah struktur untuk mengalokasikan objek dengan masa hidup yang sama secara efisien
  • Jika append biasa memperluas array secara eksponensial, Arena menambahkan blok baru dan menyediakan pointer
  • Antarmuka standarnya adalah sebagai berikut:
    • Alloc(size, align uintptr) unsafe.Pointer

Cara kerja pointer dan GC

  • GC bekerja dengan cara menandai (mark) dan mengambil kembali (sweep) memori
  • Untuk GC yang presisi, digunakan metadata bernama pointer bits yang memberi tahu posisi pointer
  • Jika pointer ditangani secara keliru di dalam Arena, GC dapat gagal melacak pointer sehingga berpotensi menimbulkan error use-after-free

Metode perancangan Arena

  • Struktur Arena memiliki field berikut:
    • next, left, cap, chunks
  • Semua alokasi diproses dengan alignment 8 byte, dan jika tidak cukup maka dibuat chunk baru dengan nextPow2
  • Chunk dialokasikan sebagai tipe struct { A [N]uintptr; P *Arena }, bukan []uintptr, sehingga GC dapat melacak Arena

Cara menjamin keamanan pointer di Arena

  • Jika menggunakan pointer yang dialokasikan hanya di dalam Arena, GC akan mempertahankan seluruh Arena
  • Dengan mengatur pointer agar mereferensikan Arena, kelangsungan hidup seluruh Arena dijamin oleh GC
  • Metode alokasi Arena melakukan hal berikut:
    • Menyimpan pointer Arena di akhir chunk dalam allocChunk()

Hasil benchmark performa

  • Dibandingkan new bawaan, alokasi Arena menunjukkan peningkatan performa rata-rata 2~4 kali atau lebih
  • Bahkan dalam situasi dengan beban GC tinggi, pendekatan Arena menunjukkan performa lebih baik hingga lebih dari 2 kali
  • Optimisasi seperti penghapusan write barrier dan pemanfaatan uintptr memberikan peningkatan performa hingga 20% pada alokasi kecil

Strategi penggunaan ulang chunk dan penghilangan heap

  • Penggunaan ulang chunk dimungkinkan dengan memanfaatkan sync.Pool
  • Melalui runtime.SetFinalizer(), chunk dapat dikembalikan ke pool saat Arena menghilang
  • Performanya sangat membaik pada alokasi kecil, tetapi pada alokasi besar bisa lebih lambat daripada new

Fitur inisialisasi ulang dan penggunaan ulang Arena

  • Dengan metode Reset(), Arena dapat dikembalikan ke keadaan awal
  • Meskipun risikonya besar, struktur yang sama dapat digunakan kembali tanpa realokasi memori
  • Saat digunakan kembali, chunk juga dipakai ulang sehingga performa meningkat drastis

Implementasi fitur Realloc

  • Dengan mengimplementasikan fungsi realloc di Arena, ekspansi dinamis dimungkinkan untuk alokasi terbaru
  • Jika tidak memungkinkan, memori baru dialokasikan lalu isinya disalin

Kesimpulan dan penyediaan kode lengkap

  • Dengan memahami secara mendalam mekanisme GC di Go dan berdasarkan implementasi internalnya, penulis menyelesaikan manajer memori berbasis Arena
  • Strukturnya memiliki keamanan dan performa sekaligus, dan jika digunakan dengan tepat sangat berguna untuk menangani struktur data berskala besar
  • Kode implementasi lengkap mencakup struct Arena serta New, Alloc, Reset, allocChunk, finalize, dan lainnya

1 komentar

 
GN⁺ 2025-04-24
Komentar Hacker News
  • Artikel ini menarik untuk dibaca

    • Jika Anda menikmati artikel ini atau ingin mengontrol alokasi memori dengan lebih baik di Go, coba lihat paket yang saya buat
    • Saya akan senang menerima masukan atau jika ada orang lain yang menggunakannya
    • Paket ini mengalokasikan memorinya sendiri secara terpisah dari runtime sehingga sepenuhnya melewati GC
    • Saat melakukan alokasi, ini tidak mengizinkan tipe pointer, tetapi menggantinya dengan tipe Reference[T] yang menyediakan fungsi yang sama
    • Pembebasan memori dilakukan secara manual, jadi tidak bisa bergantung pada garbage collector
    • Di Go, allocator kustom seperti ini umumnya mengarah pada arena yang mendukung grup alokasi yang dibuat dan dimusnahkan bersama
    • Namun, paket offheap bertujuan membangun struktur data besar berumur panjang dengan biaya garbage collection nol
    • Hal-hal seperti cache in-memory skala besar atau database
  • Baru-baru ini saat melakukan performance tuning di Go, saya akhirnya menggunakan desain arena yang sangat mirip untuk memaksimalkan performa

    • Saya menggunakan byte slice sebagai buf dan chunk alih-alih pointer unsafe
    • Saya sudah mencobanya, tetapi tidak lebih cepat dan justru jauh lebih kompleks
    • Saya perlu memeriksanya lagi sebelum 100% yakin
  • Beberapa perbaikan yang mudah

    • Jika mulai dengan slice kecil dan beberapa payload ditambahkan dalam jumlah besar, saya menulis append sendiri yang menaikkan cap lebih agresif sebelum memanggil append bawaan
    • unsafe.String berguna untuk meneruskan string dari byte slice tanpa alokasi
    • Anda harus membaca peringatannya dengan cermat dan memahami apa yang dilakukannya
  • Ini di luar topik, tetapi saya suka minimap di sampingnya

    • Berguna saat menelusuri artikel teknis yang panjang atau merujuk kembali ke sesuatu yang sudah dibaca sebelumnya
    • Saya penasaran bagaimana cara menambahkannya ke situs saya
    • Sangat keren
  • Ringkasan untuk orang yang enggan membaca artikel panjang

    • OP membangun allocator arena di Go menggunakan unsafe untuk mempercepat kerja allocator
    • Ini sangat berguna terutama saat mengalokasikan banyak hal yang dibuat dan dimusnahkan bersama
    • Masalah utamanya adalah GC Go perlu mengetahui layout data, khususnya posisi pointer, agar dapat bekerja dengan benar
    • Jika Anda mengalokasikan byte mentah dengan unsafe.Pointer, GC tidak bisa melihat dengan benar apa yang dirujuk di arena dan dapat membebaskannya secara tidak sengaja
    • Namun, agar ini tetap bekerja selama pointer menunjuk ke hal lain dalam arena yang sama, seluruh arena dipertahankan jika sebagian darinya masih direferensikan
    • Dengan (1) mempertahankan slice (chunk) yang menunjuk ke semua blok memori besar yang diperoleh arena dari sistem dan
    • (2) menggunakan reflect.StructOf untuk membuat tipe baru yang menyertakan field pointer tambahan ke blok-blok ini
    • Jadi ketika GC menemukan pointer ke chunk, ia juga akan menemukan back pointer, menandai arena sebagai hidup, dan mempertahankan slice chunk
    • Lalu diperkenalkan berbagai teknik optimasi menarik untuk menghapus bermacam pemeriksaan internal dan write barrier
  • Terkait: diskusi tentang penambahan "wilayah memori" ke standard library

    • Proposal arena sebelumnya
  • Ini menarik

    • Saya penasaran bagaimana orang yang membangun allocator off-heap atau bergaya arena di Go biasanya menguji atau melakukan benchmark terhadap keamanan memori dan interaksi GC
  • Go memprioritaskan agar ekosistem tidak rusak

    • Ini berarti kita bisa mengasumsikan Hyrum's Law akan melindungi perilaku teramati tertentu dari runtime
    • Jika klaim itu benar, Go sebagai bahasa adalah jalan buntu evolusi
    • Dalam kasus itu saya tidak yakin apakah Go masih menarik
  • Catatan meta singkat

    • Artikel ini benar-benar panjang, jadi saya tidak punya waktu untuk membaca detail latar belakangnya
    • Misalnya, bagian "Mark and Sweep" memakan lebih dari 4 halaman di layar laptop saya
    • Bagian itu baru dimulai setelah lebih dari 5 halaman sejak awal artikel
    • Saya penasaran apakah ini hasil AI yang membantu menulis bagian-bagian tersebut sehingga menjadi terlalu komprehensif
    • Membuat konten itu mudah, tetapi keputusan editorial untuk mempertahankan bagian-bagian penting tidak dilakukan
    • Saya hanya ingin tahu bagian tentang allocator arena, saya tidak butuh tutorial tentang garbage collection