GoSuda

Kesempatan minum secangkir teh hijau yang akan direbut oleh runtime Go, GreenTea GC

By snowmerak views ...

Mengapa GC baru muncul lagi?

Mengapa GC yang ada sekarang

GC Go saat ini dapat dijelaskan dengan poin-poin berikut.

  • concurrent tri-color mark and sweep
    • Menggunakan tiga warna (putih, abu-abu, hitam) untuk melacak status objek.
      • Putih: objek yang belum dikunjungi
      • Abu-abu: objek yang sudah dikunjungi tetapi belum semua objek anaknya dikunjungi
      • Hitam: objek yang semua objek anaknya sudah dikunjungi
      • Setelah penelusuran selesai, seluruh objek putih dikumpulkan.
    • Menggunakan Write Barrier untuk melindungi memori yang dialokasikan baru selama GC berlangsung.
      • Waktu ketika Write Barrier diaktifkan dan dinonaktifkan merupakan bagian besar dari yang disebut Stop-The-World (selanjutnya disebut STW).
    • Proses ini dilakukan secara konkuren.
  • No Compaction
    • GC Go tidak melakukan compaction.
    • Fragmentasi memori dapat terjadi.
      • Namun, objek berukuran 32KB ke bawah memanfaatkan per-P cache dari alokator memori bahasa Go untuk meminimalkan fragmentasi.
  • Non-Generational
    • GC Go tidak mengelola objek berdasarkan generasi.
    • Semua objek termasuk dalam generasi yang sama.
  • Escape Analysis
    • Go menggunakan Escape Analysis untuk menentukan apakah suatu objek dialokasikan di heap atau di stack.
    • Secara garis besar, jika terjadi dangling pointer atau penggunaan interface, objek tersebut dapat dianggap dialokasikan di heap.

Yang penting

GC Go menelusuri semua objek dimulai dari root lalu melakukan tri-color mark. Proses ini dapat diringkas dalam satu baris sebagai algoritma penelusuran graf konkuren dengan urutan topologi. Namun, setiap objek kemungkinan besar berada di wilayah memori yang berbeda. Secara sederhana, misalkan:

  1. Terdapat dua wilayah memori yang berjarak sekitar 32MB.
  2. Masing-masing wilayah memori tersebut dialokasikan lima objek.
    1. Objek A, B, C berada di wilayah 1
    2. Objek D, E berada di wilayah 2
  3. Urutan referensi objek adalah A -> D -> B -> E -> C.
  4. Ketika GC dimulai, GC mengunjungi A, lalu D, lalu B, lalu E, lalu C.
  5. Pada saat ini, karena A dan D berada di wilayah memori yang berbeda, proses mengunjungi A lalu D memerlukan perpindahan antarwilayah memori.
  6. Dalam proses ini terjadi perpindahan wilayah memori dan pengisian ulang cache line baru, yang disebut biaya lompatan memori (memory jump cost). Sebagian besar waktu CPU aplikasi dapat dialokasikan untuk pekerjaan GC ini.
  7. Biaya ini terus terjadi selama GC berlangsung.

Lalu apa masalahnya?

Perilaku GC seperti ini menjadi masalah serius pada kondisi berikut.

  • Jumlah core banyak dan memori besar
    • GC Go pada dasarnya dijalankan secara konkuren.
    • Namun, jika wilayah memori luas dan objek tersebar, beberapa core akan menelusuri objek di berbagai ruang memori secara bersamaan.
    • Dalam kondisi ini, jika bus antara CPU dan memori tidak cukup lebar, bandwidth memori dapat menjadi bottleneck.
    • Selain itu, karena jarak fisik yang nyata, setiap penelusuran dapat menimbulkan delay yang cukup besar.
  • Banyak objek kecil
    • Jika Anda mengoperasikan tree atau graf yang berkedalaman dalam atau memiliki banyak anak dengan Go, GC harus menelusuri seluruh objek tersebut.
    • Jika objek-objek ini tersebar di wilayah memori yang berbeda, delay akan terjadi karena alasan pertama.
    • Selain itu, karena banyak objek kecil harus ditelusuri seluruhnya, core CPU akan mengalokasikan kapasitas yang cukup besar untuk GC selama GC berjalan.

Untuk mengatasi masalah ini, tim Golang memperkenalkan GreenTea GC.

Kini Anda tidak punya waktu lagi untuk minum teh hijau

Apa yang mengambil teh hijau

