zig-alloc - 背景

歴史的経緯

メモリアロケータの進化

  • 初期のアロケータ(1960年代)
- 単純なファーストフィットアルゴリズム - フラグメンテーションが深刻な問題

  • Buddy System(1963年)
- Donald Knuthが提案 - 2のべき乗サイズでのアロケーション - 内部フラグメンテーションと外部フラグメンテーションのトレードオフ

  • Slab Allocator(1994年)
- Jeff Bonwickがsolaris向けに開発 - オブジェクトキャッシング - Linuxカーネルで採用

  • jemalloc(2005年)
- Jason Evansが開発 - FreeBSDのデフォルトアロケータ - スレッドローカルキャッシュ

Zigのアロケータ設計

Zigは独自のアプローチを採用:

// アロケータは明示的に渡される
fn processData(allocator: std.mem.Allocator, data: []const u8) ![]u8 {
    const result = try allocator.alloc(u8, data.len);
    // ...
    return result;
}

コンピュータサイエンス的な意味

アロケーション戦略

戦略 特徴 用途
First Fit シンプル、断片化しやすい 汎用
Best Fit 断片化を抑制、遅い 長期実行
Worst Fit 大きな空き領域を維持 可変サイズ
Next Fit First Fitの改良 汎用

メモリ断片化

外部フラグメンテーション:
[使用中][空き][使用中][空き][使用中]
        ↓
合計空き容量は十分だが、連続した領域がない

内部フラグメンテーション:
[要求: 100B][割当: 128B] → 28B の無駄

アロケータの階層

アプリケーション
     ↓
カスタムアロケータ(プール、アリーナ)
     ↓
汎用アロケータ(malloc)
     ↓
システムアロケータ(mmap, VirtualAlloc)
     ↓
カーネル(ページアロケータ)

実践での活用

ゲーム開発

// フレームアロケータ - 毎フレームリセット
var frame_arena = ArenaAllocator.init(page_allocator);
defer frame_arena.deinit();

while (game_running) {
    // フレーム内のアロケーションは全てアリーナから
    processFrame(frame_arena.allocator());

    // フレーム終了時に一括解放
    frame_arena.reset();
}

組み込みシステム

// 静的バッファのみ使用(ヒープなし)
var static_buffer: [64 * 1024]u8 = undefined;
var fba = FixedBufferAllocator.init(&static_buffer);

// このアロケータは追加のメモリ要求をしない

高性能サーバー

// リクエストごとにアリーナを使用
fn handleRequest(req: *Request) !void {
    var arena = ArenaAllocator.init(gpa);
    defer arena.deinit();

    // リクエスト処理中の全てのアロケーション
    const response = try processRequest(arena.allocator(), req);
    try sendResponse(response);
    // 関数終了時に全て解放
}

実世界とのギャップ

Zigの明示的アロケータ vs 他言語

言語 アロケーション方法
C malloc/free(暗黙的)
C++ new/delete、アロケータ
Rust Box、Vec(暗黙的)
Go GC(完全自動)
Zig 明示的アロケータ渡し

利点と欠点

利点:

  • メモリ使用パターンが明確
  • 最適なアロケータを選択可能
  • テスト時にモックアロケータ使用可能

欠点:

  • コードが冗長になりがち
  • アロケータの伝播が必要
  • 学習曲線が急