authorgravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-10 21:08:29-07:00
committergravatar for andrew@ziglang.orgAndrew Kelley <andrew@ziglang.org> 2022-11-29 23:46:02-07:00
log0c0c70ee82df6e727dd488318e6d44d5010eef79
tree1dfe0a477004c7a2ce2bc5c401f3e5b9955f0e23
parent3ea04ed64cd47e65bee3bad1771d349042bb4392

std.heap.WasmAllocator: large allocations


1 files changed, 83 insertions(+), 69 deletions(-)

lib/std/heap/WasmAllocator.zig+83-69
...@@ -23,15 +23,18 @@ pub const vtable = Allocator.VTable{...@@ -23,15 +23,18 @@ pub const vtable = Allocator.VTable{
23pub const Error = Allocator.Error;23pub const Error = Allocator.Error;
2424
25const max_usize = math.maxInt(usize);25const max_usize = math.maxInt(usize);
26const ushift = math.Log2Int(usize);
26const bigpage_size = 512 * 1024;27const bigpage_size = 512 * 1024;
27const pages_per_bigpage = bigpage_size / wasm.page_size;28const pages_per_bigpage = bigpage_size / wasm.page_size;
28const bigpage_count = max_usize / bigpage_size;29const bigpage_count = max_usize / bigpage_size;
2930
30//// This has a length of 1024 usizes.
31//var bigpages_used = [1]usize{0} ** (bigpage_count / @bitSizeOf(usize));
32
33/// We have a small size class for all sizes up to 512kb.31/// We have a small size class for all sizes up to 512kb.
34const size_class_count = math.log2(bigpage_size);32const size_class_count = math.log2(bigpage_size);
33/// 0 - 1 bigpage
34/// 1 - 2 bigpages
35/// 2 - 4 bigpages
36/// etc.
37const big_size_class_count = math.log2(bigpage_count);
3538
36const FreeList = struct {39const FreeList = struct {
37 /// Each element is the address of a freed pointer.40 /// Each element is the address of a freed pointer.
...@@ -46,20 +49,26 @@ const FreeList = struct {...@@ -46,20 +49,26 @@ const FreeList = struct {
46 };49 };
47};50};
4851
49var next_addrs = [1]usize{0} ** size_class_count;52const Bucket = struct {
50var frees = [1]FreeList{FreeList.init} ** size_class_count;53 ptr: usize,
51var bigpage_free_list: FreeList = .{54 end: usize,
52 .ptr = &bigpage_free_buf,55
53 .len = 0,56 const init: Bucket = .{
54 .cap = bigpage_free_buf.len,57 .ptr = 0,
58 .end = 0,
59 };
55};60};
56var bigpage_free_buf: [16]usize = undefined;61
62var next_addrs = [1]Bucket{Bucket.init} ** size_class_count;
63var frees = [1]FreeList{FreeList.init} ** size_class_count;
64var big_frees = [1]FreeList{FreeList.init} ** big_size_class_count;
5765
58fn alloc(ctx: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize) Error![]u8 {66fn alloc(ctx: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize) Error![]u8 {
59 _ = ctx;67 _ = ctx;
60 _ = len_align;68 _ = len_align;
61 _ = ra;69 _ = ra;
62 const slot_size = math.ceilPowerOfTwoAssert(usize, @max(len, alignment));70 const aligned_len = @max(len, alignment);
71 const slot_size = math.ceilPowerOfTwoAssert(usize, aligned_len);
63 const class = math.log2(slot_size);72 const class = math.log2(slot_size);
64 if (class < size_class_count) {73 if (class < size_class_count) {
65 const addr = a: {74 const addr = a: {
...@@ -69,55 +78,30 @@ fn alloc(ctx: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize)...@@ -69,55 +78,30 @@ fn alloc(ctx: *anyopaque, len: usize, alignment: u29, len_align: u29, ra: usize)
69 break :a free_list.ptr[free_list.len];78 break :a free_list.ptr[free_list.len];
70 }79 }
7180
72 // Ensure unused capacity in the corresponding free list.
73 // This prevents memory allocation within free().81 // This prevents memory allocation within free().
74 if (free_list.len >= free_list.cap) {82 try ensureFreeListCapacity(free_list);
75 const old_bigpage_count = free_list.cap / bigpage_size;
76 if (bigpage_free_list.cap - bigpage_free_list.len < old_bigpage_count) {
77 return error.OutOfMemory;
78 }
79 const new_bigpage_count = old_bigpage_count + 1;
80 const addr = try allocBigPages(new_bigpage_count);
81 const new_ptr = @intToPtr([*]usize, addr);
82 const old_ptr = free_list.ptr;
83 @memcpy(
84 @ptrCast([*]u8, new_ptr),
85 @ptrCast([*]u8, old_ptr),
86 @sizeOf(usize) * free_list.len,
87 );
88 free_list.ptr = new_ptr;
89 free_list.cap = new_bigpage_count * (bigpage_size / @sizeOf(usize));
90
91 var i: usize = 0;
92 while (i < old_bigpage_count) : (i += 1) {
93 bigpage_free_list.ptr[bigpage_free_list.len] = @ptrToInt(old_ptr) +
94 i * bigpage_size;
95 bigpage_free_list.len += 1;
96 }
97 }
9883
99 const next_addr = next_addrs[class];84 const next_addr = next_addrs[class];
100 if (next_addr % bigpage_size == 0) {85 if (next_addr.ptr == next_addr.end) {
101 //std.debug.print("alloc big page len={d} class={d} slot_size={d}\n", .{
102 // len, class, slot_size,
103 //});
104 const addr = try allocBigPages(1);86 const addr = try allocBigPages(1);
105 next_addrs[class] = addr + slot_size;87 //std.debug.print("allocated fresh slot_size={d} class={d} addr=0x{x}\n", .{
88 // slot_size, class, addr,
89 //});
90 next_addrs[class] = .{
91 .ptr = addr + slot_size,
92 .end = addr + bigpage_size,
93 };
106 break :a addr;94 break :a addr;
107 } else {95 } else {
108 //std.debug.print("easy! len={d} class={d} slot_size={d}\n", .{96 next_addrs[class].ptr = next_addr.ptr + slot_size;
109 // len, class, slot_size,97 break :a next_addr.ptr;
110 //});
111 next_addrs[class] = next_addr + slot_size;
112 break :a next_addr;
113 }98 }
114 };99 };
115 return @intToPtr([*]u8, addr)[0..len];100 return @intToPtr([*]u8, addr)[0..len];
116 } else {
117 std.debug.panic("big alloc: len={d} align={d} slot_size={d} class={d}", .{
118 len, alignment, slot_size, class,
119 });
120 }101 }
102 const bigpages_needed = (aligned_len + (bigpage_size - 1)) / bigpage_size;
103 const addr = try allocBigPages(bigpages_needed);
104 return @intToPtr([*]u8, addr)[0..len];
121}105}
122106
123fn resize(107fn resize(
...@@ -145,29 +129,65 @@ fn free(...@@ -145,29 +129,65 @@ fn free(
145) void {129) void {
146 _ = ctx;130 _ = ctx;
147 _ = return_address;131 _ = return_address;
148 const class_size = @max(buf.len, buf_align);132 const aligned_len = @max(buf.len, buf_align);
149 const class = math.log2(class_size);133 const slot_size = math.ceilPowerOfTwoAssert(usize, aligned_len);
134 const class = math.log2(slot_size);
150 if (class < size_class_count) {135 if (class < size_class_count) {
151 const free_list = &frees[class];136 const free_list = &frees[class];
152 assert(free_list.len < free_list.cap);137 assert(free_list.len < free_list.cap);
153 free_list.ptr[free_list.len] = @ptrToInt(buf.ptr);138 free_list.ptr[free_list.len] = @ptrToInt(buf.ptr);
154 free_list.len += 1;139 free_list.len += 1;
155 } else {140 } else {
156 std.debug.panic("big free: len={d} align={d}", .{141 const bigpages_needed = (aligned_len + (bigpage_size - 1)) / bigpage_size;
157 buf.len, buf_align,142 const big_slot_size = math.ceilPowerOfTwoAssert(usize, bigpages_needed);
158 });143 const big_class = math.log2(big_slot_size);
144 const free_list = &big_frees[big_class];
145 assert(free_list.len < free_list.cap);
146 free_list.ptr[free_list.len] = @ptrToInt(buf.ptr);
147 free_list.len += 1;
159 }148 }
160}149}
161150
162inline fn allocBigPages(n: usize) !usize {151fn allocBigPages(n: usize) !usize {
163 if (n == 1 and bigpage_free_list.len > 0) {152 const slot_size = math.ceilPowerOfTwoAssert(usize, n);
164 bigpage_free_list.len -= 1;153 const class = math.log2(slot_size);
165 return bigpage_free_list.ptr[bigpage_free_list.len];154
155 const free_list = &big_frees[class];
156 if (free_list.len > 0) {
157 free_list.len -= 1;
158 return free_list.ptr[free_list.len];
166 }159 }
167 const page_index = @wasmMemoryGrow(0, n * pages_per_bigpage);160
168 if (page_index <= 0)161 //std.debug.print("ensureFreeListCapacity slot_size={d} big_class={d}\n", .{
169 return error.OutOfMemory;162 // slot_size, class,
170 return @intCast(u32, page_index) * wasm.page_size;163 //});
164 // This prevents memory allocation within free().
165 try ensureFreeListCapacity(free_list);
166
167 const page_index = @wasmMemoryGrow(0, slot_size * pages_per_bigpage);
168 if (page_index <= 0) return error.OutOfMemory;
169 const addr = @intCast(u32, page_index) * wasm.page_size;
170 //std.debug.print("got 0x{x}..0x{x} from memory.grow\n", .{
171 // addr, addr + wasm.page_size * slot_size * pages_per_bigpage,
172 //});
173 return addr;
174}
175
176fn ensureFreeListCapacity(free_list: *FreeList) Allocator.Error!void {
177 if (free_list.len < free_list.cap) return;
178 const old_bigpage_count = free_list.cap / bigpage_size;
179 free_list.cap = math.maxInt(usize); // Prevent recursive calls.
180 const new_bigpage_count = @max(old_bigpage_count * 2, 1);
181 const addr = try allocBigPages(new_bigpage_count);
182 //std.debug.print("allocated {d} big pages: 0x{x}\n", .{ new_bigpage_count, addr });
183 const new_ptr = @intToPtr([*]usize, addr);
184 @memcpy(
185 @ptrCast([*]u8, new_ptr),
186 @ptrCast([*]u8, free_list.ptr),
187 @sizeOf(usize) * free_list.len,
188 );
189 free_list.ptr = new_ptr;
190 free_list.cap = new_bigpage_count * (bigpage_size / @sizeOf(usize));
171}191}
172192
173const test_ally = Allocator{193const test_ally = Allocator{
...@@ -207,16 +227,10 @@ test "small allocations - free in reverse order" {...@@ -207,16 +227,10 @@ test "small allocations - free in reverse order" {
207}227}
208228
209test "large allocations" {229test "large allocations" {
210 std.debug.print("alloc ptr1\n", .{});
211 const ptr1 = try test_ally.alloc(u64, 42768);230 const ptr1 = try test_ally.alloc(u64, 42768);
212 std.debug.print("alloc ptr2\n", .{});
213 const ptr2 = try test_ally.alloc(u64, 52768);231 const ptr2 = try test_ally.alloc(u64, 52768);
214 std.debug.print("free ptr1\n", .{});
215 test_ally.free(ptr1);232 test_ally.free(ptr1);
216 std.debug.print("alloc ptr3\n", .{});
217 const ptr3 = try test_ally.alloc(u64, 62768);233 const ptr3 = try test_ally.alloc(u64, 62768);
218 std.debug.print("free ptr3\n", .{});
219 test_ally.free(ptr3);234 test_ally.free(ptr3);
220 std.debug.print("free ptr2\n", .{});
221 test_ally.free(ptr2);235 test_ally.free(ptr2);
222}236}