Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Resize-in-place for large allocs. (Shrink-only) #289

Merged
merged 54 commits into from
Sep 8, 2023
Merged
Show file tree
Hide file tree
Changes from 47 commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
49640db
first try at resize-in-place for large allocs.
dsm9000 Sep 4, 2023
355d5d0
first try at resize-in-place for large allocs. (minor corrections)
dsm9000 Sep 4, 2023
f5febbd
first try at resize-in-place for large allocs. (minor corrections)
dsm9000 Sep 4, 2023
0ac6d69
corrections.
dsm9000 Sep 4, 2023
4b96dad
corrections.
dsm9000 Sep 4, 2023
89a46e0
corrections.
dsm9000 Sep 4, 2023
94caea2
corrections. (simplify growAlloc)
dsm9000 Sep 4, 2023
e9c14c0
corrections. (simplify growAlloc)
dsm9000 Sep 4, 2023
e3f2f28
corrections. (misc. cleanup)
dsm9000 Sep 4, 2023
1bb3c1f
corrections. (misc. cleanup)
dsm9000 Sep 4, 2023
f774cfb
corrections. (misc. cleanup)
dsm9000 Sep 4, 2023
906de70
corrections. (misc. cleanup)
dsm9000 Sep 4, 2023
4cee257
corrections. (misc. cleanup)
dsm9000 Sep 4, 2023
8a05ead
corrections. (tests)
dsm9000 Sep 5, 2023
0f52ff2
corrections. (simplify growAlloc.)
dsm9000 Sep 5, 2023
34a1a53
corrections. (simplify growAlloc.)
dsm9000 Sep 5, 2023
d43335a
corrections. (simplify growAlloc.)
dsm9000 Sep 5, 2023
816c7c7
corrections. (asserts)
dsm9000 Sep 5, 2023
91b215c
corrections. (asserts)
dsm9000 Sep 5, 2023
9b3c652
uglier but faster growAlloc()
dsm9000 Sep 5, 2023
717e975
tmp
dsm9000 Sep 5, 2023
04f9bc0
cut-down version where resizeLarge() supports only shrinking.
dsm9000 Sep 6, 2023
9bd10c4
cut-down version where resizeLarge() supports only shrinking.
dsm9000 Sep 6, 2023
6e0d589
cut-down version where resizeLarge() supports only shrinking.
dsm9000 Sep 6, 2023
b7199f6
shrink corrections.
dsm9000 Sep 6, 2023
b5d1b90
Merge branch 'master' of github.com:dsm9000/sdc into resize_in_place
dsm9000 Sep 7, 2023
4406d9e
shrink corrections.
dsm9000 Sep 7, 2023
f0f41ce
shrink tests with full hpd.
dsm9000 Sep 7, 2023
821ae45
shrink tests with full hpd.
dsm9000 Sep 7, 2023
3bd87ca
shrink tests with full hpd.
dsm9000 Sep 7, 2023
4d0c377
shrink corrections.
dsm9000 Sep 7, 2023
5de41f6
unregisterHPD from master
dsm9000 Sep 7, 2023
b84107e
unregisterHPD from master
dsm9000 Sep 7, 2023
d3bbbd9
unregisterHPD from master
dsm9000 Sep 7, 2023
da34634
shrink corrections.
dsm9000 Sep 7, 2023
d594054
shrink corrections.
dsm9000 Sep 7, 2023
a533477
shrink corrections.
dsm9000 Sep 7, 2023
7db00ce
Merge branch 'master' of github.com:dsm9000/sdc into resize_in_place
dsm9000 Sep 7, 2023
bc94676
shrink corrections
dsm9000 Sep 7, 2023
a4cf611
shrink corrections
dsm9000 Sep 7, 2023
7a6f6a6
shrink corrections
dsm9000 Sep 7, 2023
946f6d7
shrink corrections
dsm9000 Sep 7, 2023
f1b96ca
shrink corrections
dsm9000 Sep 7, 2023
ddcbfc9
shrink corrections
dsm9000 Sep 7, 2023
491c7a9
shrink corrections
dsm9000 Sep 7, 2023
af46b49
shrink corrections
dsm9000 Sep 7, 2023
daa5f6e
shrink corrections
dsm9000 Sep 7, 2023
cd98c58
assert correction.
dsm9000 Sep 8, 2023
42a8b96
Merge branch 'master' of github.com:dsm9000/sdc into resize_in_place
dsm9000 Sep 8, 2023
235a70d
use new pageCount feature.
dsm9000 Sep 8, 2023
6a74319
Merge branch 'master' of github.com:dsm9000/sdc into resize_in_place
dsm9000 Sep 8, 2023
c92b1fe
use hpdIndex
dsm9000 Sep 8, 2023
b209967
Merge branch 'master' of github.com:dsm9000/sdc into resize_in_place
dsm9000 Sep 8, 2023
8f94b7c
use getPageCount. additional test for realloc.
dsm9000 Sep 8, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
204 changes: 204 additions & 0 deletions sdlib/d/gc/arena.d
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,34 @@ public:
return null;
}

