Alokator default dan temp

Memilih antara heap dan penyimpanan scratch temporer.

Daftar isi

Setiap alokasi memori di Odin memakai salah satu dari dua alokator yang terpasang pada context, yaitu alokator default untuk data yang bertahan dan temp_allocator untuk memori sementara. Topik ini membahas identitas dan perilaku keduanya, kapan masing-masing dipilih, serta satu program pembanding yang memakai keduanya dalam satu alur kerja pencatatan suhu. Setelah topik ini, keputusan menaruh data di heap atau di penyimpanan sementara dapat diambil berdasarkan umur pakai datanya.

Alokator default heap dan identitasnya#

Alokator default adalah alokator heap yang disediakan runtime Odin, yaitu penyimpanan dinamis yang meminta blok memori baru ke sistem operasi dan mencatat setiap blok yang dikeluarkannya. Field context.allocator menunjuk ke alokator heap itu sejak proc main dimulai, dan setiap make tanpa argumen alokator mendarat di situ. Identitas alokator ini dapat diperiksa karena proc alokatornya bernama default_allocator_proc di paket base:runtime, sehingga perbandingan context.allocator.procedure == runtime.default_allocator_proc menghasilkan true pada program yang belum mengganti alokator. Field context.temp_allocator juga sudah terpasang sejak awal dengan arena bawaan runtime berukuran 4 megabyte, sesuai konstanta DEFAULT_TEMP_ALLOCATOR_BACKING_SIZE pada sumber runtime. Dua fasilitas ini siap dipakai tanpa inisialisasi apa pun, sehingga program pertama yang memanggil make sudah memakai keduanya secara benar.

Perbandingan identitas itu berguna ketika program perlu memastikan alokator mana yang sedang aktif sebelum memilih strategi penanganan memori. Proc alokator tersimpan sebagai nilai proc biasa pada field procedure, sehingga perbandingannya berupa perbandingan alamat proc dan tidak memicu alokasi apa pun. Alokasi pada heap bawaan mengembalikan blok yang diisi nol karena mode yang dipakai make adalah .Alloc, sedangkan blok yang dilepaskan melalui delete kembali ke alokator penopang heap bawaan runtime.

Alokator temp dan cara meresetnya#

temp_allocator berperilaku seperti arena, yaitu alokator yang hanya menaikkan pointer dan tidak mencatat alokasi satu per satu. Alokasi dari temp sangat murah, tetapi harganya adalah disiplin umur pakai: seluruh isi temp dibuang sekaligus ketika free_all(context.temp_allocator) dipanggil, dan tidak ada cara melepas satu alokasi temp secara terpisah. Pustaka core memakai temp untuk hasil yang berumur sangat pendek, misalnya fmt.tprintf yang mengalokasikan string hasil format dari temp_allocator, berbeda dengan fmt.aprintf yang menerima alokator secara eksplisit. Pengisian temp menaikkan pemakaian hingga arena bawaannya penuh, dan arena tersebut tumbuh dengan meminjam memori dari alokator penopang. Karena itu, free_all tetap wajib dipanggil secara berkala pada program berumur panjang agar pemakaian memori tidak merambat naik. Pola yang lazim adalah satu panggilan free_all per putaran kerja, misalnya di akhir frame atau di akhir satu batch pemrosesan.

Kapan heap dan temp dipilih#

Kriteria penentuannya adalah umur pakai data, bukan ukurannya. Tabel berikut merangkum pemetaan itu beserta cara melepaskan masing-masing.

AspekAlokator default (heap)temp_allocator (scratch)
Umur pakaiBertahan melewati lingkup dan putaran kerjaHanya di dalam satu lingkup atau satu putaran
Contoh pemakaianCatatan sensor, struktur data inti programString hasil format, buffer cetak sementara
Cara melepaskandelete atau free per alokasifree_all untuk seluruh isi sekaligus
Biaya alokasiLebih mahal karena pencatatan per blokSangat murah, hanya menaikkan pointer
Risiko utamaKebocoran bila lupa dilepaskanPointer mati bila data lolos dari lingkup

Pola pemakaian yang sehat memisahkan keduanya pada batas yang sama. Satu putaran kerja, misalnya satu frame game atau satu batch pembacaan sensor, mengalokasikan seluruh data kerjanya dari temp, memproses atau mencetak dengan data itu, lalu mengosongkan temp pada akhir putaran. Data yang lolos dari putaran, seperti hasil yang dikumpulkan, dialihkan ke heap pada saat lahir. Dengan pemisahan itu, pemakaian temp kembali ke nol di setiap akhir putaran, sedangkan pertumbuhan heap hanya mengikuti data yang memang diminta untuk bertahan.

Program pembanding laporan suhu#

Program berikut mengoperasikan keduanya pada satu alur kerja: string laporan dicetak dari temp, sedangkan catatan historis tersimpan di heap. Baris terakhir membuktikan bahwa reset temp tidak menyentuh heap.

