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;...@@ -90,15 +90,12 @@ const mem = std.mem;
90const Allocator = std.mem.Allocator;90const Allocator = std.mem.Allocator;
91const StackTrace = std.builtin.StackTrace;91const 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) {
94 .windows => 64 * 1024, // Makes `std.heap.PageAllocator` take the happy path.94 .windows => 64 * 1024, // Makes `std.heap.PageAllocator` take the happy path.
95 .wasi => 64 * 1024, // Max alignment supported by `std.heap.WasmAllocator`.95 .wasi => 64 * 1024, // Max alignment supported by `std.heap.WasmAllocator`.
96 else => 128 * 1024, // Avoids too many active mappings when `page_size_max` is low.96 else => 128 * 1024, // Avoids too many active mappings when `page_size_max` is low.
97});97});
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);
102const Log2USize = std.math.Log2Int(usize);99const Log2USize = std.math.Log2Int(usize);
103100
104const default_sys_stack_trace_frames: usize = if (std.debug.sys_can_stack_trace) 6 else 0;101const default_sys_stack_trace_frames: usize = if (std.debug.sys_can_stack_trace) 6 else 0;
...@@ -159,6 +156,12 @@ pub const Config = struct {...@@ -159,6 +156,12 @@ pub const Config = struct {
159 /// Magic value that distinguishes allocations owned by this allocator from156 /// Magic value that distinguishes allocations owned by this allocator from
160 /// other regions of memory.157 /// other regions of memory.
161 canary: usize = @truncate(0x9232a6ff85dff10f),158 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,
162};165};
163166
164/// Default initialization of this struct is deprecated; use `.init` instead.167/// Default initialization of this struct is deprecated; use `.init` instead.
...@@ -184,6 +187,15 @@ pub fn DebugAllocator(comptime config: Config) type {...@@ -184,6 +187,15 @@ pub fn DebugAllocator(comptime config: Config) type {
184 break :init result;187 break :init result;
185 };188 };
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
187 const total_requested_bytes_init = if (config.enable_memory_limit) @as(usize, 0) else {};199 const total_requested_bytes_init = if (config.enable_memory_limit) @as(usize, 0) else {};
188 const requested_memory_limit_init = if (config.enable_memory_limit) @as(usize, math.maxInt(usize)) else {};200 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" {...@@ -1138,17 +1150,17 @@ test "large object - grow" {
1138 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");1150 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
1139 const allocator = gpa.allocator();1151 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);
1142 defer allocator.free(slice1);1154 defer allocator.free(slice1);
11431155
1144 const old = slice1;1156 const old = slice1;
1145 slice1 = try allocator.realloc(slice1, page_size * 2 - 10);1157 slice1 = try allocator.realloc(slice1, default_page_size * 2 - 10);
1146 try std.testing.expect(slice1.ptr == old.ptr);1158 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);
1149 try std.testing.expect(slice1.ptr == old.ptr);1161 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);
1152}1164}
11531165
1154test "realloc small object to large object" {1166test "realloc small object to large object" {
...@@ -1162,7 +1174,7 @@ test "realloc small object to large object" {...@@ -1162,7 +1174,7 @@ test "realloc small object to large object" {
1162 slice[60] = 0x34;1174 slice[60] = 0x34;
11631175
1164 // This requires upgrading to a large object1176 // 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;
1166 slice = try allocator.realloc(slice, large_object_size);1178 slice = try allocator.realloc(slice, large_object_size);
1167 try std.testing.expect(slice[0] == 0x12);1179 try std.testing.expect(slice[0] == 0x12);
1168 try std.testing.expect(slice[60] == 0x34);1180 try std.testing.expect(slice[60] == 0x34);
...@@ -1173,22 +1185,22 @@ test "shrink large object to large object" {...@@ -1173,22 +1185,22 @@ test "shrink large object to large object" {
1173 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");1185 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
1174 const allocator = gpa.allocator();1186 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);
1177 defer allocator.free(slice);1189 defer allocator.free(slice);
1178 slice[0] = 0x12;1190 slice[0] = 0x12;
1179 slice[60] = 0x34;1191 slice[60] = 0x34;
11801192
1181 if (!allocator.resize(slice, page_size * 2 + 1)) return;1193 if (!allocator.resize(slice, default_page_size * 2 + 1)) return;
1182 slice = slice.ptr[0 .. page_size * 2 + 1];1194 slice = slice.ptr[0 .. default_page_size * 2 + 1];
1183 try std.testing.expect(slice[0] == 0x12);1195 try std.testing.expect(slice[0] == 0x12);
1184 try std.testing.expect(slice[60] == 0x34);1196 try std.testing.expect(slice[60] == 0x34);
11851197
1186 try std.testing.expect(allocator.resize(slice, page_size * 2 + 1));1198 try std.testing.expect(allocator.resize(slice, default_page_size * 2 + 1));
1187 slice = slice[0 .. page_size * 2 + 1];1199 slice = slice[0 .. default_page_size * 2 + 1];
1188 try std.testing.expect(slice[0] == 0x12);1200 try std.testing.expect(slice[0] == 0x12);
1189 try std.testing.expect(slice[60] == 0x34);1201 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);
1192 try std.testing.expect(slice[0] == 0x12);1204 try std.testing.expect(slice[0] == 0x12);
1193 try std.testing.expect(slice[60] == 0x34);1205 try std.testing.expect(slice[60] == 0x34);
1194}1206}
...@@ -1204,13 +1216,13 @@ test "shrink large object to large object with larger alignment" {...@@ -1204,13 +1216,13 @@ test "shrink large object to large object with larger alignment" {
1204 var fba = std.heap.FixedBufferAllocator.init(&debug_buffer);1216 var fba = std.heap.FixedBufferAllocator.init(&debug_buffer);
1205 const debug_allocator = fba.allocator();1217 const debug_allocator = fba.allocator();
12061218
1207 const alloc_size = page_size * 2 + 50;1219 const alloc_size = default_page_size * 2 + 50;
1208 var slice = try allocator.alignedAlloc(u8, 16, alloc_size);1220 var slice = try allocator.alignedAlloc(u8, 16, alloc_size);
1209 defer allocator.free(slice);1221 defer allocator.free(slice);
12101222
1211 const big_alignment: usize = switch (builtin.os.tag) {1223 const big_alignment: usize = switch (builtin.os.tag) {
1212 .windows => page_size * 32, // Windows aligns to 64K.1224 .windows => default_page_size * 32, // Windows aligns to 64K.
1213 else => page_size * 2,1225 else => default_page_size * 2,
1214 };1226 };
1215 // This loop allocates until we find a page that is not aligned to the big1227 // This loop allocates until we find a page that is not aligned to the big
1216 // alignment. Then we shrink the allocation after the loop, but increase the1228 // alignment. Then we shrink the allocation after the loop, but increase the
...@@ -1236,7 +1248,7 @@ test "realloc large object to small object" {...@@ -1236,7 +1248,7 @@ test "realloc large object to small object" {
1236 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");1248 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
1237 const allocator = gpa.allocator();1249 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);
1240 defer allocator.free(slice);1252 defer allocator.free(slice);
1241 slice[0] = 0x12;1253 slice[0] = 0x12;
1242 slice[16] = 0x34;1254 slice[16] = 0x34;
...@@ -1282,18 +1294,18 @@ test "realloc large object to larger alignment" {...@@ -1282,18 +1294,18 @@ test "realloc large object to larger alignment" {
1282 var fba = std.heap.FixedBufferAllocator.init(&debug_buffer);1294 var fba = std.heap.FixedBufferAllocator.init(&debug_buffer);
1283 const debug_allocator = fba.allocator();1295 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);
1286 defer allocator.free(slice);1298 defer allocator.free(slice);
12871299
1288 const big_alignment: usize = switch (builtin.os.tag) {1300 const big_alignment: usize = switch (builtin.os.tag) {
1289 .windows => page_size * 32, // Windows aligns to 64K.1301 .windows => default_page_size * 32, // Windows aligns to 64K.
1290 else => page_size * 2,1302 else => default_page_size * 2,
1291 };1303 };
1292 // This loop allocates until we find a page that is not aligned to the big alignment.1304 // This loop allocates until we find a page that is not aligned to the big alignment.
1293 var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator);1305 var stuff_to_free = std.ArrayList([]align(16) u8).init(debug_allocator);
1294 while (mem.isAligned(@intFromPtr(slice.ptr), big_alignment)) {1306 while (mem.isAligned(@intFromPtr(slice.ptr), big_alignment)) {
1295 try stuff_to_free.append(slice);1307 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);
1297 }1309 }
1298 while (stuff_to_free.popOrNull()) |item| {1310 while (stuff_to_free.popOrNull()) |item| {
1299 allocator.free(item);1311 allocator.free(item);
...@@ -1301,15 +1313,15 @@ test "realloc large object to larger alignment" {...@@ -1301,15 +1313,15 @@ test "realloc large object to larger alignment" {
1301 slice[0] = 0x12;1313 slice[0] = 0x12;
1302 slice[16] = 0x34;1314 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);
1305 try std.testing.expect(slice[0] == 0x12);1317 try std.testing.expect(slice[0] == 0x12);
1306 try std.testing.expect(slice[16] == 0x34);1318 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);
1309 try std.testing.expect(slice[0] == 0x12);1321 try std.testing.expect(slice[0] == 0x12);
1310 try std.testing.expect(slice[16] == 0x34);1322 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);
1313 try std.testing.expect(slice[0] == 0x12);1325 try std.testing.expect(slice[0] == 0x12);
1314 try std.testing.expect(slice[16] == 0x34);1326 try std.testing.expect(slice[16] == 0x34);
1315}1327}
...@@ -1327,7 +1339,7 @@ test "large object rejects shrinking to small" {...@@ -1327,7 +1339,7 @@ test "large object rejects shrinking to small" {
1327 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");1339 defer std.testing.expect(gpa.deinit() == .ok) catch @panic("leak");
1328 const allocator = gpa.allocator();1340 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);
1331 defer allocator.free(slice);1343 defer allocator.free(slice);
1332 slice[0] = 0x12;1344 slice[0] = 0x12;
1333 slice[3] = 0x34;1345 slice[3] = 0x34;
...@@ -1379,8 +1391,8 @@ test "large allocations count requested size not backing size" {...@@ -1379,8 +1391,8 @@ test "large allocations count requested size not backing size" {
1379 var gpa: DebugAllocator(.{ .enable_memory_limit = true }) = .{};1391 var gpa: DebugAllocator(.{ .enable_memory_limit = true }) = .{};
1380 const allocator = gpa.allocator();1392 const allocator = gpa.allocator();
13811393
1382 var buf = try allocator.alignedAlloc(u8, 1, page_size + 1);1394 var buf = try allocator.alignedAlloc(u8, 1, default_page_size + 1);
1383 try std.testing.expectEqual(page_size + 1, gpa.total_requested_bytes);1395 try std.testing.expectEqual(default_page_size + 1, gpa.total_requested_bytes);
1384 buf = try allocator.realloc(buf, 1);1396 buf = try allocator.realloc(buf, 1);
1385 try std.testing.expectEqual(1, gpa.total_requested_bytes);1397 try std.testing.expectEqual(1, gpa.total_requested_bytes);
1386 buf = try allocator.realloc(buf, 2);1398 buf = try allocator.realloc(buf, 2);