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

allocAppendable() and getCapacity(). #284

Merged
merged 35 commits into from
Aug 24, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
35 commits
Select commit Hold shift + click to select a range
3a28488
bare-bones appendables (not tested yet)
dsm9000 Aug 15, 2023
bb26af1
tests; recommended corrections; afaik everything but adjustment on re…
dsm9000 Aug 15, 2023
6cdf9b7
realloc handling for appendables; tests for same
dsm9000 Aug 15, 2023
2afbad9
realloc handling for appendables; tests for same
dsm9000 Aug 15, 2023
781dd37
realloc handling for appendables; tests for same
dsm9000 Aug 15, 2023
af06b86
realloc handling for appendables; tests for same
dsm9000 Aug 15, 2023
fb86c4f
realloc handling for appendables; tests for same
dsm9000 Aug 15, 2023
20850f6
realloc handling for appendables; tests for same (simplified)
dsm9000 Aug 15, 2023
6bcd254
realloc handling for appendables; tests for same (simplified)
dsm9000 Aug 15, 2023
0de7276
realloc handling for appendables; tests for same (simplified)
dsm9000 Aug 15, 2023
2e782e6
realloc handling for appendables (with tests for unknown-to-gc ptrs)
dsm9000 Aug 16, 2023
43b61bd
realloc handling for appendables (with tests for unknown-to-gc ptrs)
dsm9000 Aug 16, 2023
852d98f
corrections
dsm9000 Aug 16, 2023
960ccf6
Merge branch 'master' of github.com:dsm9000/sdc into basic_appendables
dsm9000 Aug 17, 2023
e5c753e
in-place appending api.
dsm9000 Aug 21, 2023
5c276ff
in-place appending api.
dsm9000 Aug 21, 2023
23abbcd
in-place appending api.
dsm9000 Aug 21, 2023
e697fba
in-place appending api. (corrections)
dsm9000 Aug 21, 2023
d4d9899
in-place appending api. (corrections, tests)
dsm9000 Aug 21, 2023
3b439dd
in-place appending api. (corrections, tests)
dsm9000 Aug 21, 2023
f8d87ba
getCapacity(), realloc(), and tests for same; support for initially-e…
dsm9000 Aug 22, 2023
2037ca8
getCapacity(), realloc(), and tests for same; support for initially-e…
dsm9000 Aug 22, 2023
103e7fe
getCapacity(), realloc(), and tests for same; support for initially-e…
dsm9000 Aug 22, 2023
978238c
getCapacity(), realloc(), and tests for same; support for initially-e…
dsm9000 Aug 22, 2023
54dcc43
getCapacity(), realloc(), and tests for same; support for initially-e…
dsm9000 Aug 22, 2023
ef01fee
getCapacity(), realloc(), and tests for same; support for initially-e…
dsm9000 Aug 22, 2023
9066a34
getCapacity() docs
dsm9000 Aug 22, 2023
4e7db41
getCapacity() docs
dsm9000 Aug 22, 2023
f15d0ac
getCapacity() docs
dsm9000 Aug 22, 2023
5bce071
getCapacity() docs
dsm9000 Aug 22, 2023
eac9095
getCapacity() docs
dsm9000 Aug 22, 2023
2cd70e3
getCapacity() corrections
dsm9000 Aug 23, 2023
0c73f63
various corrections
dsm9000 Aug 23, 2023
514481e
various corrections
dsm9000 Aug 23, 2023
733feca
corrections and simplifications suggested by deadalnix.
dsm9000 Aug 24, 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
32 changes: 30 additions & 2 deletions sdlib/d/gc/extent.d
Original file line number Diff line number Diff line change
Expand Up @@ -99,7 +99,15 @@ private:
Links _links;

import d.gc.bitmap;
Bitmap!512 _slabData = void;
union MetaData {
// Slab occupancy (constrained by freeSlots, so usable for all classes).
Bitmap!512 slabData;

// Metadata for large extents.
size_t usedCapacity;
}