bool resizeLarge(shared(ExtentMap)* emap, Extent* e, size_t size) shared {
assert(e !is null, "Extent is null!");
assert(e.isLarge(), "Expected a large extent!");
assert(e.arenaIndex == index, "Invalid arena index!");
assert(isLargeSize(size));

// Huge is not supported:
if (e.isHuge()) {
Copy link
Collaborator

Choose a reason for hiding this comment

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

Can this happen if the isLarge assert pass.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yes, please see definition, isLarge includes any extent that isn't a slab.

return false;
}

auto computedPageCount = alignUp(size, PageSize) / PageSize;
uint newPages = computedPageCount & uint.max;
assert(newPages == computedPageCount, "Unexpected page count!");
Copy link
Contributor

Choose a reason for hiding this comment

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

getPageCount


auto currentSize = e.size;
uint oldPages = cast(uint) (currentSize / PageSize);
Copy link
Contributor

Choose a reason for hiding this comment

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

e.pageCount


// Growing is not yet supported:
if (newPages >= oldPages) {
return newPages == oldPages;
}

shrinkAlloc(emap, e, newPages);

return true;
}

/**
* Deallocation facility.
*/
Expand Down Expand Up @@ -244,6 +272,32 @@ private:
(cast(Arena*) &this).freePagesImpl(e, n, pages);
}

void shrinkAlloc(shared(ExtentMap)* emap, Extent* e, uint pages) shared {
assert(!e.isHuge(), "Does not support huge!");
assert(isAligned(e.address, PageSize), "Invalid extent address!");
assert(isAligned(e.size, PageSize), "Invalid extent size!");
assert(e.hpd.address is alignDown(e.address, HugePageSize),
"Invalid hpd!");
assert(pages > 0 && pages <= PageCount, "Invalid number of pages!");

uint currentPages = cast(uint) (e.size / PageSize);
assert(currentPages > pages, "Invalid shrink pages!");
Copy link
Contributor

Choose a reason for hiding this comment

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

e.pageCount


uint delta = currentPages - pages;

uint n = ((cast(size_t) e.address) / PageSize) % PageCount;
Copy link
Contributor

Choose a reason for hiding this comment

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

e.hpdIndex

uint index = n + pages;

auto newSize = pages * PageSize;
emap.clear(e.address + newSize, e.size - newSize);

mutex.lock();
scope(exit) mutex.unlock();

// Shrink:
(cast(Arena*) &this).shrinkAllocImpl(e, index, pages, delta);
}

private:
Extent* allocPagesImpl(uint pages, ulong mask, ExtentClass ec) {
assert(mutex.isHeld(), "Mutex not held!");
Expand Down Expand Up @@ -338,6 +392,22 @@ private:
unusedExtents.insert(e);
}

