Refactor use of sizeclasses (#415)
The primary aim for this refactor is to use a representation for sizeclasses that uniformly covers both large and small. This allows certain operations such as alloc_size and external_pointer to be uniformly implemented. The additional types make clear which kind of sizeclass is in use. This also tidies up the code for sizeclass based divisible by and modulus. It fixes a bug in rust_realloc that didn't correctly determine a realloc was required for large classes.
This commit is contained in:
committed by
GitHub
parent
02f36a4b31
commit
3d403aef7f
@@ -166,7 +166,7 @@ int main(int, char**)
|
||||
f(5);
|
||||
f(7);
|
||||
printf("\n");
|
||||
for (size_t exp = 1; exp < snmalloc::MAX_SIZECLASS_BITS; exp++)
|
||||
for (size_t exp = 1; exp < snmalloc::MAX_SMALL_SIZECLASS_BITS; exp++)
|
||||
{
|
||||
auto shifted = [exp](size_t v) { return v << exp; };
|
||||
|
||||
|
||||
@@ -147,7 +147,7 @@ int main(int argc, char** argv)
|
||||
|
||||
our_free(nullptr);
|
||||
|
||||
for (sizeclass_t sc = 0; sc < (MAX_SIZECLASS_BITS + 4); sc++)
|
||||
for (smallsizeclass_t sc = 0; sc < (MAX_SMALL_SIZECLASS_BITS + 4); sc++)
|
||||
{
|
||||
const size_t size = bits::one_at_bit(sc);
|
||||
printf("malloc: %zu\n", size);
|
||||
@@ -161,12 +161,13 @@ int main(int argc, char** argv)
|
||||
|
||||
our_free(nullptr);
|
||||
|
||||
for (sizeclass_t sc = 0; sc < NUM_SIZECLASSES; sc++)
|
||||
for (smallsizeclass_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++)
|
||||
{
|
||||
const size_t size = sizeclass_to_size(sc);
|
||||
|
||||
bool overflow = false;
|
||||
for (size_t n = 1; bits::umul(size, n, overflow) <= MAX_SIZECLASS_SIZE;
|
||||
for (size_t n = 1;
|
||||
bits::umul(size, n, overflow) <= MAX_SMALL_SIZECLASS_SIZE;
|
||||
n *= 5)
|
||||
{
|
||||
if (overflow)
|
||||
@@ -178,13 +179,13 @@ int main(int argc, char** argv)
|
||||
test_calloc(0, size, SUCCESS, false);
|
||||
}
|
||||
|
||||
for (sizeclass_t sc = 0; sc < NUM_SIZECLASSES; sc++)
|
||||
for (smallsizeclass_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++)
|
||||
{
|
||||
const size_t size = sizeclass_to_size(sc);
|
||||
test_realloc(our_malloc(size), size, SUCCESS, false);
|
||||
test_realloc(nullptr, size, SUCCESS, false);
|
||||
test_realloc(our_malloc(size), ((size_t)-1) / 2, ENOMEM, true);
|
||||
for (sizeclass_t sc2 = 0; sc2 < NUM_SIZECLASSES; sc2++)
|
||||
for (smallsizeclass_t sc2 = 0; sc2 < NUM_SMALL_SIZECLASSES; sc2++)
|
||||
{
|
||||
const size_t size2 = sizeclass_to_size(sc2);
|
||||
test_realloc(our_malloc(size), size2, SUCCESS, false);
|
||||
@@ -192,13 +193,13 @@ int main(int argc, char** argv)
|
||||
}
|
||||
}
|
||||
|
||||
for (sizeclass_t sc = 0; sc < (MAX_SIZECLASS_BITS + 4); sc++)
|
||||
for (smallsizeclass_t sc = 0; sc < (MAX_SMALL_SIZECLASS_BITS + 4); sc++)
|
||||
{
|
||||
const size_t size = bits::one_at_bit(sc);
|
||||
test_realloc(our_malloc(size), size, SUCCESS, false);
|
||||
test_realloc(nullptr, size, SUCCESS, false);
|
||||
test_realloc(our_malloc(size), ((size_t)-1) / 2, ENOMEM, true);
|
||||
for (sizeclass_t sc2 = 0; sc2 < (MAX_SIZECLASS_BITS + 4); sc2++)
|
||||
for (smallsizeclass_t sc2 = 0; sc2 < (MAX_SMALL_SIZECLASS_BITS + 4); sc2++)
|
||||
{
|
||||
const size_t size2 = bits::one_at_bit(sc2);
|
||||
printf("size1: %zu, size2:%zu\n", size, size2);
|
||||
@@ -213,10 +214,10 @@ int main(int argc, char** argv)
|
||||
test_posix_memalign(((size_t)-1) / 2, 0, EINVAL, true);
|
||||
test_posix_memalign(OS_PAGE_SIZE, sizeof(uintptr_t) / 2, EINVAL, true);
|
||||
|
||||
for (size_t align = sizeof(uintptr_t); align < MAX_SIZECLASS_SIZE * 8;
|
||||
for (size_t align = sizeof(uintptr_t); align < MAX_SMALL_SIZECLASS_SIZE * 8;
|
||||
align <<= 1)
|
||||
{
|
||||
for (sizeclass_t sc = 0; sc < NUM_SIZECLASSES - 6; sc++)
|
||||
for (smallsizeclass_t sc = 0; sc < NUM_SMALL_SIZECLASSES - 6; sc++)
|
||||
{
|
||||
const size_t size = sizeclass_to_size(sc);
|
||||
test_posix_memalign(size, align, SUCCESS, false);
|
||||
|
||||
@@ -236,11 +236,18 @@ void test_external_pointer()
|
||||
// Malloc does not have an external pointer querying mechanism.
|
||||
auto& alloc = ThreadAlloc::get();
|
||||
|
||||
for (uint8_t sc = 0; sc < NUM_SIZECLASSES; sc++)
|
||||
for (uint8_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++)
|
||||
{
|
||||
size_t size = sizeclass_to_size(sc);
|
||||
void* p1 = alloc.alloc(size);
|
||||
|
||||
if (size != alloc.alloc_size(p1))
|
||||
{
|
||||
std::cout << "Requested size: " << size
|
||||
<< " alloc_size: " << alloc.alloc_size(p1) << std::endl;
|
||||
abort();
|
||||
}
|
||||
|
||||
for (size_t offset = 0; offset < size; offset += 17)
|
||||
{
|
||||
void* p2 = pointer_offset(p1, offset);
|
||||
@@ -248,8 +255,9 @@ void test_external_pointer()
|
||||
void* p4 = alloc.external_pointer<End>(p2);
|
||||
if (p1 != p3)
|
||||
{
|
||||
std::cout << "size: " << size << " offset: " << offset << " p1: " << p1
|
||||
<< " p3: " << p3 << std::endl;
|
||||
std::cout << "size: " << size << " alloc_size: " << alloc.alloc_size(p1)
|
||||
<< " offset: " << offset << " p1: " << p1 << " p3: " << p3
|
||||
<< std::endl;
|
||||
}
|
||||
SNMALLOC_CHECK(p1 == p3);
|
||||
if ((size_t)p4 != (size_t)p1 + size - 1)
|
||||
@@ -272,7 +280,11 @@ void check_offset(void* base, void* interior)
|
||||
auto& alloc = ThreadAlloc::get();
|
||||
void* calced_base = alloc.external_pointer((void*)interior);
|
||||
if (calced_base != (void*)base)
|
||||
{
|
||||
std::cout << "Calced base: " << calced_base << " actual base: " << base
|
||||
<< " for interior: " << interior << std::endl;
|
||||
abort();
|
||||
}
|
||||
}
|
||||
|
||||
void check_external_pointer_large(size_t* base)
|
||||
@@ -301,7 +313,7 @@ void test_external_pointer_large()
|
||||
|
||||
for (size_t i = 0; i < count; i++)
|
||||
{
|
||||
size_t b = MAX_SIZECLASS_BITS + 3;
|
||||
size_t b = MAX_SMALL_SIZECLASS_BITS + 3;
|
||||
size_t rand = r.next() & ((1 << b) - 1);
|
||||
size_t size = (1 << 24) + rand;
|
||||
total_size += size;
|
||||
@@ -383,7 +395,7 @@ void test_calloc_large_bug()
|
||||
// Some PALS have special paths for PAGE aligned zeroing of large
|
||||
// allocations. This is a large allocation that is intentionally
|
||||
// not a multiple of page size.
|
||||
const size_t size = (MAX_SIZECLASS_SIZE << 3) - 7;
|
||||
const size_t size = (MAX_SMALL_SIZECLASS_SIZE << 3) - 7;
|
||||
|
||||
void* p1 = alloc.alloc<YesZero>(size);
|
||||
SNMALLOC_CHECK(alloc.alloc_size(alloc.external_pointer(p1)) >= size);
|
||||
@@ -415,7 +427,7 @@ void test_static_sized_allocs()
|
||||
{
|
||||
// For each small, medium, and large class, do each kind dealloc. This is
|
||||
// mostly to ensure that all of these forms compile.
|
||||
for (size_t sc = 0; sc < NUM_SIZECLASSES; sc++)
|
||||
for (size_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++)
|
||||
{
|
||||
// test_static_sized_alloc<sc, 0>();
|
||||
// test_static_sized_alloc<sc, 1>();
|
||||
@@ -430,6 +442,32 @@ void test_static_sized_allocs()
|
||||
// test_static_sized_alloc<large_sizeclass_to_size(0), 2>();
|
||||
}
|
||||
|
||||
void test_remaining_bytes()
|
||||
{
|
||||
auto& alloc = ThreadAlloc::get();
|
||||
for (size_t sc = 0; sc < NUM_SMALL_SIZECLASSES; sc++)
|
||||
{
|
||||
auto size = sizeclass_to_size(sc);
|
||||
char* p = (char*)alloc.alloc(size);
|
||||
for (size_t offset = 0; offset < size; offset++)
|
||||
{
|
||||
auto rem = alloc.remaining_bytes(p + offset);
|
||||
if (rem != (size - offset))
|
||||
{
|
||||
printf(
|
||||
"Allocation size: %zu, Offset: %zu, Remaining bytes: %zu, "
|
||||
"Expected: %zu\n",
|
||||
size,
|
||||
offset,
|
||||
rem,
|
||||
size - offset);
|
||||
abort();
|
||||
}
|
||||
}
|
||||
alloc.dealloc(p);
|
||||
}
|
||||
}
|
||||
|
||||
int main(int argc, char** argv)
|
||||
{
|
||||
setup();
|
||||
@@ -455,12 +493,12 @@ int main(int argc, char** argv)
|
||||
UNUSED(argc);
|
||||
UNUSED(argv);
|
||||
#endif
|
||||
|
||||
test_alloc_dealloc_64k();
|
||||
test_random_allocation();
|
||||
test_calloc();
|
||||
test_double_alloc();
|
||||
#ifndef SNMALLOC_PASS_THROUGH // Depends on snmalloc specific features
|
||||
test_remaining_bytes();
|
||||
test_static_sized_allocs();
|
||||
test_calloc_large_bug();
|
||||
test_external_pointer_dealloc_bug();
|
||||
|
||||
@@ -17,35 +17,37 @@ int main(int argc, char** argv)
|
||||
|
||||
bool failed = false;
|
||||
|
||||
for (size_t size_class = 0; size_class < NUM_SIZECLASSES; size_class++)
|
||||
for (size_t size_class = 0; size_class < NUM_SMALL_SIZECLASSES; size_class++)
|
||||
{
|
||||
size_t rsize = sizeclass_to_size((uint8_t)size_class);
|
||||
size_t max_offset = sizeclass_to_slab_size(size_class);
|
||||
sizeclass_t sc = sizeclass_t::from_small_class(size_class);
|
||||
for (size_t offset = 0; offset < max_offset; offset++)
|
||||
{
|
||||
size_t rounded = (offset / rsize) * rsize;
|
||||
size_t mod = offset % rsize;
|
||||
bool mod_0 = (offset % rsize) == 0;
|
||||
|
||||
size_t opt_rounded = round_by_sizeclass(size_class, offset);
|
||||
if (rounded != opt_rounded)
|
||||
size_t opt_mod = index_in_object(sc, offset);
|
||||
if (mod != opt_mod)
|
||||
{
|
||||
std::cout << "rsize " << rsize << " offset " << offset << " opt "
|
||||
<< opt_rounded << " correct " << rounded << std::endl
|
||||
<< opt_mod << " correct " << mod << std::endl
|
||||
<< std::flush;
|
||||
failed = true;
|
||||
}
|
||||
|
||||
bool opt_mod_0 = is_multiple_of_sizeclass(size_class, offset);
|
||||
bool opt_mod_0 = divisible_by_sizeclass(size_class, offset);
|
||||
if (opt_mod_0 != mod_0)
|
||||
{
|
||||
std::cout << "rsize " << rsize << " offset " << offset << " opt_mod "
|
||||
<< opt_mod_0 << " correct " << mod_0 << std::endl
|
||||
std::cout << "rsize " << rsize << " offset " << offset
|
||||
<< " opt_mod0 " << opt_mod_0 << " correct " << mod_0
|
||||
<< std::endl
|
||||
<< std::flush;
|
||||
failed = true;
|
||||
}
|
||||
}
|
||||
if (failed)
|
||||
abort();
|
||||
}
|
||||
if (failed)
|
||||
abort();
|
||||
return 0;
|
||||
}
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
#include <test/setup.h>
|
||||
|
||||
NOINLINE
|
||||
snmalloc::sizeclass_t size_to_sizeclass(size_t size)
|
||||
snmalloc::smallsizeclass_t size_to_sizeclass(size_t size)
|
||||
{
|
||||
return snmalloc::size_to_sizeclass(size);
|
||||
}
|
||||
@@ -15,7 +15,7 @@ void test_align_size()
|
||||
SNMALLOC_CHECK(snmalloc::aligned_size(128, 160) == 256);
|
||||
|
||||
for (size_t size = 1;
|
||||
size < snmalloc::sizeclass_to_size(snmalloc::NUM_SIZECLASSES - 1);
|
||||
size < snmalloc::sizeclass_to_size(snmalloc::NUM_SMALL_SIZECLASSES - 1);
|
||||
size++)
|
||||
{
|
||||
size_t rsize = snmalloc::round_size(size);
|
||||
@@ -39,7 +39,7 @@ void test_align_size()
|
||||
}
|
||||
|
||||
for (size_t alignment_bits = 0;
|
||||
alignment_bits < snmalloc::MAX_SIZECLASS_BITS;
|
||||
alignment_bits < snmalloc::MAX_SMALL_SIZECLASS_BITS;
|
||||
alignment_bits++)
|
||||
{
|
||||
auto alignment = (size_t)1 << alignment_bits;
|
||||
@@ -78,10 +78,11 @@ int main(int, char**)
|
||||
std::cout << "sizeclass |-> [size_low, size_high] " << std::endl;
|
||||
|
||||
size_t slab_size = 0;
|
||||
for (snmalloc::sizeclass_t sz = 0; sz < snmalloc::NUM_SIZECLASSES; sz++)
|
||||
for (snmalloc::smallsizeclass_t sz = 0; sz < snmalloc::NUM_SMALL_SIZECLASSES;
|
||||
sz++)
|
||||
{
|
||||
if (
|
||||
sz < snmalloc::NUM_SIZECLASSES &&
|
||||
sz < snmalloc::NUM_SMALL_SIZECLASSES &&
|
||||
slab_size != snmalloc::sizeclass_to_slab_size(sz))
|
||||
{
|
||||
slab_size = snmalloc::sizeclass_to_slab_size(sz);
|
||||
|
||||
@@ -188,7 +188,7 @@ int main(int, char**)
|
||||
f(5);
|
||||
f(7);
|
||||
printf("\n");
|
||||
for (size_t exp = 1; exp < snmalloc::MAX_SIZECLASS_BITS; exp++)
|
||||
for (size_t exp = 1; exp < snmalloc::MAX_SMALL_SIZECLASS_BITS; exp++)
|
||||
{
|
||||
auto shifted = [exp](size_t v) { return v << exp; };
|
||||
|
||||
|
||||
Reference in New Issue
Block a user