MetaData _metadata;

this(uint arenaIndex, void* ptr, size_t size, ubyte generation,
HugePageDescriptor* hpd, ExtentClass ec) {
Expand All @@ -120,6 +128,8 @@ private:
bits |= ulong(binInfos[ec.sizeClass].slots) << 48;

slabData.clear();
} else {
setUsedCapacity(size);
}
}

Expand Down Expand Up @@ -240,7 +250,25 @@ public:
// FIXME: in contract.
assert(isSlab(), "slabData accessed on non slab!");

return _slabData;
return _metadata.slabData;
}

/**
* Large features.
*/
bool isLarge() const {
return !isSlab();
}

@property
ulong usedCapacity() {
assert(isLarge(), "usedCapacity accessed on non large!");
return _metadata.usedCapacity;
}

void setUsedCapacity(size_t size) {
assert(isLarge(), "Cannot set used capacity on a slab alloc!");
_metadata.usedCapacity = size;
}
}

Expand Down
154 changes: 151 additions & 3 deletions sdlib/d/gc/tcache.d
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,20 @@ public:
: arena.allocLarge(emap, size, false);
}

void* allocAppendable(size_t size, bool containsPointers) {
// Force large allocation rather than slab.
enum MinSize = getSizeFromClass(ClassCount.Small);

import d.gc.util;
auto asize = max(MinSize, getAllocSize(size));
auto ptr = alloc(asize, containsPointers);

// Remember the size we actually use.
auto pd = getPageDescriptor(ptr);
pd.extent.setUsedCapacity(size);
return ptr;
}

void* calloc(size_t size, bool containsPointers) {
if (!isAllocatableSize(size)) {
return null;
Expand Down Expand Up @@ -78,14 +92,16 @@ public:
copySize = getSizeFromClass(oldSizeClass);
}
} else {
import d.gc.util;
copySize = min(size, pd.extent.usedCapacity);

auto esize = pd.extent.size;
if (alignUp(size, PageSize) == esize) {
pd.extent.setUsedCapacity(copySize);
return ptr;
}

// TODO: Try to extend/shrink in place.
import d.gc.util;
copySize = min(size, esize);
}

containsPointers = (containsPointers | pd.containsPointers) != 0;
Expand All @@ -94,14 +110,67 @@ public:
return null;
}

if (isLargeSize(size)) {
auto npd = getPageDescriptor(newPtr);
npd.extent.setUsedCapacity(copySize);
}

memcpy(newPtr, ptr, copySize);
pd.arena.free(emap, pd, ptr);

return newPtr;
}

/**
* GC facilities
* Appendable facilities.
*/

/**
* Appendable's mechanics:
*
* __data__ _____free space_______
* / \/ \
* -----sss s....... ....... ........
* \___________________________/
* Capacity is 27
*
* If the slice's end doesn't match the used capacity,
* then we return 0 in order to force a reallocation
* when appending:
*
* ___data____ ____free space_____
* / \/ \
* -----sss s---.... ....... ........
* \___________________________/
* Capacity is 0
*
* See also: https://dlang.org/spec/arrays.html#capacity-reserve
*/
size_t getCapacity(const void[] slice) {
auto pd = maybeGetPageDescriptor(slice.ptr);
if (pd.extent is null) {
return 0;
}

// Appendable slabs are not supported.
if (pd.isSlab()) {
return 0;
}

// Slice must not end before valid data ends, or capacity is zero:
auto startIndex = slice.ptr - pd.extent.address;
auto stopIndex = startIndex + slice.length;

// If the slice end doesn't match the used capacity, bail.
if (stopIndex != pd.extent.usedCapacity) {
return 0;
}

return pd.extent.size - startIndex;
}

