authorgravatar for justus@klausecker.deJustus Klausecker <justus@klausecker.de> 2026-03-25 14:56:56+01:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2026-04-02 23:00:26+02:00
logce3f254526609a9b2088d81070903107c969bb81
treefde83374a04ecdcf5a575be192f11b5180bbe397
parent7f3c1329f83b48125795872075e455e1ac5e971c

std.heap.ArenaAllocator: do not cmpxchg in hot path when it would be a noop

The cmpxchg is there to recover alignment padding that isn't needed (which can only be determined after the fetch-and-add that reserves it as allocated memory). As cmpxchg tends to be a very expensive operation, it is actually faster to introduce an additional branch here that checks if the cmpxchg would be a noop (because all of the reserved alignment padding was in fact necessary) and skips it if that's the case. This does not measurably regress performance if the arena is only accessed by a single thread and yields slight performance benefits for multi-threaded usage. If the arena is commonly used for unaligned allocations, the perf benefits are quite significant. Co-authored-by: Jacob Young <amazingjacob@gmail.com>

1 files changed, 10 insertions(+), 8 deletions(-)

lib/std/heap/ArenaAllocator.zig+10-8
...@@ -358,14 +358,16 @@ fn alloc(ctx: *anyopaque, n: usize, alignment: Alignment, ret_addr: usize) ?[*]u...@@ -358,14 +358,16 @@ fn alloc(ctx: *anyopaque, n: usize, alignment: Alignment, ret_addr: usize) ?[*]u
358 const end_index = @atomicRmw(usize, &node.end_index, .Add, alignable, .acquire); // acquire any memory that may have been freed358 const end_index = @atomicRmw(usize, &node.end_index, .Add, alignable, .acquire); // acquire any memory that may have been freed
359 const aligned_index = alignedIndex(buf.ptr, end_index, alignment);359 const aligned_index = alignedIndex(buf.ptr, end_index, alignment);
360 assert(end_index + alignable >= aligned_index + n);360 assert(end_index + alignable >= aligned_index + n);
361 _ = @cmpxchgStrong(361 if (end_index + alignable != aligned_index + n) {
362 usize,362 _ = @cmpxchgStrong(
363 &node.end_index,363 usize,
364 end_index + alignable,364 &node.end_index,
365 aligned_index + n,365 end_index + alignable,
366 .monotonic, // no need to release alignment padding; there's no one accessing it!366 aligned_index + n,
367 .monotonic,367 .monotonic, // no need to release alignment padding; there's no one accessing it!
368 );368 .monotonic,
369 );
370 }
369371
370 if (aligned_index + n > buf.len) break :first_node .{ node, buf.len };372 if (aligned_index + n > buf.len) break :first_node .{ node, buf.len };
371 return buf[aligned_index..][0..n].ptr;373 return buf[aligned_index..][0..n].ptr;