🙄
ZigのAllocator まとめ
ZigのAllocator まとめ
最近Zigに入門中。Allocatorについて調べたのでまとめる。
Allocators
Zigでは動的なメモリ管理のパターンが、標準ライブラリでいくつか提供されている。
std.heap.page_allocator
最も一般的なallocator。
OSにページ単位でメモリ要求する。最小単位がページのため、1byteのみ欲しい時など非効率な場合がある。
test "allocation" {
// page_allocatorの生成
const allocator = std.heap.page_allocator;
// 100byteを確保
const memory = try allocator.alloc(u8, 100);
// スコープを出ると同時に確保した100byteを開放
defer allocator.free(memory);
try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}
std.heap.FixedBufferAllocator
固定長のメモリ割当を行う。ヒープ割当は行わないらしい。
カーネルを書く時など、ヒープ割当が望めない場合に利用できる。
実際の割当はコンパイル時に処理される?(スタック上に確保されるのか?)
test "fixed buffer allocator" {
// キャパシティ情報だけ確保?
var buffer: [1000]u8 = undefined;
// ポインタを渡してallocatorを初期化
var fba = std.heap.FixedBufferAllocator.init(&buffer);
// FixedBufferAllocatorの生成
const allocator = fba.allocator();
// 100byteを確保
const memory = try allocator.alloc(u8, 100);
// スコープを出ると同時に確保した100byteを開放 (スタックに確保されるとするなら普通に開放される気がするが...)
defer allocator.free(memory);
try expect(memory.len == 100);
try expect(@TypeOf(memory) == []u8);
}
std.heap.ArenaAllocator
複数回割当を実行できて、1回で開放できる。
開放するときはfree
じゃなくてdeinit
らしい。
実際に割当を行うallocatorは別途指定する。
test "arena allocator" {
// page_allocatorを指定してallocatorを初期化
var arena = std.heap.ArenaAllocator.init(std.heap.page_allocator);
// スコープを抜けると同時に確保したメモリを一括開放
defer arena.deinit();
// ArenaAllAllocatorを生成
const allocator = arena.allocator();
// 複数回メモリを確保
_ = try allocator.alloc(u8, 1);
_ = try allocator.alloc(u8, 10);
_ = try allocator.alloc(u8, 100);
}
std.heap.GeneralPurposeAllocator
複数回のfree
やメモリのリークなどが検知できる。
test "GPA" {
var gpa = std.heap.GeneralPurposeAllocator(.{}){};
// GeneralPurposeAllocatorを生成
const allocator = gpa.allocator();
defer {
// 確保したメモリを開放 (2回目)
const leaked = gpa.deinit();
// 2重開放が戻り値で検知できる
if (leaked) expect(false) catch @panic("TEST FAIL"); //fail test; can't try in defer as defer is executed after we return
}
// 100byteを確保
const bytes = try allocator.alloc(u8, 100);
// 確保したメモリを開放 (1回目)
defer allocator.free(bytes);
}
std.heap.c_allocator
安全性は低いけどパフォーマンスが高いらしい。
Discussion
とてもわかりやすいまとめで、zig入門の良い参考になりました。
FixedBufferAllocatorについて調べてみたのですが、100行程度の実装で特に難しい内容ではなくて、スタック上の固定長割当領域をまるでヒープのように扱えるものでした。
ちなみに
var buffer: [1000]u8 = undefined;
の意味は以下記事がわかりやすくて、要約するとキャパシティは確保したいがメモリの中身は気にしないとコンパイラに伝える、とあり、まさにアロケータで利用することを念頭にした構文のようです。