Skip to content

Commit 26f53d0

Browse files
committed
runtime: Allocate old space lazily
This commit adds a lazy_allocate member to Space which will be checked before each allocation to see whether the memory is currently allocated or not. If lazy_allocate is true, then the memory space will be allocated anew. This halves the minimum memory requirements of the VM.
1 parent 7ca1b53 commit 26f53d0

File tree

1 file changed

+43
-23
lines changed

1 file changed

+43
-23
lines changed

src/runtime/Heap.zig

Lines changed: 43 additions & 23 deletions
Original file line numberDiff line numberDiff line change
@@ -72,7 +72,7 @@ pub fn destroy(self: *Self) void {
7272
}
7373

7474
fn init(self: *Self, allocator: Allocator, vm: *VirtualMachine) !void {
75-
var old_space = try Space.init(self, allocator, "old space", InitialOldSpaceSize);
75+
var old_space = Space.lazyInit(self, "old space", InitialOldSpaceSize);
7676
errdefer old_space.deinit(allocator);
7777

7878
var from_space = try Space.init(self, allocator, "from space", NewSpaceSize);
@@ -101,13 +101,6 @@ fn init(self: *Self, allocator: Allocator, vm: *VirtualMachine) !void {
101101
self.from_space.scavenge_target = &self.to_space;
102102
self.from_space.tenure_target = &self.old_space;
103103
self.eden.tenure_target = &self.from_space;
104-
105-
if (GC_DEBUG) {
106-
std.debug.print("Heap.init: Eden is {*}-{*}\n", .{ self.eden.memory.ptr, self.eden.memory.ptr + self.eden.memory.len });
107-
std.debug.print("Heap.init: From space is {*}-{*}\n", .{ self.from_space.memory.ptr, self.from_space.memory.ptr + self.from_space.memory.len });
108-
std.debug.print("Heap.init: To space is {*}-{*}\n", .{ self.to_space.memory.ptr, self.to_space.memory.ptr + self.to_space.memory.len });
109-
std.debug.print("Heap.init: Old space is {*}-{*}\n", .{ self.old_space.memory.ptr, self.old_space.memory.ptr + self.old_space.memory.len });
110-
}
111104
}
112105

113106
fn deinit(self: *Self) void {
@@ -294,13 +287,18 @@ const Space = struct {
294287
/// A reference back to the heap.
295288
heap: *Self,
296289

290+
/// If true, then memory is undefined, and the next time an allocation is
291+
/// requested the memory space should be allocated.
292+
lazy_allocate: bool = true,
293+
/// The size of this memory space.
294+
size: usize,
297295
/// The raw memory contents of the space. The space capacity can be learned
298-
/// with `memory.len`.
299-
memory: []u64,
296+
/// with `memory.len`. Uninitialized while `lazy_allocate` is true.
297+
memory: []u64 = undefined,
300298
/// A slice of the current object segment in this space.
301-
object_segment: []u64,
299+
object_segment: []u64 = undefined,
302300
/// A slice of the current byte array segment in this space.
303-
byte_array_segment: []u64,
301+
byte_array_segment: []u64 = undefined,
304302
/// The set of objects which reference an object in this space. When a
305303
/// constant or assignable slot from a previous space references this space,
306304
/// it is added to this set; when it starts referencing another space, it is
@@ -347,27 +345,43 @@ const Space = struct {
347345
previous: ?*const NewerGenerationLink,
348346
};
349347

348+
pub fn lazyInit(heap: *Self, comptime name: [*:0]const u8, size: usize) Space {
349+
return Space{
350+
.heap = heap,
351+
.name = name,
352+
.size = size,
353+
.remembered_set = .{},
354+
.finalization_set = .{},
355+
.tracked_set = .{},
356+
};
357+
}
358+
350359
pub fn init(heap: *Self, allocator: Allocator, comptime name: [*:0]const u8, size: usize) !Space {
351-
var memory = try allocator.alloc(u64, size / @sizeOf(u64));
360+
var self = lazyInit(heap, name, size);
361+
try self.allocateMemory(allocator);
362+
return self;
363+
}
364+
365+
fn allocateMemory(self: *Space, allocator: Allocator) !void {
366+
var memory = try allocator.alloc(u64, @divExact(self.size, @sizeOf(u64)));
352367

353368
// REVIEW: When Zig fixes its zero-length arrays to not create a slice
354369
// with an undefined address, get rid of this and just use
355370
// memory[0..0].
356371
var runtime_zero: usize = 0;
357372

358-
return Space{
359-
.heap = heap,
360-
.memory = memory,
361-
.object_segment = memory[0..runtime_zero],
362-
.byte_array_segment = (memory.ptr + memory.len)[0..runtime_zero],
363-
.remembered_set = .{},
364-
.finalization_set = .{},
365-
.tracked_set = .{},
366-
.name = name,
367-
};
373+
self.lazy_allocate = false;
374+
self.memory = memory;
375+
self.object_segment = memory[0..runtime_zero];
376+
self.byte_array_segment = (memory.ptr + memory.len)[0..runtime_zero];
377+
378+
if (GC_DEBUG) std.debug.print("Heap.init: {s} is {*}-{*}\n", .{ self.name, self.memory.ptr, self.memory.ptr + self.memory.len });
368379
}
369380

370381
pub fn deinit(self: *Space, allocator: Allocator) void {
382+
if (self.lazy_allocate)
383+
return;
384+
371385
// Finalize everything that needs to be finalized.
372386
var finalization_it = self.finalization_set.iterator();
373387
while (finalization_it.next()) |entry| {
@@ -741,6 +755,9 @@ const Space = struct {
741755
/// Allocates the requested amount in bytes in the object segment of this
742756
/// space, garbage collecting if there is not enough space.
743757
pub fn allocateInObjectSegment(self: *Space, allocator: Allocator, size: usize) ![*]u64 {
758+
if (self.lazy_allocate)
759+
try self.allocateMemory(allocator);
760+
744761
if (self.freeMemory() < size) try self.collectGarbage(allocator, size);
745762

746763
const size_in_words = @divExact(size, @sizeOf(u64));
@@ -757,6 +774,9 @@ const Space = struct {
757774
/// Allocates the requested amount in bytes in the byte vector segment of
758775
/// this space, garbage collecting if there is not enough space.
759776
pub fn allocateInByteVectorSegment(self: *Space, allocator: Allocator, size: usize) ![*]u64 {
777+
if (self.lazy_allocate)
778+
try self.allocateMemory(allocator);
779+
760780
if (self.freeMemory() < size) try self.collectGarbage(allocator, size);
761781

762782
const size_in_words = @divExact(size, @sizeOf(u64));

0 commit comments

Comments
 (0)