odin main.odin
package main

import "base:runtime"
import "core:fmt"

// catatan yang harus bertahan sampai akhir program
catatan_suhu: [dynamic]f32

tambah_catatan :: proc(nilai: f32) {
        append(&catatan_suhu, nilai)
}

main :: proc() {
        fmt.println("alokator default adalah heap runtime:", context.allocator.procedure == runtime.default_allocator_proc)
        fmt.println("alokator temp bawaan tersedia:", context.temp_allocator.data != nil)

        suhu := [5]f32{28.4, 29.1, 30.2, 28.9, 29.7}

        for nilai in suhu {
                // tprintf mengalokasikan string laporan dari context.temp_allocator
                // string ini hanya hidup selama baris laporan dicetak
                laporan := fmt.tprintf("laporan suhu: %.1f derajat", nilai)
                fmt.println(laporan)

                // data historis dialokasikan dari context.allocator (heap)
                tambah_catatan(nilai)
        }

        // seluruh memori temp dikosongkan dalam satu langkah
        err := free_all(context.temp_allocator)
        fmt.println("error free_all memori temp:", err)

        // heap tidak terpengaruh oleh reset memori temp
        fmt.println("jumlah catatan di heap:", len(catatan_suhu))

        total := f32(0)
        for nilai in catatan_suhu {
                total += nilai
        }
        fmt.printf("rata-rata suhu: %.2f derajat\n", total / f32(len(catatan_suhu)))
}
bash
# sumberkan lingkungan sandbox jalur odin bila belum tersedia
[ -f /home/kal/my-project/tools/env.sh ] && source /home/kal/my-project/tools/env.sh
# jalankan program dari dalam folder topik
odin run main.odin -file
text Keluaran
alokator default adalah heap runtime: true
alokator temp bawaan tersedia: true
laporan suhu: 28.4 derajat
laporan suhu: 29.1 derajat
laporan suhu: 30.2 derajat
laporan suhu: 28.9 derajat
laporan suhu: 29.7 derajat
error free_all memori temp: None
jumlah catatan di heap: 5
rata-rata suhu: 29.26 derajat

Dua baris pertama membuktikan identitas keduanya. Setiap putaran perulangan mengalokasikan string laporan dari temp dan menambah satu nilai ke dynamic array di heap. Penambahan pada dynamic array dialokasikan dari context.allocator karena append memakai alokator context. Panggilan free_all mengembalikan None karena reset memori temp berhasil, sedangkan catatan di heap tetap utuh sehingga rata-rata bisa dihitung setelahnya.

Perhatikan juga apa yang tidak dilakukan program ini. String laporan tidak dilepaskan satu per satu karena lahir di temp dan dibuang oleh free_all sekaligus, sedangkan catatan_suhu sengaja bertahan sampai akhir program dan pelepasannya ditangani pada bagian akhir proses. Pembagian kerja dalam satu putaran seperti ini adalah bentuk paling sederhana dari manajemen memori per frame, dan menjadi dasar pemanfaatan arena yang lebih menyeluruh. Pengukuran identitas pada dua baris pertama sekaligus membuktikan bahwa tidak ada inisialisasi alokator yang perlu ditulis.

Kesalahan umum#

Kesalahan pertama adalah menyimpan hasil fmt.tprintf sampai melewati titik reset. String itu lahir di dalam area yang dikosongkan oleh free_all, sehingga menyimpannya di field struct berarti menyimpan pointer ke memori yang tidak lagi sah. Nilai yang harus bertahan disalin ke alokator default terlebih dahulu, misalnya dengan strings.clone yang mengalokasikan salinannya dari context.allocator, atau dibuat dengan fmt.aprintf yang menerima alokator secara eksplisit.

Kesalahan kedua adalah mengalokasikan data jangka panjang di temp dengan menuliskan context.temp_allocator sebagai argumen make. Alokasinya berhasil dan pengujian di lingkungan pengembangan tampak baik, tetapi pemakaian pertama setelah free_all menyentuh memori yang sudah dikosongkan. Kriteria pilihannya tetap sama, yaitu umur pakai data, dan argumen alokator eksplisit pada make hanya dipakai untuk data yang memang mati bersama lingkunya.

Ringkasan#

  • context.allocator bawaan adalah alokator heap runtime dengan proc runtime.default_allocator_proc, sedangkan context.temp_allocator adalah arena bawaan berukuran 4 megabyte.
  • Memori temp hanya dikosongkan sekaligus melalui free_all, sehingga hanya cocok untuk data yang tidak lolos dari lingkup atau putaran kerja.
  • Data yang bertahan melewati lingkup dialokasikan dari heap dan dilepaskan per alokasi dengan delete.
  • fmt.tprintf memakai temp_allocator, sedangkan fmt.aprintf menerima alokator secara eksplisit.

Referensi#