Skip to content

Commit 9601906

Browse files
committed
std.heap.DebugAllocator: make page size configurable
1 parent cd99ab3 commit 9601906

File tree

1 file changed

+41
-29
lines changed

1 file changed

+41
-29
lines changed

lib/std/heap/debug_allocator.zig

Lines changed: 41 additions & 29 deletions
Original file line numberDiff line numberDiff line change
@@ -90,15 +90,12 @@ const mem = std.mem;
9090
const Allocator = std.mem.Allocator;
9191
const StackTrace = std.builtin.StackTrace;
9292

93-
const page_size: usize = @max(std.heap.page_size_max, switch (builtin.os.tag) {
93+
const 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
});
98-
const page_align: mem.Alignment = .fromByteUnits(page_size);
9998

100-
/// Integer type for pointing to slots in a small allocation
101-
const SlotIndex = std.meta.Int(.unsigned, math.log2(page_size) + 1);
10299
const Log2USize = std.math.Log2Int(usize);
103100

104101
const 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

11541166
test "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,34 +1294,34 @@ 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);
13001312
}
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);

0 commit comments

Comments
 (0)