Skip to content

Commit

Permalink
Keep track of the dirty blocks when within regions smalller than 1GB.
Browse files Browse the repository at this point in the history
The dirty block tracking will become incorrect for larger regions, but
nothing bad should happen, as this information isn't current used.
  • Loading branch information
deadalnix committed Dec 6, 2023
1 parent a21fb07 commit a1d4c32
Showing 1 changed file with 76 additions and 29 deletions.
105 changes: 76 additions & 29 deletions sdlib/d/gc/region.d
Original file line number Diff line number Diff line change
Expand Up @@ -153,7 +153,7 @@ private:
auto size = blocks * BlockSize;

auto r = getOrAllocateRegion();
r.at(ptr, size, size);
r.atDirty(ptr, size);
registerRegion(r);
}

Expand Down Expand Up @@ -200,7 +200,7 @@ private:
}

// Newly allocated blocks are considered clean.
return r.at(ptr, size, 0);
return r.atClean(ptr, size);
}

Region* getOrAllocateRegion() {
Expand Down Expand Up @@ -366,10 +366,31 @@ struct Region {

public:
Region* at(void* ptr, size_t size, size_t dirtySize) {
auto oldDirtyBlocks = dirtyBlocks;
scope(success) dirtyBlocks = oldDirtyBlocks;

this = Region(ptr, size, generation, dirtySize);
return &this;
}

Region* atClean(void* ptr, size_t size) {
at(ptr, size, 0);
dirtyBlocks.clear();
return &this;
}

Region* atDirty(void* ptr, size_t size) {
at(ptr, size, size);

// Make the region dirty.
// FIXME: We use max to ensures we don't trip an assert
// when the region is larger than 1GB.
dirtyBlocks
.setRollingRange(startOffset, min(blockCount, RefillBlockCount));

return &this;
}

static fromSlot(Base.Slot slot) {
// FIXME: in contract
assert(slot.address !is null, "Slot is empty!");
Expand All @@ -379,23 +400,6 @@ public:
return r;
}

Region* merge(Region* r) {
assert(address is (r.address + r.size) || r.address is (address + size),
"Regions are not adjacent!");

auto left = address < r.address ? &this : r;
auto right = address < r.address ? r : &this;

// Dirt is at all times contiguous within a region, and starts at the bottom.
// Given as purging is not yet supported, this invariant always holds.
assert(left.dirtySize == left.size || right.dirtySize == 0,
"Merge would place dirty blocks in front of clean blocks!");

import d.gc.util;
auto a = min(address, r.address);
return at(a, size + r.size, dirtySize + r.dirtySize);
}

@property
ref PHNode phnode() {
return _links.phnode;
Expand All @@ -412,13 +416,45 @@ public:
}

@property
size_t blockCount() const {
return size / BlockSize;
uint blockCount() const {
return (size / BlockSize) & uint.max;
}

@property
size_t dirtyBlockCount() const {
return dirtySize / BlockSize;
uint dirtyBlockCount() const {
return (dirtySize / BlockSize) & uint.max;
}

@property
uint startOffset() const {
auto blockIndex = (cast(size_t) address) / BlockSize;
return blockIndex % RefillBlockCount;
}

bool contains(const void* ptr) const {
return address <= ptr && ptr < address + size;
}

Region* merge(Region* r) {
assert(address is (r.address + r.size) || r.address is (address + size),
"Regions are not adjacent!");

auto left = address < r.address ? &this : r;
auto right = address < r.address ? r : &this;

// Dirt is at all times contiguous within a region, and starts at the bottom.
// Given as purging is not yet supported, this invariant always holds.
assert(left.dirtySize == left.size || right.dirtySize == 0,
"Merge would place dirty blocks in front of clean blocks!");

// Copy the dirty bits.
// FIXME: We use max to ensures we don't trip an assert
// when the region is larger than 1GB.
dirtyBlocks.setRollingRangeFrom(r.dirtyBlocks, r.startOffset,
min(r.blockCount, RefillBlockCount));

auto a = min(address, r.address);
return at(a, size + r.size, dirtySize + r.dirtySize);
}
}

Expand Down Expand Up @@ -493,13 +529,21 @@ unittest trackDirtyBlocks {
blockAddresses[i] = blockArray[i].address;
}

void freeRun(BlockDescriptor[] slice) {
auto oldDirties = regionAllocator.dirtyBlockCount;
foreach (block; slice) {
regionAllocator.release(&block);
}
void freeRun(BlockDescriptor[] blocks) {
auto expectedDirtyBlocks = regionAllocator.dirtyBlockCount;

assert(regionAllocator.dirtyBlockCount == oldDirties + slice.length);
foreach (b; blocks) {
regionAllocator.release(&b);

expectedDirtyBlocks++;
assert(regionAllocator.dirtyBlockCount == expectedDirtyBlocks);

Region rr;
rr.at(b.address, BlockSize, 0);
auto r = ra.regionsByRange.find(&rr);
assert(r.contains(b.address));
assert(r.dirtyBlocks.valueAt(rr.startOffset));
}
}

// Verify that a region with given block count and dirt exists at address.
Expand All @@ -512,6 +556,9 @@ unittest trackDirtyBlocks {
assert(r.address == address);
assert(r.blockCount == blocks);
assert(r.dirtyBlockCount == dirtyBlocks);
assert(
r.dirtyBlocks.rollingCountBits(r.startOffset, blocks) == dirtyBlocks
);
}

// Initially, there are no dirty blocks.
Expand Down

1 comment on commit a1d4c32

@dsm9000
Copy link
Contributor

@dsm9000 dsm9000 commented on a1d4c32 Dec 6, 2023

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I was making the very same thing, and it looks like you beat me to the finish!
Should I continue from this to the multi-level version, or go back to other ticket?

Please sign in to comment.