diff --git a/src/ds/bits.h b/src/ds/bits.h index 1ad466db6..0b09b719e 100644 --- a/src/ds/bits.h +++ b/src/ds/bits.h @@ -378,7 +378,9 @@ namespace snmalloc * Map large range of strictly positive integers * into an exponent and mantissa pair. * - * The reverse mapping is given as: + * The reverse mapping is given by first adding one to the value, and then + * extracting the bottom MANTISSA bits as m, and the rest as e. + * Then each value maps as: * * e | m | value * --------------------------------- @@ -391,57 +393,44 @@ namespace snmalloc * smallest exponent and mantissa with a * reverse mapping not less than the value. * + * The e and m in the forward mapping and reverse are not the same, and the + * initial increment in from_exp_mant and the decrement in to_exp_mant + * handle the different ways it is calculating and using the split. + * This is due to the rounding of bits below the mantissa in the + * representation, which is confusing but leads to the fastest code. + * * Does not work for value=0. ***********************************************/ template static size_t to_exp_mant(size_t value) { - value += ((size_t)1 << (LOW_BITS)) - 1; - value >>= LOW_BITS; - - if (MANTISSA_BITS > 0) - { - size_t LEADING_BIT = ((size_t)1 << MANTISSA_BITS) >> 1; - size_t MANTISSA_MASK = ((size_t)1 << MANTISSA_BITS) - 1; + size_t LEADING_BIT = ((size_t)1 << (MANTISSA_BITS + LOW_BITS)) >> 1; + size_t MANTISSA_MASK = ((size_t)1 << MANTISSA_BITS) - 1; - value = value - 1; + value = value - 1; - size_t e = (bits::BITS - clz(value | LEADING_BIT)) - MANTISSA_BITS; - size_t shift_e = (e == 0) ? 0 : e - 1; - size_t m = (value >> shift_e) & MANTISSA_MASK; + size_t e = + bits::BITS - MANTISSA_BITS - LOW_BITS - clz(value | LEADING_BIT); + size_t b = (e == 0) ? 0 : 1; + size_t m = (value >> (LOW_BITS + e - b)) & MANTISSA_MASK; - return (e << MANTISSA_BITS) + m; - } - else - { - return bits::next_pow2_bits(value); - } + return (e << MANTISSA_BITS) + m; } template constexpr static size_t to_exp_mant_const(size_t value) { - value += ((size_t)1 << LOW_BITS) - 1; - value >>= LOW_BITS; + size_t LEADING_BIT = ((size_t)1 << (MANTISSA_BITS + LOW_BITS)) >> 1; + size_t MANTISSA_MASK = ((size_t)1 << MANTISSA_BITS) - 1; - if (MANTISSA_BITS > 0) - { - size_t LEADING_BIT = (size_t)1 << (MANTISSA_BITS - 1); - size_t MANTISSA_MASK = ((size_t)1 << MANTISSA_BITS) - 1; - - value = value - 1; + value = value - 1; - size_t e = - (bits::BITS - clz_const(value | LEADING_BIT)) - MANTISSA_BITS; - size_t shift_e = (e == 0) ? 0 : e - 1; - size_t m = (value >> shift_e) & MANTISSA_MASK; + size_t e = + bits::BITS - MANTISSA_BITS - LOW_BITS - clz_const(value | LEADING_BIT); + size_t b = (e == 0) ? 0 : 1; + size_t m = (value >> (LOW_BITS + e - b)) & MANTISSA_MASK; - return (e << MANTISSA_BITS) + m; - } - else - { - return bits::next_pow2_bits_const(value); - } + return (e << MANTISSA_BITS) + m; } template @@ -449,12 +438,13 @@ namespace snmalloc { if (MANTISSA_BITS > 0) { + m_e = m_e + 1; size_t MANTISSA_MASK = ((size_t)1 << MANTISSA_BITS) - 1; size_t m = m_e & MANTISSA_MASK; size_t e = m_e >> MANTISSA_BITS; size_t b = e == 0 ? 0 : 1; size_t shifted_e = e - b; - size_t extended_m = (m + ((size_t)b << MANTISSA_BITS)) + 1; + size_t extended_m = (m + ((size_t)b << MANTISSA_BITS)); return extended_m << (shifted_e + LOW_BITS); } else diff --git a/src/test/func/sizeclass/sizeclass.cc b/src/test/func/sizeclass/sizeclass.cc new file mode 100644 index 000000000..a98c20129 --- /dev/null +++ b/src/test/func/sizeclass/sizeclass.cc @@ -0,0 +1,53 @@ +#include +#include + +NOINLINE +uint8_t size_to_sizeclass(size_t size) +{ + return snmalloc::size_to_sizeclass(size); +} + +int main(int, char**) +{ + bool failed = false; + size_t size_low = 0; + + std::cout << "0 has sizeclass: " << (size_t)snmalloc::size_to_sizeclass(0) + << std::endl; + + std::cout << "sizeclass |-> [size_low, size_high] " << std::endl; + + for (uint8_t sz = 0; sz < snmalloc::NUM_SIZECLASSES; sz++) + { + // Separate printing for small and medium sizeclasses + if (sz == snmalloc::NUM_SMALL_CLASSES) + std::cout << std::endl; + + size_t size = snmalloc::sizeclass_to_size(sz); + std::cout << (size_t)sz << " |-> " + << "[" << size_low + 1 << ", " << size << "]" << std::endl; + + if (size < size_low) + { + std::cout << "Sizeclass " << (size_t)sz << " is " << size + << " which is less than " << size_low << std::endl; + failed = true; + } + + for (size_t i = size_low + 1; i <= size; i++) + { + if (size_to_sizeclass(i) != sz) + { + std::cout << "Size " << i << " has sizeclass " + << (size_t)size_to_sizeclass(i) << " but expected sizeclass " + << (size_t)sz << std::endl; + failed = true; + } + } + + size_low = size; + } + + if (failed) + abort(); +} \ No newline at end of file