Area yang tampaknya menjadi tempat solusi tercepat untuk GC yang ada adalah locality memori. Artinya, objek diletakkan berdekatan satu sama lain sehingga lompatan memori dapat diminimalkan. Namun, pola penulisan kode oleh programmer tidak dapat dipaksakan, dan alokasi objek juga tidak dapat diprediksi bergantung pada workflow.

Mungkin karena itulah, pendekatan yang dipilih tim Golang adalah Memory Span.

Apa itu Memory Span?

Memory span adalah area yang mengalokasikan wilayah memori berukuran relatif besar lalu digunakan untuk mengalokasikan objek-objek kecil. GC baru bernama GreenTea GC melakukan pengumpulan sampah terhadap memory span ini. Detail operasinya hampir sama dengan Tri-Color Mark and Sweep yang ada.

Operasi GreenTea GC

Pertama, GreenTea GC mengalokasikan memory span. Ukurannya, sesuai dengan yang dijelaskan sebelumnya, adalah 8KiB, ukuran yang cukup besar. Di dalamnya dapat dialokasikan objek berukuran maksimal 512 bytes. Ukuran ini kira-kira sedemikian rupa sehingga ukuran node tree atau graf seperti contoh tadi, atau struct yang umumnya lolos ke heap, sulit melampaui batas ini. Objek berukuran 512 bytes ke bawah akan terkumpul di memory span ini setiap kali lolos ke heap, dan ketika memory span penuh, memory span baru dialokasikan.

Ketika GC terjadi, GreenTea GC memasukkan memory span ini ke dalam antrean dan memeriksanya secara berurutan. Dalam proses ini, GreenTea GC menggunakan penjadwalan yang hampir sama dengan model GMP yang ada. Mekanisme seperti work stealing juga sudah diimplementasikan. Pekerja yang mengambil memory span dari antrean akan memeriksa objek di dalam memory span yang ditugaskan kepadanya. Dalam proses ini, Tri-Color Mark and Sweep digunakan dengan cara yang sama.

Apa keuntungannya?

Jika dilihat secara garis besar, perbedaan dengan GC yang ada hanya satu. Satuan yang dijalankan GC dalam satu waktu berubah dari objek menjadi memory span. Karena itu, GC memiliki keuntungan berikut.

  • Locality memori: karena objek terkumpul dalam memory span, lompatan memori saat menelusuri objek diminimalkan. Dengan kata lain, cache CPU dapat dimanfaatkan secara maksimal.
  • Peningkatan performa GC: dengan menjalankan GC per memory span, GC bekerja lebih efisien pada lingkungan multi-core.
  • Optimasi alokasi memori: karena memory span dialokasikan dengan ukuran tetap, overhead alokasi untuk objek kecil berkurang dan kemungkinan terjadinya fragmentasi menurun. Hal ini juga meningkatkan efisiensi dealokasi memori.

Singkatnya, mulai sekarang kita dapat mengalokasikan objek berukuran 512 bytes ke bawah lebih sering dan dengan lebih ringan.

Namun, ada juga beberapa kondisi yang menguntungkan.

  • Struktur tree/graf: fan-out tinggi dan jarang mengalami perubahan
    • B+ tree, Trie, AST (Abstract Syntax Tree)
    • Struktur data yang ramah cache
  • Pemrosesan batch: workflow alokasi besar untuk data kecil
    • Banyak objek kecil yang dihasilkan dari parsing JSON
    • Objek DAO dari result set database

Dalam kasus seperti ini, GreenTea GC dapat menunjukkan performa yang lebih baik dibanding GC yang ada. Namun, hal ini tidak berlaku untuk semua kasus, dan terutama ketika objek tersebar di wilayah memori yang berbeda, peningkatan performa yang dramatis masih sulit diharapkan.

Jadi

Tim Golang tampaknya berniat terus memperbaiki GC dalam jangka panjang. GreenTea GC ini dapat dilihat sebagai perubahan kecil pada area yang terbatas, dan bukan pengganti GC yang ada. Namun, GreenTea GC menjadi contoh menarik yang memperlihatkan masalah yang dihadapi atau diantisipasi tim Golang. Meskipun masalahnya cukup rumit, upaya menyelesaikannya dengan menambahkan konsep yang relatif sederhana juga terasa mengesankan. Secara pribadi, saya menganggapnya sebagai kasus yang menarik.

GreenTea GC adalah fitur eksperimental yang diperkenalkan mulai Go 1.25. Fitur ini dapat diaktifkan dengan menggunakan variabel lingkungan GOEXPERIMENT=greenteagc. Karena fitur ini masih eksperimental, diperlukan pengujian yang memadai sebelum digunakan di lingkungan produksi.

Referensi