authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-05 19:28:48-08:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-02-06 14:23:23-08:00
log960190643ab4fe2227cbc7ab69a3907bf6722a6e
tree2f010a0ee8dbffb256f454e705d36d17b363811a
parentcd99ab32294a3c22f09615c93d611593a2887cc3

std.heap.DebugAllocator: make page size configurable


1 files changed, 41 insertions(+), 29 deletions(-)

lib/std/heap/debug_allocator.zig+41-29
......@@ -90,15 +90,12 @@ const mem = std.mem;
9090const Allocator = std.mem.Allocator;
9191const StackTrace = std.builtin.StackTrace;
9292
93const page_size: usize = @max(std.heap.page_size_max, switch (builtin.os.tag) {
93const default_page_size: usize = @max(std.heap.page_size_max, switch (builtin.os.tag) {
9494 .windows => 64 * 1024, // Makes `std.heap.PageAllocator` take the happy path.
9595 .wasi => 64 * 1024, // Max alignment supported by `std.heap.WasmAllocator`.
9696 else => 128 * 1024, // Avoids too many active mappings when `page_size_max` is low.
9797});
98const page_align: mem.Alignment = .fromByteUnits(page_size);
9998
100/// Integer type for pointing to slots in a small allocation
101const SlotIndex = std.meta.Int(.unsigned, math.log2(page_size) + 1);
10299const Log2USize = std.math.Log2Int(usize);
103100
104101const default_sys_stack_trace_frames: usize = if (std.debug.sys_can_stack_trace) 6 else 0;
......@@ -159,6 +156,12 @@ pub const Config = struct {
159156 /// Magic value that distinguishes allocations owned by this allocator from
160157 /// other regions of memory.
161158 canary: usize = @truncate(0x9232a6ff85dff10f),
159
160 /// The size of allocations requested from the backing allocator for
161 /// subdividing into slots for small allocations.
162 ///
163 /// Must be a power of two.
164 page_size: usize = default_page_size,
162165};
163166
164167/// Default initialization of this struct is deprecated; use `.init` instead.
......@@ -184,6 +187,15 @@ pub fn DebugAllocator(comptime config: Config) type {
184187 break :init result;
185188 };
186189
190 comptime {
191 assert(math.isPowerOfTwo(page_size));
192 }
193
194 const page_size = config.page_size;
195 const page_align: mem.Alignment = .fromByteUnits(page_size);
196 /// Integer type for pointing to slots in a small allocation
197 const SlotIndex = std.meta.Int(.unsigned, math.log2(page_size) + 1);
198
187199 const total_requested_bytes_init = if (config.enable_memory_limit) @as(usize, 0) else {};
188200 const requested_memory_limit_init = if (config.enable_memory_limit) @as(usize, math.maxInt(usize)) else {};
189201
......@@ -1138,17 +1150,17 @@ test "large object - grow" {
11381150 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
11391151 const allocator = gpa.allocator();
11401152
1141 var slice1 = try allocator.alloc(u8, page_size * 2 - 20);
1153 var slice1 = try allocator.alloc(u8, default_page_size * 2 - 20);
11421154 defer allocator.free(slice1);
11431155
11441156 const old = slice1;
1145 slice1 = try allocator.realloc(slice1, page_size * 2 - 10);
1157 slice1 = try allocator.realloc(slice1, default_page_size * 2 - 10);
11461158 try std.testing.expect(slice1.ptr == old.ptr);
11471159
1148 slice1 = try allocator.realloc(slice1, page_size * 2);
1160 slice1 = try allocator.realloc(slice1, default_page_size * 2);
11491161 try std.testing.expect(slice1.ptr == old.ptr);
11501162
1151 slice1 = try allocator.realloc(slice1, page_size * 2 + 1);
1163 slice1 = try allocator.realloc(slice1, default_page_size * 2 + 1);
11521164}
11531165
11541166test "realloc small object to large object" {
......@@ -1162,7 +1174,7 @@ test "realloc small object to large object" {
11621174 slice[60] = 0x34;
11631175
11641176 // This requires upgrading to a large object
1165 const large_object_size = page_size * 2 + 50;
1177 const large_object_size = default_page_size * 2 + 50;
11661178 slice = try allocator.realloc(slice, large_object_size);
11671179 try std.testing.expect(slice[0] == 0x12);
11681180 try std.testing.expect(slice[60] == 0x34);
......@@ -1173,22 +1185,22 @@ test "shrink large object to large object" {
11731185 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
11741186 const allocator = gpa.allocator();
11751187
1176 var slice = try allocator.alloc(u8, page_size * 2 + 50);
1188 var slice = try allocator.alloc(u8, default_page_size * 2 + 50);
11771189 defer allocator.free(slice);
11781190 slice[0] = 0x12;
11791191 slice[60] = 0x34;
11801192
1181 if (!allocator.resize(slice, page_size * 2 + 1)) return;
1182 slice = slice.ptr[0 .. page_size * 2 + 1];
1193 if (!allocator.resize(slice, default_page_size * 2 + 1)) return;
1194 slice = slice.ptr[0 .. default_page_size * 2 + 1];
11831195 try std.testing.expect(slice[0] == 0x12);
11841196 try std.testing.expect(slice[60] == 0x34);
11851197
1186 try std.testing.expect(allocator.resize(slice, page_size * 2 + 1));
1187 slice = slice[0 .. page_size * 2 + 1];
1198 try std.testing.expect(allocator.resize(slice, default_page_size * 2 + 1));
1199 slice = slice[0 .. default_page_size * 2 + 1];
11881200 try std.testing.expect(slice[0] == 0x12);
11891201 try std.testing.expect(slice[60] == 0x34);
11901202
1191 slice = try allocator.realloc(slice, page_size * 2);
1203 slice = try allocator.realloc(slice, default_page_size * 2);
11921204 try std.testing.expect(slice[0] == 0x12);
11931205 try std.testing.expect(slice[60] == 0x34);
11941206}
......@@ -1204,13 +1216,13 @@ test "shrink large object to large object with larger alignment" {
12041216 var fba = std.heap.FixedBufferAllocator.init(&debug_buffer);
12051217 const debug_allocator = fba.allocator();
12061218
1207 const alloc_size = page_size * 2 + 50;
1219 const alloc_size = default_page_size * 2 + 50;
12081220 var slice = try allocator.alignedAlloc(u8, 16, alloc_size);
12091221 defer allocator.free(slice);
12101222
12111223 const big_alignment: usize = switch (builtin.os.tag) {
1212 .windows => page_size * 32, // Windows aligns to 64K.
1213 else => page_size * 2,
1224 .windows => default_page_size * 32, // Windows aligns to 64K.
1225 else => default_page_size * 2,
12141226 };
12151227 // This loop allocates until we find a page that is not aligned to the big
12161228 // alignment. Then we shrink the allocation after the loop, but increase the
......@@ -1236,7 +1248,7 @@ test "realloc large object to small object" {
12361248 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
12371249 const allocator = gpa.allocator();
12381250
1239 var slice = try allocator.alloc(u8, page_size * 2 + 50);
1251 var slice = try allocator.alloc(u8, default_page_size * 2 + 50);
12401252 defer allocator.free(slice);
12411253 slice[0] = 0x12;
12421254 slice[16] = 0x34;
......@@ -1282,18 +1294,18 @@ test "realloc large object to larger alignment" {
12821294 var fba = std.heap.FixedBufferAllocator.init(&debug_buffer);
12831295 const debug_allocator = fba.allocator();
12841296
1285 var slice = try allocator.alignedAlloc(u8, 16, page_size * 2 + 50);
1297 var slice = try allocator.alignedAlloc(u8, 16, default_page_size * 2 + 50);
12861298 defer allocator.free(slice);
12871299
12881300 const big_alignment: usize = switch (builtin.os.tag) {
1289 .windows => page_size * 32, // Windows aligns to 64K.
1290 else => page_size * 2,
1301 .windows => default_page_size * 32, // Windows aligns to 64K.
1302 else => default_page_size * 2,
12911303 };
12921304 // This loop allocates until we find a page that is not aligned to the big alignment.
12931305 var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator);
12941306 while (mem.isAligned(@intFromPtr(slice.ptr), big_alignment)) {
12951307 try stuff_to_free.append(slice);
1296 slice = try allocator.alignedAlloc(u8, 16, page_size * 2 + 50);
1308 slice = try allocator.alignedAlloc(u8, 16, default_page_size * 2 + 50);
12971309 }
12981310 while (stuff_to_free.popOrNull()) |item| {
12991311 allocator.free(item);
......@@ -1301,15 +1313,15 @@ test "realloc large object to larger alignment" {
13011313 slice[0] = 0x12;
13021314 slice[16] = 0x34;
13031315
1304 slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 100);
1316 slice = try allocator.reallocAdvanced(slice, 32, default_page_size * 2 + 100);
13051317 try std.testing.expect(slice[0] == 0x12);
13061318 try std.testing.expect(slice[16] == 0x34);
13071319
1308 slice = try allocator.reallocAdvanced(slice, 32, page_size * 2 + 25);
1320 slice = try allocator.reallocAdvanced(slice, 32, default_page_size * 2 + 25);
13091321 try std.testing.expect(slice[0] == 0x12);
13101322 try std.testing.expect(slice[16] == 0x34);
13111323
1312 slice = try allocator.reallocAdvanced(slice, big_alignment, page_size * 2 + 100);
1324 slice = try allocator.reallocAdvanced(slice, big_alignment, default_page_size * 2 + 100);
13131325 try std.testing.expect(slice[0] == 0x12);
13141326 try std.testing.expect(slice[16] == 0x34);
13151327}
......@@ -1327,7 +1339,7 @@ test "large object rejects shrinking to small" {
13271339 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
13281340 const allocator = gpa.allocator();
13291341
1330 var slice = try allocator.alloc(u8, page_size * 2 + 50);
1342 var slice = try allocator.alloc(u8, default_page_size * 2 + 50);
13311343 defer allocator.free(slice);
13321344 slice[0] = 0x12;
13331345 slice[3] = 0x34;
......@@ -1379,8 +1391,8 @@ test "large allocations count requested size not backing size" {
13791391 var gpa: DebugAllocator(.{ .enable_memory_limit = true }) = .{};
13801392 const allocator = gpa.allocator();
13811393
1382 var buf = try allocator.alignedAlloc(u8, 1, page_size + 1);
1383 try std.testing.expectEqual(page_size + 1, gpa.total_requested_bytes);
1394 var buf = try allocator.alignedAlloc(u8, 1, default_page_size + 1);
1395 try std.testing.expectEqual(default_page_size + 1, gpa.total_requested_bytes);
13841396 buf = try allocator.realloc(buf, 1);
13851397 try std.testing.expectEqual(1, gpa.total_requested_bytes);
13861398 buf = try allocator.realloc(buf, 2);