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 {...@@ -2661,7 +2661,7 @@ pub const Allocating = struct {
2661 pub fn ensureTotalCapacity(a: *Allocating, new_capacity: usize) Allocator.Error!void {2661 pub fn ensureTotalCapacity(a: *Allocating, new_capacity: usize) Allocator.Error!void {
2662 // Protects growing unnecessarily since better_capacity will be larger.2662 // Protects growing unnecessarily since better_capacity will be larger.
2663 if (a.writer.buffer.len >= new_capacity) return;2663 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);
2665 return ensureTotalCapacityPrecise(a, better_capacity);2665 return ensureTotalCapacityPrecise(a, better_capacity);
2666 }2666 }
26672667
lib/std/array_list.zig+6-11
...@@ -172,7 +172,7 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type...@@ -172,7 +172,7 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
172 // a new buffer and doing our own copy. With a realloc() call,172 // a new buffer and doing our own copy. With a realloc() call,
173 // the allocator implementation would pointlessly copy our173 // the allocator implementation would pointlessly copy our
174 // extra capacity.174 // extra capacity.
175 const new_capacity = Aligned(T, alignment).growCapacity(self.capacity, new_len);175 const new_capacity = Aligned(T, alignment).growCapacity(new_len);
176 const old_memory = self.allocatedSlice();176 const old_memory = self.allocatedSlice();
177 if (self.allocator.remap(old_memory, new_capacity)) |new_memory| {177 if (self.allocator.remap(old_memory, new_capacity)) |new_memory| {
178 self.items.ptr = new_memory.ptr;178 self.items.ptr = new_memory.ptr;
...@@ -408,7 +408,7 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type...@@ -408,7 +408,7 @@ pub fn AlignedManaged(comptime T: type, comptime alignment: ?mem.Alignment) type
408 // Protects growing unnecessarily since better_capacity will be larger.408 // Protects growing unnecessarily since better_capacity will be larger.
409 if (self.capacity >= new_capacity) return;409 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);
412 return self.ensureTotalCapacityPrecise(better_capacity);412 return self.ensureTotalCapacityPrecise(better_capacity);
413 }413 }
414414
...@@ -1160,7 +1160,7 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {...@@ -1160,7 +1160,7 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
1160 /// Invalidates element pointers if additional memory is needed.1160 /// Invalidates element pointers if additional memory is needed.
1161 pub fn ensureTotalCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {1161 pub fn ensureTotalCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
1162 if (self.capacity >= new_capacity) return;1162 if (self.capacity >= new_capacity) return;
1163 return self.ensureTotalCapacityPrecise(gpa, growCapacity(self.capacity, new_capacity));1163 return self.ensureTotalCapacityPrecise(gpa, growCapacity(new_capacity));
1164 }1164 }
11651165
1166 /// If the current capacity is less than `new_capacity`, this function will1166 /// 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 {...@@ -1359,17 +1359,12 @@ pub fn Aligned(comptime T: type, comptime alignment: ?mem.Alignment) type {
1359 return self.getLast();1359 return self.getLast();
1360 }1360 }
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
1364 /// Called when memory growth is necessary. Returns a capacity larger than1364 /// Called when memory growth is necessary. Returns a capacity larger than
1365 /// minimum that grows super-linearly.1365 /// minimum that grows super-linearly.
1366 pub fn growCapacity(current: usize, minimum: usize) usize {1366 pub fn growCapacity(minimum: usize) usize {
1367 var new = current;1367 return minimum +| (minimum / 2 + init_capacity);
1368 while (true) {
1369 new +|= new / 2 + init_capacity;
1370 if (new >= minimum)
1371 return new;
1372 }
1373 }1368 }
1374 };1369 };
1375}1370}
lib/std/deque.zig+1-13
...@@ -56,7 +56,7 @@ pub fn Deque(comptime T: type) type {...@@ -56,7 +56,7 @@ pub fn Deque(comptime T: type) type {
56 /// Invalidates element pointers if additional memory is needed.56 /// Invalidates element pointers if additional memory is needed.
57 pub fn ensureTotalCapacity(deque: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {57 pub fn ensureTotalCapacity(deque: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
58 if (deque.buffer.len >= new_capacity) return;58 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));
60 }60 }
6161
62 /// If the current capacity is less than `new_capacity`, this function will62 /// If the current capacity is less than `new_capacity`, this function will
...@@ -243,18 +243,6 @@ pub fn Deque(comptime T: type) type {...@@ -243,18 +243,6 @@ pub fn Deque(comptime T: type) type {
243 return index - head_len;243 return index - head_len;
244 }244 }
245 }245 }
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 }
258 };246 };
259}247}
260248
lib/std/multi_array_list.zig+9-14
...@@ -457,24 +457,19 @@ pub fn MultiArrayList(comptime T: type) type {...@@ -457,24 +457,19 @@ pub fn MultiArrayList(comptime T: type) type {
457 /// Invalidates element pointers if additional memory is needed.457 /// Invalidates element pointers if additional memory is needed.
458 pub fn ensureTotalCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {458 pub fn ensureTotalCapacity(self: *Self, gpa: Allocator, new_capacity: usize) Allocator.Error!void {
459 if (self.capacity >= new_capacity) return;459 if (self.capacity >= new_capacity) return;
460 return self.setCapacity(gpa, growCapacity(self.capacity, new_capacity));460 return self.setCapacity(gpa, growCapacity(new_capacity));
461 }461 }
462462
463 const init_capacity = init: {463 const init_capacity: comptime_int = init: {
464 var max = 1;464 var max: comptime_int = 1;
465 for (fields) |field| max = @as(comptime_int, @max(max, @sizeOf(field.type)));465 for (fields) |field| max = @max(max, @sizeOf(field.type));
466 break :init @as(comptime_int, @max(1, std.atomic.cache_line / max));466 break :init @max(1, std.atomic.cache_line / max);
467 };467 };
468468
469 /// Called when memory growth is necessary. Returns a capacity larger than469 /// Given a lower bound of required memory capacity, returns a larger value
470 /// minimum that grows super-linearly.470 /// with super-linear growth.
471 fn growCapacity(current: usize, minimum: usize) usize {471 pub fn growCapacity(minimum: usize) usize {
472 var new = current;472 return minimum +| (minimum / 2 + init_capacity);
473 while (true) {
474 new +|= new / 2 + init_capacity;
475 if (new >= minimum)
476 return new;
477 }
478 }473 }
479474
480 /// Modify the array so that it can hold at least `additional_count` **more** items.475 /// Modify the array so that it can hold at least `additional_count` **more** items.