authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-03 22:34:29-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-06 14:23:23-08:00
log36e9b0f0261a5421f22dbf17f513a8c2f685c5d3
tree62e7d47f13e388f4db799b597a7617b80c2d9d48
parent2c5113f6d1ee9d5041d9d79cfbe13e6bea2aff56

std.mem.Allocator: keep the undefined memset

Reversal on the decision: the Allocator interface is the correct place for the memset to undefined because it allows Allocator implementations to bypass the interface and use a backing allocator directly, skipping the performance penalty of memsetting the entire allocation, which may be very large, as well as having valuable zeroes on them. closes #4298

2 files changed, 27 insertions(+), 19 deletions(-)

lib/std/heap/general_purpose_allocator.zig+23-12
...@@ -157,6 +157,9 @@ pub const Config = struct {...@@ -157,6 +157,9 @@ pub const Config = struct {
157157
158 /// Enables emitting info messages with the size and address of every allocation.158 /// Enables emitting info messages with the size and address of every allocation.
159 verbose_log: bool = false,159 verbose_log: bool = false,
160
161 /// Tell whether the backing allocator returns already-zeroed memory.
162 backing_allocator_zeroes: bool = true,
160};163};
161164
162pub const Check = enum { ok, leak };165pub const Check = enum { ok, leak };
...@@ -179,7 +182,8 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -179,7 +182,8 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
179182
180 const Self = @This();183 const Self = @This();
181184
182 /// The initial state of a `GeneralPurposeAllocator`, containing no allocations and backed by the system page allocator.185 /// The initial state of a `GeneralPurposeAllocator`, containing no
186 /// allocations and backed by the system page allocator.
183 pub const init: Self = .{187 pub const init: Self = .{
184 .backing_allocator = std.heap.page_allocator,188 .backing_allocator = std.heap.page_allocator,
185 .buckets = [1]Buckets{.{}} ** small_bucket_count,189 .buckets = [1]Buckets{.{}} ** small_bucket_count,
...@@ -508,7 +512,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -508,7 +512,7 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
508 fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void {512 fn collectStackTrace(first_trace_addr: usize, addresses: *[stack_n]usize) void {
509 if (stack_n == 0) return;513 if (stack_n == 0) return;
510 @memset(addresses, 0);514 @memset(addresses, 0);
511 var stack_trace = StackTrace{515 var stack_trace: StackTrace = .{
512 .instruction_addresses = addresses,516 .instruction_addresses = addresses,
513 .index = 0,517 .index = 0,
514 };518 };
...@@ -1092,22 +1096,29 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {...@@ -1092,22 +1096,29 @@ pub fn GeneralPurposeAllocator(comptime config: Config) type {
1092 }1096 }
10931097
1094 fn createBucket(self: *Self, size_class: usize) Error!*BucketHeader {1098 fn createBucket(self: *Self, size_class: usize) Error!*BucketHeader {
1095 const page = try self.backing_allocator.alignedAlloc(u8, page_size, page_size);1099 const alignment: mem.Alignment = .fromByteUnits(page_size);
1096 errdefer self.backing_allocator.free(page);1100 const page = self.backing_allocator.rawAlloc(page_size, alignment, @returnAddress()) orelse
1101 return error.OutOfMemory;
1102 errdefer self.backing_allocator.rawFree(page[0..page_size], alignment, @returnAddress());
10971103
1098 const bucket_size = bucketSize(size_class);1104 const bucket_size = bucketSize(size_class);
1099 const bucket_bytes = try self.backing_allocator.alignedAlloc(u8, @alignOf(BucketHeader), bucket_size);1105 const header_align: mem.Alignment = .fromByteUnits(@alignOf(BucketHeader));
1100 const ptr: *BucketHeader = @ptrCast(bucket_bytes.ptr);1106 const ptr: *BucketHeader = @alignCast(@ptrCast(self.backing_allocator.rawAlloc(
1107 bucket_size,
1108 header_align,
1109 @returnAddress(),
1110 ) orelse return error.OutOfMemory));
1101 ptr.* = .{1111 ptr.* = .{
1102 .page = page.ptr,1112 .page = @alignCast(page),
1103 .alloc_cursor = 0,1113 .alloc_cursor = 0,
1104 .used_count = 0,1114 .used_count = 0,
1105 };1115 };
1106 // Set the used bits to all zeroes1116 if (!config.backing_allocator_zeroes) {
1107 @memset(@as([*]u8, @as(*[1]u8, ptr.usedBits(0)))[0..usedBitsCount(size_class)], 0);1117 @memset(@as([*]u8, @as(*[1]u8, ptr.usedBits(0)))[0..usedBitsCount(size_class)], 0);
1108 if (config.safety) {1118 if (config.safety) {
1109 // Set the requested sizes to zeroes1119 // Set the requested sizes to zeroes
1110 @memset(mem.sliceAsBytes(ptr.requestedSizes(size_class)), 0);1120 @memset(mem.sliceAsBytes(ptr.requestedSizes(size_class)), 0);
1121 }
1111 }1122 }
1112 return ptr;1123 return ptr;
1113 }1124 }
lib/std/mem/Allocator.zig+4-7
...@@ -128,25 +128,25 @@ pub fn noFree(...@@ -128,25 +128,25 @@ pub fn noFree(
128}128}
129129
130/// This function is not intended to be called except from within the130/// This function is not intended to be called except from within the
131/// implementation of an Allocator131/// implementation of an `Allocator`.
132pub inline fn rawAlloc(a: Allocator, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 {132pub inline fn rawAlloc(a: Allocator, len: usize, alignment: Alignment, ret_addr: usize) ?[*]u8 {
133 return a.vtable.alloc(a.ptr, len, alignment, ret_addr);133 return a.vtable.alloc(a.ptr, len, alignment, ret_addr);
134}134}
135135
136/// This function is not intended to be called except from within the136/// This function is not intended to be called except from within the
137/// implementation of an Allocator.137/// implementation of an `Allocator`.
138pub inline fn rawResize(a: Allocator, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool {138pub inline fn rawResize(a: Allocator, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) bool {
139 return a.vtable.resize(a.ptr, memory, alignment, new_len, ret_addr);139 return a.vtable.resize(a.ptr, memory, alignment, new_len, ret_addr);
140}140}
141141
142/// This function is not intended to be called except from within the142/// This function is not intended to be called except from within the
143/// implementation of an Allocator.143/// implementation of an `Allocator`.
144pub inline fn rawRemap(a: Allocator, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {144pub inline fn rawRemap(a: Allocator, memory: []u8, alignment: Alignment, new_len: usize, ret_addr: usize) ?[*]u8 {
145 return a.vtable.remap(a.ptr, memory, alignment, new_len, ret_addr);145 return a.vtable.remap(a.ptr, memory, alignment, new_len, ret_addr);
146}146}
147147
148/// This function is not intended to be called except from within the148/// This function is not intended to be called except from within the
149/// implementation of an Allocator149/// implementation of an `Allocator`.
150pub inline fn rawFree(a: Allocator, memory: []u8, alignment: Alignment, ret_addr: usize) void {150pub inline fn rawFree(a: Allocator, memory: []u8, alignment: Alignment, ret_addr: usize) void {
151 return a.vtable.free(a.ptr, memory, alignment, ret_addr);151 return a.vtable.free(a.ptr, memory, alignment, ret_addr);
152}152}
...@@ -271,7 +271,6 @@ fn allocBytesWithAlignment(self: Allocator, comptime alignment: u29, byte_count:...@@ -271,7 +271,6 @@ fn allocBytesWithAlignment(self: Allocator, comptime alignment: u29, byte_count:
271 }271 }
272272
273 const byte_ptr = self.rawAlloc(byte_count, .fromByteUnits(alignment), return_address) orelse return Error.OutOfMemory;273 const byte_ptr = self.rawAlloc(byte_count, .fromByteUnits(alignment), return_address) orelse return Error.OutOfMemory;
274 // TODO: https://github.com/ziglang/zig/issues/4298
275 @memset(byte_ptr[0..byte_count], undefined);274 @memset(byte_ptr[0..byte_count], undefined);
276 return @alignCast(byte_ptr);275 return @alignCast(byte_ptr);
277}276}
...@@ -391,7 +390,6 @@ pub fn reallocAdvanced(...@@ -391,7 +390,6 @@ pub fn reallocAdvanced(
391 return error.OutOfMemory;390 return error.OutOfMemory;
392 const copy_len = @min(byte_count, old_byte_slice.len);391 const copy_len = @min(byte_count, old_byte_slice.len);
393 @memcpy(new_mem[0..copy_len], old_byte_slice[0..copy_len]);392 @memcpy(new_mem[0..copy_len], old_byte_slice[0..copy_len]);
394 // TODO https://github.com/ziglang/zig/issues/4298
395 @memset(old_byte_slice, undefined);393 @memset(old_byte_slice, undefined);
396 self.rawFree(old_byte_slice, .fromByteUnits(Slice.alignment), return_address);394 self.rawFree(old_byte_slice, .fromByteUnits(Slice.alignment), return_address);
397395
...@@ -408,7 +406,6 @@ pub fn free(self: Allocator, memory: anytype) void {...@@ -408,7 +406,6 @@ pub fn free(self: Allocator, memory: anytype) void {
408 const bytes_len = bytes.len + if (Slice.sentinel() != null) @sizeOf(Slice.child) else 0;406 const bytes_len = bytes.len + if (Slice.sentinel() != null) @sizeOf(Slice.child) else 0;
409 if (bytes_len == 0) return;407 if (bytes_len == 0) return;
410 const non_const_ptr = @constCast(bytes.ptr);408 const non_const_ptr = @constCast(bytes.ptr);
411 // TODO: https://github.com/ziglang/zig/issues/4298
412 @memset(non_const_ptr[0..bytes_len], undefined);409 @memset(non_const_ptr[0..bytes_len], undefined);
413 self.rawFree(non_const_ptr[0..bytes_len], .fromByteUnits(Slice.alignment), @returnAddress());410 self.rawFree(non_const_ptr[0..bytes_len], .fromByteUnits(Slice.alignment), @returnAddress());
414}411}