global: use DebugAllocator/SmpAllocator instead of GeneralPurposeAllocator/libc allocator

Zig 0.14 introduces the SmpAllocator which claims performance on par with or potentially
better than the glibc allocator. The GeneralPurposeAllocator has also been renamed to
DebugAllocator. This PR updates the global allocator to use DebugAllocator or SmpAllocator
depending on if runtime safety is enabled.
This commit is contained in:
Jeffrey C. Ollie
2025-03-13 11:37:30 -05:00
parent aeada3f1a8
commit 274f9e6c3a

View File

@ -25,9 +25,8 @@ pub var state: GlobalState = undefined;
/// be one of these at any given moment. This is extracted into a dedicated /// be one of these at any given moment. This is extracted into a dedicated
/// struct because it is reused by main and the static C lib. /// struct because it is reused by main and the static C lib.
pub const GlobalState = struct { pub const GlobalState = struct {
const GPA = std.heap.GeneralPurposeAllocator(.{}); debug_allocator: std.heap.DebugAllocator(.{}),
is_debug: bool,
gpa: ?GPA,
alloc: std.mem.Allocator, alloc: std.mem.Allocator,
action: ?cli.Action, action: ?cli.Action,
logging: Logging, logging: Logging,
@ -57,7 +56,8 @@ pub const GlobalState = struct {
// IMPORTANT: this MUST be initialized before any log output because // IMPORTANT: this MUST be initialized before any log output because
// the log function uses the global state. // the log function uses the global state.
self.* = .{ self.* = .{
.gpa = null, .debug_allocator = .init,
.is_debug = true,
.alloc = undefined, .alloc = undefined,
.action = null, .action = null,
.logging = .{ .stderr = {} }, .logging = .{ .stderr = {} },
@ -66,30 +66,20 @@ pub const GlobalState = struct {
}; };
errdefer self.deinit(); errdefer self.deinit();
self.gpa = gpa: { self.alloc, self.is_debug = allocator: {
// Use the libc allocator if it is available because it is WAY switch (builtin.mode) {
// faster than GPA. We only do this in release modes so that we // In modes that have runtime safety checks use DebugAllocator
// can get easy memory leak detection in debug modes. // for it's ability to detect memory shennanigans.
if (builtin.link_libc) { .Debug, .ReleaseSafe => {
if (switch (builtin.mode) { break :allocator .{ self.debug_allocator.allocator(), true };
.ReleaseSafe, .ReleaseFast => true, },
// In all other modes use the global SmpAllocator for performance.
// We also use it if we can detect we're running under .ReleaseFast, .ReleaseSmall => {
// Valgrind since Valgrind only instruments the C allocator break :allocator .{ std.heap.smp_allocator, false };
else => std.valgrind.runningOnValgrind() > 0, },
}) break :gpa null;
} }
break :gpa GPA{};
}; };
self.alloc = if (self.gpa) |*value|
value.allocator()
else if (builtin.link_libc)
std.heap.c_allocator
else
unreachable;
// We first try to parse any action that we may be executing. // We first try to parse any action that we may be executing.
self.action = try cli.Action.detectCLI(self.alloc); self.action = try cli.Action.detectCLI(self.alloc);
@ -187,10 +177,11 @@ pub const GlobalState = struct {
// Flush our crash logs // Flush our crash logs
crash.deinit(); crash.deinit();
if (self.gpa) |*value| { if (self.is_debug) {
// We want to ensure that we deinit the GPA because this is // We want to ensure that we deinit the DebugAllocator because
// the point at which it will output if there were safety violations. // this is the point at which it will output if there were safety
_ = value.deinit(); // violations.
_ = self.debug_allocator.deinit();
} }
} }