authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-19 17:48:07-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2025-09-20 14:34:18-07:00
log0c1fbc4ea6ff5c74b83d3959eac0b355570bd439
treec2b4473f8ce7ef2a69e6ab3f9b9d53aa4591fb1d
parent4d1b15bd9d4175127a63595f4fd80761c2e6564c

std: remove loop from growCapacity

I measured this against master branch and found no statistical difference. Since this code is simpler and logically superior due to always leaving sufficient unused capacity when growing, it is preferred over status quo.

4 files changed, 17 insertions(+), 39 deletions(-)

lib/std/Io/Writer.zig+1-1
......@@ -2661,7 +2661,7 @@ pub const Allocating = struct {
26612661 pub fn ensureTotalCapacity(a: *Allocating, new_capacity: usize) Allocator.Error!void {
26622662 // Protects growing unnecessarily since better_capacity will be larger.
26632663 if (a.writer.buffer.len >= new_capacity) return;
2664 const better_capacity = ArrayList(u8).growCapacity(a.writer.buffer.len, new_capacity);
2664 const better_capacity = ArrayList(u8).growCapacity(new_capacity);
26652665 return ensureTotalCapacityPrecise(a, better_capacity);
26662666 }
26672667
lib/std/array_list.zig+6-11
......@@ -172,7 +172,7 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
172172 // a new buffer and doing our own copy. With a realloc() call,
173173 // the allocator implementation would pointlessly copy our
174174 // extra capacity.
175 const new_capacity = Aligned(T, alignment).growCapacity(self.capacity, new_len);
175 const new_capacity = Aligned(T, alignment).growCapacity(new_len);
176176 const old_memory = self.allocatedSlice();
177177 if (self.allocator.remap(old_memory, new_capacity)) |new_memory| {
178178 self.items.ptr = new_memory.ptr;
......@@ -408,7 +408,7 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
408408 // Protects growing unnecessarily since better_capacity will be larger.
409409 if (self.capacity >= new_capacity) return;
410410
411 const better_capacity = Aligned(T, alignment).growCapacity(self.capacity, new_capacity);
411 const better_capacity = Aligned(T, alignment).growCapacity(new_capacity);
412412 return self.ensureTotalCapacityPrecise(better_capacity);
413413 }
414414
......@@ -1160,7 +1160,7 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
11601160 /// Invalidates element pointers if additional memory is needed.
11611161 pub fn ensureTotalCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
11621162 if (self.capacity >= new_capacity) return;
1163 return self.ensureTotalCapacityPrecise(gpa, growCapacity(self.capacity, new_capacity));
1163 return self.ensureTotalCapacityPrecise(gpa, growCapacity(new_capacity));
11641164 }
11651165
11661166 /// If the current capacity is less than `new_capacity`, this function will
......@@ -1359,17 +1359,12 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
13591359 return self.getLast();
13601360 }
13611361
1362 const init_capacity = @as(comptime_int, @max(1, std.atomic.cache_line / @sizeOf(T)));
1362 const init_capacity: comptime_int = @max(1, std.atomic.cache_line / @sizeOf(T));
13631363
13641364 /// Called when memory growth is necessary. Returns a capacity larger than
13651365 /// minimum that grows super-linearly.
1366 pub fn growCapacity(current: usize, minimum: usize) usize {
1367 var new = current;
1368 while (true) {
1369 new +|= new / 2 + init_capacity;
1370 if (new >= minimum)
1371 return new;
1372 }
1366 pub fn growCapacity(minimum: usize) usize {
1367 return minimum +| (minimum / 2 + init_capacity);
13731368 }
13741369 };
13751370}
lib/std/deque.zig+1-13
......@@ -56,7 +56,7 @@ pub fn Deque(comptime T: type) type {
5656 /// Invalidates element pointers if additional memory is needed.
5757 pub fn ensureTotalCapacity(deque: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
5858 if (deque.buffer.len >= new_capacity) return;
59 return deque.ensureTotalCapacityPrecise(gpa, growCapacity(deque.buffer.len, new_capacity));
59 return deque.ensureTotalCapacityPrecise(gpa, std.ArrayList(T).growCapacity(new_capacity));
6060 }
6161
6262 /// If the current capacity is less than `new_capacity`, this function will
......@@ -243,18 +243,6 @@ pub fn Deque(comptime T: type) type {
243243 return index - head_len;
244244 }
245245 }
246
247 const init_capacity: comptime_int = @max(1, std.atomic.cache_line / @sizeOf(T));
248
249 /// Called when memory growth is necessary. Returns a capacity larger than
250 /// minimum that grows super-linearly.
251 fn growCapacity(current: usize, minimum: usize) usize {
252 var new = current;
253 while (true) {
254 new +|= new / 2 + init_capacity;
255 if (new >= minimum) return new;
256 }
257 }
258246 };
259247}
260248
lib/std/multi_array_list.zig+9-14
......@@ -457,24 +457,19 @@ pub fn MultiArrayList(comptime T: type) type {
457457 /// Invalidates element pointers if additional memory is needed.
458458 pub fn ensureTotalCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
459459 if (self.capacity >= new_capacity) return;
460 return self.setCapacity(gpa, growCapacity(self.capacity, new_capacity));
460 return self.setCapacity(gpa, growCapacity(new_capacity));
461461 }
462462
463 const init_capacity = init: {
464 var max = 1;
465 for (fields) |field| max = @as(comptime_int, @max(max, @sizeOf(field.type)));
466 break :init @as(comptime_int, @max(1, std.atomic.cache_line / max));
463 const init_capacity: comptime_int = init: {
464 var max: comptime_int = 1;
465 for (fields) |field| max = @max(max, @sizeOf(field.type));
466 break :init @max(1, std.atomic.cache_line / max);
467467 };
468468
469 /// Called when memory growth is necessary. Returns a capacity larger than
470 /// minimum that grows super-linearly.
471 fn growCapacity(current: usize, minimum: usize) usize {
472 var new = current;
473 while (true) {
474 new +|= new / 2 + init_capacity;
475 if (new >= minimum)
476 return new;
477 }
469 /// Given a lower bound of required memory capacity, returns a larger value
470 /// with super-linear growth.
471 pub fn growCapacity(minimum: usize) usize {
472 return minimum +| (minimum / 2 + init_capacity);
478473 }
479474
480475 /// Modify the array so that it can hold at least `additional_count` **more** items.