void shrinkAllocImpl(Extent* e, uint index, uint pages, uint delta) {
assert(mutex.isHeld(), "Mutex not held!");
assert(index <= PageCount - pages, "Invalid index!");
Copy link
Contributor

Choose a reason for hiding this comment

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

Not a blocker, but index > 0.

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Thank you, fixed.

assert(pages > 0 && pages <= PageCount, "Invalid number of pages!");
assert(delta > 0, "Invalid delta!");

auto hpd = e.hpd;
unregisterHPD(hpd);

e.at(e.address, pages * PageSize, hpd);
hpd.clear(index, delta);

assert(!hpd.empty);
registerHPD(hpd);
dsm9000 marked this conversation as resolved.
Show resolved Hide resolved
Comment on lines +383 to +390
Copy link
Contributor

Choose a reason for hiding this comment

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

This is great how clean this became.

}

/**
* HugePageDescriptor heaps management.
*/
Expand Down Expand Up @@ -575,3 +645,137 @@ unittest allocHuge {
arena.freePages(e3);
arena.freePages(e4);
}

unittest resizeLarge {
import d.gc.arena;
shared Arena arena;

auto base = &arena.base;
scope(exit) arena.base.clear();

import d.gc.emap;
static shared ExtentMap emap;
emap.tree.base = base;

import d.gc.region;
shared RegionAllocator regionAllocator;
regionAllocator.base = base;

arena.regionAllocator = &regionAllocator;

// Allocation 0: 35 pages:
auto ptr0 = arena.allocLarge(&emap, 35 * PageSize);
assert(ptr0 !is null);
auto pd0 = emap.lookup(ptr0);
assert(pd0.extent.address is ptr0);
assert(pd0.extent.size == 35 * PageSize);
auto pd0x = emap.lookup(ptr0);
assert(pd0x.extent.address is ptr0);

// Allocation 1: 20 pages:
auto ptr1 = arena.allocLarge(&emap, 20 * PageSize);
assert(ptr1 !is null);
assert(ptr1 is ptr0 + 35 * PageSize);
auto pd1 = emap.lookup(ptr1);
assert(pd1.extent.address is ptr1);
assert(pd1.extent.size == 20 * PageSize);

// Growing by zero is allowed:
assert(arena.resizeLarge(&emap, pd0.extent, 35 * PageSize));

// Shrink no. 0 down to 10 pages:
assert(arena.resizeLarge(&emap, pd0.extent, 10 * PageSize));
assert(pd0.extent.address is ptr0);
assert(pd0.extent.size == 10 * PageSize);
auto pd0xx = emap.lookup(ptr0);
assert(pd0xx.extent.address is ptr0);

// See that newly-last page is mapped:
auto okpd = emap.lookup(ptr0 + 9 * PageSize);
assert(okpd.extent !is null);

// But the page after the newly-last one, should not be mapped:
auto badpd = emap.lookup(ptr0 + 10 * PageSize);
assert(badpd.extent is null);

// Allocate 25 pages + 1 byte, will not fit in the hole after no.0:
auto ptr2 = arena.allocLarge(&emap, 1 + 25 * PageSize);
assert(ptr2 !is null);
auto pd2 = emap.lookup(ptr2);
assert(pd2.extent.address is ptr2);
assert(ptr2 is ptr1 + 20 * PageSize);

// Now allocate precisely 25 pages.
// This new alloc WILL fit in and fill the free space after no. 0:
auto ptr3 = arena.allocLarge(&emap, 25 * PageSize);
assert(ptr3 !is null);
auto pd3 = emap.lookup(ptr3);
assert(pd3.extent.address is ptr3);
assert(ptr3 is ptr0 + 10 * PageSize);

arena.free(&emap, pd0, ptr0);
arena.free(&emap, pd1, ptr1);
arena.free(&emap, pd2, ptr2);
arena.free(&emap, pd3, ptr3);

// Allocate 128 pages:
auto ptr4 = arena.allocLarge(&emap, 128 * PageSize);
assert(ptr4 !is null);
auto pd4 = emap.lookup(ptr4);
assert(pd4.extent.address is ptr4);

// Allocate 256 pages:
auto ptr5 = arena.allocLarge(&emap, 256 * PageSize);
assert(ptr5 !is null);
auto pd5 = emap.lookup(ptr5);
assert(pd5.extent.address is ptr5);
assert(pd5.extent.hpd == pd4.extent.hpd);

// Allocate 128 pages, hpd full:
auto ptr6 = arena.allocLarge(&emap, 128 * PageSize);
assert(ptr6 !is null);
auto pd6 = emap.lookup(ptr6);
assert(pd6.extent.address is ptr6);
assert(pd6.extent.hpd == pd5.extent.hpd);
assert(pd6.extent.hpd.full);

// Shrink first alloc:
assert(arena.resizeLarge(&emap, pd4.extent, 96 * PageSize));
assert(pd4.extent.size == 96 * PageSize);
assert(!pd6.extent.hpd.full);

// Shrink second alloc:
assert(arena.resizeLarge(&emap, pd5.extent, 128 * PageSize));
assert(pd5.extent.size == 128 * PageSize);

// Shrink third alloc:
assert(arena.resizeLarge(&emap, pd6.extent, 64 * PageSize));
assert(pd6.extent.size == 64 * PageSize);

// Allocate 128 pages, should go after second alloc:
auto ptr7 = arena.allocLarge(&emap, 128 * PageSize);
assert(ptr7 !is null);
auto pd7 = emap.lookup(ptr7);
assert(pd7.extent.address is ptr7);
assert(pd7.extent.hpd == pd6.extent.hpd);
assert(ptr7 is ptr5 + 128 * PageSize);

// Allocate 32 pages, should go after first alloc:
auto ptr8 = arena.allocLarge(&emap, 32 * PageSize);
assert(ptr8 !is null);
auto pd8 = emap.lookup(ptr8);
assert(pd8.extent.address is ptr8);
assert(pd8.extent.hpd == pd7.extent.hpd);
assert(ptr8 is ptr4 + 96 * PageSize);

// Allocate 64 pages, should go after third alloc:
auto ptr9 = arena.allocLarge(&emap, 64 * PageSize);
assert(ptr9 !is null);
auto pd9 = emap.lookup(ptr9);
assert(pd9.extent.address is ptr9);
assert(pd9.extent.hpd == pd8.extent.hpd);
assert(ptr9 is ptr6 + 64 * PageSize);

// Now full again:
assert(pd9.extent.hpd.full);
}
9 changes: 6 additions & 3 deletions sdlib/d/gc/tcache.d
Original file line number Diff line number Diff line change
Expand Up @@ -98,12 +98,13 @@ public:
}
} else {
auto esize = pd.extent.size;
if (samePointerness && alignUp(size, PageSize) == esize) {
if (samePointerness && (alignUp(size, PageSize) == esize
|| (isLargeSize(size)
&& pd.arena.resizeLarge(emap, pd.extent, size)))) {
pd.extent.setUsedCapacity(size);
return ptr;
}

// TODO: Try to extend/shrink in place.
import d.gc.util;
copySize = min(size, pd.extent.usedCapacity);
}
Expand Down Expand Up @@ -440,10 +441,12 @@ unittest getCapacity {
assert(threadCache.getCapacity(p3[0 .. 20000]) == 0);
assert(threadCache.getCapacity(p3[0 .. 20001]) == 20480);

// This realloc happens in-place:
auto p4 = threadCache.realloc(p3, 16000, false);
assert(p4 !is p3);
assert(p4 is p3);
assert(threadCache.getCapacity(p4[0 .. 16000]) == 16384);
Copy link
Contributor

Choose a reason for hiding this comment

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

Is there a test shrink from large to small?


// This one, not:
auto p5 = threadCache.realloc(p4, 20000, false);
assert(p5 !is p4);
assert(threadCache.getCapacity(p5[0 .. 20000]) == 20480);
Expand Down
Loading