HN 日本語サマリー

← 一覧へ戻る
プログラミング

ArenaAllocatorsはArrayListと相性が悪い

ArenaAllocators don't play nicely with ArrayLists (openmymind.net)

7 pointsby ibobev1 コメント

要約

ArenaAllocatorは、メモリを解放しても即座にアリーナに返却されるとは限らないため、ArrayListのような動的にサイズが変更されるデータ構造と組み合わせると、メモリ使用量が想定以上に増加する可能性があります。これは、ArrayListの拡張時に古いメモリが解放されるタイミングが、ArenaAllocatorの解放条件を満たさない場合に発生します。

全文翻訳

ArenaAllocatorsはArrayListと相性が悪い Aug 04, 2026 以前、ArenaAllocatorsと、なぜそのfreeをnoopとして扱うべきかについて検討しました。要点は、freeまたはdestroyが実際にメモリをアリーナに返却するためには、2つの条件を満たす必要があるということです: 解放されたメモリは、最後に割り当てられたメモリである必要がある。 解放されたメモリは、Arenaの現在のノードから割り当てられたものでなければならない(これはArenaがどのように成長するかという内部的な詳細です)。 したがって、このコードを考えると: var a = try arena.alloc(u8, 100); var b = try arena.alloc(u8, 100); arena.free(b); arena.free(a); bのメモリはアリーナに返却されると確信できますが、aのメモリについては何も言えません。なぜでしょうか?bは最後に割り当てられたメモリであったため解放され、定義上、アリーナの現在のノードから取得されたものでなければなりません。bが解放された後、aは最初の要件を満たします。それは最後に割り当てられたメモリになります。しかし、bの割り当てが新しいノードを作成したかどうかはわかりません。アリーナの現在のノードは、aが取得されたノードである場合もあれば、そうでない場合もあります。 ノードの件が混乱するように思えるなら、元の投稿を読むか、単に忘れてください。なぜなら、最初の単純なルールが満たされない一般的な状況があるからです。それはArrayList(またはWriter.Allocating)の成長です。 データがArrayListに追加されると、まず既存のメモリを再マッピングしようとします(つまり、インプレースで成長させます)。しかし、それが失敗した場合、より大きな新しいチャンクを割り当て、メモリをコピーし、以前の割り当てを解放します。問題がわかりますか? 以下はarray_list.zigからのコードです: const new_memory = try gpa.alignedAlloc(T, alignment, new_capacity); @memcpy(new_memory[0..self.items.len], self.items); gpa.free(old_memory); ArrayListの成長と他の割り当てをインターリーブしていない場合でも、割り当て+コピー+解放は、old_memoryが最後の割り当てではないことを保証します(最後の割り当てはnew_memoryです)。 解決策はありますか?実際にはありませんが、2つのことができます。第一に、ArrayListのサイズを適切に設定します(例: initCapacityまたはensureTotalCapacityPreciseを使用)。第二に、追加/書き込みと他の割り当てをインターリーブすることを避けます。もしそれができれば、リマップブランチに遭遇する可能性がはるかに高くなり、この問題を回避できます。 どちらも、どのような種類のAllocatorを使用しているかに関わらず、念頭に置くべき良い点ですが、ArenaAllocatorを使用する場合、最悪のケースは約3倍のメモリになります。 これは明白だとわかっていますが、実際に考えたことはありませんでした。おそらく私だけではないでしょう。