/**
* GC facilities.
*/
void addRoots(const void[] range) {
auto ptr = cast(void*) roots.ptr;
Expand Down Expand Up @@ -269,3 +338,82 @@ unittest makeRange {
checkRange(ptr[1 .. 7], 0, 0);
checkRange(ptr[1 .. 8], 8, 8);
}

unittest getCapacity {
// Test capacity for non appendable allocs.
auto nonAppendable = threadCache.alloc(100, false);
assert(threadCache.getCapacity(nonAppendable[0 .. 100]) == 0);

// Capacity of any slice in space unknown to the GC is zero:
void* nullPtr = null;
assert(threadCache.getCapacity(nullPtr[0 .. 100]) == 0);

void* stackPtr = &nullPtr;
assert(threadCache.getCapacity(stackPtr[0 .. 100]) == 0);

void* tlPtr = &threadCache;
assert(threadCache.getCapacity(tlPtr[0 .. 100]) == 0);

// Check capacity for an appendable GC allocation.
auto p0 = threadCache.allocAppendable(100, false);

// p0 is appendable and has the minimum large size.
// Capacity of segment from p0, length 100 is 16384:
assert(threadCache.getCapacity(p0[0 .. 100]) == 16384);
assert(threadCache.getCapacity(p0[1 .. 100]) == 16383);
assert(threadCache.getCapacity(p0[50 .. 100]) == 16334);
assert(threadCache.getCapacity(p0[99 .. 100]) == 16285);
assert(threadCache.getCapacity(p0[100 .. 100]) == 16284);

// If the slice doesn't go the end of the allocated area
// then the capacity must be 0.
assert(threadCache.getCapacity(p0[0 .. 0]) == 0);
assert(threadCache.getCapacity(p0[0 .. 1]) == 0);
assert(threadCache.getCapacity(p0[0 .. 50]) == 0);
assert(threadCache.getCapacity(p0[0 .. 99]) == 0);

assert(threadCache.getCapacity(p0[0 .. 99]) == 0);
assert(threadCache.getCapacity(p0[1 .. 99]) == 0);
assert(threadCache.getCapacity(p0[50 .. 99]) == 0);
assert(threadCache.getCapacity(p0[99 .. 99]) == 0);

// This would almost certainly be a bug in userland,
// but let's make sure be behave reasonably there.
assert(threadCache.getCapacity(p0[0 .. 101]) == 0);
assert(threadCache.getCapacity(p0[1 .. 101]) == 0);
assert(threadCache.getCapacity(p0[50 .. 101]) == 0);
assert(threadCache.getCapacity(p0[100 .. 101]) == 0);
assert(threadCache.getCapacity(p0[101 .. 101]) == 0);

// Realloc.
auto p1 = threadCache.allocAppendable(20000, false);
assert(threadCache.getCapacity(p1[0 .. 19999]) == 0);
assert(threadCache.getCapacity(p1[0 .. 20000]) == 20480);
assert(threadCache.getCapacity(p1[0 .. 20001]) == 0);

// Decreasing the size of the allocation
// should adjust capacity acordingly.
auto p2 = threadCache.realloc(p1, 19999, false);
assert(p2 is p1);

assert(threadCache.getCapacity(p2[0 .. 19999]) == 20480);
assert(threadCache.getCapacity(p2[0 .. 20000]) == 0);
assert(threadCache.getCapacity(p2[0 .. 20001]) == 0);

// Increasing the size of the allocation
// does not necesserly increase capacity.
auto p3 = threadCache.realloc(p2, 20001, false);
assert(p3 is p2);

assert(threadCache.getCapacity(p3[0 .. 19999]) == 20480);
assert(threadCache.getCapacity(p3[0 .. 20000]) == 0);
assert(threadCache.getCapacity(p3[0 .. 20001]) == 0);

auto p4 = threadCache.realloc(p3, 16000, false);
assert(p4 !is p3);
assert(threadCache.getCapacity(p4[0 .. 16000]) == 16384);

auto p5 = threadCache.realloc(p4, 20000, false);
assert(p5 !is p4);
assert(threadCache.getCapacity(p5[0 .. 16000]) == 20480);
}
Loading