Natural alignment for USE_MALLOC (#248)
* Add concept of natural alignment to tests. snmalloc naturally aligns blocks very heavily, so that the largest power-of-two in the rounded size is the alignment. This checks that in the test, and provides a method for finding the natural alignment of a block. * Improve USE_MALLOC to provide alignment snmalloc provides a lot of alginment guarantees. This ensures that when we pass through to the system allocator we still get those alignment guarantees. The commit also fixes the tests to work with USE_MALLOC, and builds a set of unit tests for ctest to check behaviour.
This commit is contained in:
committed by
GitHub
parent
f89f78ad46
commit
923705e514
@@ -10,6 +10,7 @@
|
||||
#include "../test/histogram.h"
|
||||
#include "allocstats.h"
|
||||
#include "chunkmap.h"
|
||||
#include "external_alloc.h"
|
||||
#include "largealloc.h"
|
||||
#include "mediumslab.h"
|
||||
#include "pooled.h"
|
||||
@@ -125,14 +126,18 @@ namespace snmalloc
|
||||
SNMALLOC_FAST_PATH ALLOCATOR void* alloc()
|
||||
{
|
||||
static_assert(size != 0, "Size must not be zero.");
|
||||
#ifdef USE_MALLOC
|
||||
#ifdef SNMALLOC_PASS_THROUGH
|
||||
static_assert(
|
||||
allow_reserve == YesReserve,
|
||||
"When passing to malloc, cannot require NoResereve");
|
||||
if constexpr (zero_mem == NoZero)
|
||||
return malloc(size);
|
||||
else
|
||||
return calloc(1, size);
|
||||
// snmalloc guarantees a lot of alignment, so we can depend on this
|
||||
// make pass through call aligned_alloc with the alignment snmalloc
|
||||
// would guarantee.
|
||||
void* result = external_alloc::aligned_alloc(
|
||||
natural_alignment(size), round_size(size));
|
||||
if constexpr (zero_mem == YesZero)
|
||||
memset(result, 0, size);
|
||||
return result;
|
||||
#else
|
||||
constexpr sizeclass_t sizeclass = size_to_sizeclass_const(size);
|
||||
|
||||
@@ -162,14 +167,18 @@ namespace snmalloc
|
||||
template<ZeroMem zero_mem = NoZero, AllowReserve allow_reserve = YesReserve>
|
||||
SNMALLOC_FAST_PATH ALLOCATOR void* alloc(size_t size)
|
||||
{
|
||||
#ifdef USE_MALLOC
|
||||
#ifdef SNMALLOC_PASS_THROUGH
|
||||
static_assert(
|
||||
allow_reserve == YesReserve,
|
||||
"When passing to malloc, cannot require NoResereve");
|
||||
if constexpr (zero_mem == NoZero)
|
||||
return malloc(size);
|
||||
else
|
||||
return calloc(1, size);
|
||||
// snmalloc guarantees a lot of alignment, so we can depend on this
|
||||
// make pass through call aligned_alloc with the alignment snmalloc
|
||||
// would guarantee.
|
||||
void* result = external_alloc::aligned_alloc(
|
||||
natural_alignment(size), round_size(size));
|
||||
if constexpr (zero_mem == YesZero)
|
||||
memset(result, 0, size);
|
||||
return result;
|
||||
#else
|
||||
// Perform the - 1 on size, so that zero wraps around and ends up on
|
||||
// slow path.
|
||||
@@ -241,9 +250,9 @@ namespace snmalloc
|
||||
template<size_t size>
|
||||
void dealloc(void* p)
|
||||
{
|
||||
#ifdef USE_MALLOC
|
||||
#ifdef SNMALLOC_PASS_THROUGH
|
||||
UNUSED(size);
|
||||
return free(p);
|
||||
return external_alloc::free(p);
|
||||
#else
|
||||
check_size(p, size);
|
||||
constexpr sizeclass_t sizeclass = size_to_sizeclass_const(size);
|
||||
@@ -281,9 +290,9 @@ namespace snmalloc
|
||||
*/
|
||||
SNMALLOC_FAST_PATH void dealloc(void* p, size_t size)
|
||||
{
|
||||
#ifdef USE_MALLOC
|
||||
#ifdef SNMALLOC_PASS_THROUGH
|
||||
UNUSED(size);
|
||||
return free(p);
|
||||
return external_alloc::free(p);
|
||||
#else
|
||||
SNMALLOC_ASSERT(p != nullptr);
|
||||
check_size(p, size);
|
||||
@@ -327,8 +336,8 @@ namespace snmalloc
|
||||
*/
|
||||
SNMALLOC_FAST_PATH void dealloc(void* p)
|
||||
{
|
||||
#ifdef USE_MALLOC
|
||||
return free(p);
|
||||
#ifdef SNMALLOC_PASS_THROUGH
|
||||
return external_alloc::free(p);
|
||||
#else
|
||||
|
||||
uint8_t size = chunkmap().get(address_cast(p));
|
||||
@@ -397,7 +406,7 @@ namespace snmalloc
|
||||
template<Boundary location = Start>
|
||||
static void* external_pointer(void* p)
|
||||
{
|
||||
#ifdef USE_MALLOC
|
||||
#ifdef SNMALLOC_PASS_THROUGH
|
||||
error("Unsupported");
|
||||
UNUSED(p);
|
||||
#else
|
||||
@@ -463,6 +472,9 @@ namespace snmalloc
|
||||
public:
|
||||
SNMALLOC_FAST_PATH static size_t alloc_size(const void* p)
|
||||
{
|
||||
#ifdef SNMALLOC_PASS_THROUGH
|
||||
return external_alloc::malloc_usable_size(const_cast<void*>(p));
|
||||
#else
|
||||
// This must be called on an external pointer.
|
||||
size_t size = ChunkMap::get(address_cast(p));
|
||||
|
||||
@@ -492,6 +504,7 @@ namespace snmalloc
|
||||
}
|
||||
|
||||
return alloc_size_error();
|
||||
#endif
|
||||
}
|
||||
|
||||
size_t get_id()
|
||||
|
||||
64
src/mem/external_alloc.h
Normal file
64
src/mem/external_alloc.h
Normal file
@@ -0,0 +1,64 @@
|
||||
#pragma once
|
||||
|
||||
#ifdef SNMALLOC_PASS_THROUGH
|
||||
# include <stdlib.h>
|
||||
# if defined(_WIN32) //|| defined(__APPLE__)
|
||||
# error "Pass through not supported on this platform"
|
||||
// The Windows aligned allocation API is not capable of supporting the
|
||||
// snmalloc API Apple was not providing aligned memory in some tests.
|
||||
# else
|
||||
// Defines malloc_size for the platform.
|
||||
# if defined(_WIN32)
|
||||
namespace snmalloc::external_alloc
|
||||
{
|
||||
inline size_t malloc_usable_size(void* ptr)
|
||||
{
|
||||
return _msize(ptr);
|
||||
}
|
||||
}
|
||||
# elif defined(__APPLE__)
|
||||
# include <malloc/malloc.h>
|
||||
namespace snmalloc::external_alloc
|
||||
{
|
||||
inline size_t malloc_usable_size(void* ptr)
|
||||
{
|
||||
return malloc_size(ptr);
|
||||
}
|
||||
}
|
||||
# elif defined(__linux__)
|
||||
# include <malloc.h>
|
||||
namespace snmalloc::external_alloc
|
||||
{
|
||||
using ::malloc_usable_size;
|
||||
}
|
||||
# elif defined(__sun) || defined(__HAIKU__) || defined(__NetBSD__) || \
|
||||
defined(__OpenBSD__)
|
||||
namespace snmalloc::external_alloc
|
||||
{
|
||||
using ::malloc_usable_size;
|
||||
}
|
||||
# elif defined(__FreeBSD__)
|
||||
# include <malloc_np.h>
|
||||
namespace snmalloc::external_alloc
|
||||
{
|
||||
using ::malloc_usable_size;
|
||||
}
|
||||
# else
|
||||
# error Define malloc size macro for this platform.
|
||||
# endif
|
||||
namespace snmalloc::external_alloc
|
||||
{
|
||||
inline void* aligned_alloc(size_t alignment, size_t size)
|
||||
{
|
||||
void* result;
|
||||
if (posix_memalign(&result, alignment, size) != 0)
|
||||
{
|
||||
result = nullptr;
|
||||
}
|
||||
return result;
|
||||
}
|
||||
|
||||
using ::free;
|
||||
}
|
||||
# endif
|
||||
#endif
|
||||
@@ -72,7 +72,7 @@ namespace snmalloc
|
||||
|
||||
void cleanup_unused()
|
||||
{
|
||||
#ifndef USE_MALLOC
|
||||
#ifndef SNMALLOC_PASS_THROUGH
|
||||
// Call this periodically to free and coalesce memory allocated by
|
||||
// allocators that are not currently in use by any thread.
|
||||
// One atomic operation to extract the stack, another to restore it.
|
||||
@@ -102,7 +102,7 @@ namespace snmalloc
|
||||
*/
|
||||
void debug_check_empty(bool* result = nullptr)
|
||||
{
|
||||
#ifndef USE_MALLOC
|
||||
#ifndef SNMALLOC_PASS_THROUGH
|
||||
// This is a debugging function. It checks that all memory from all
|
||||
// allocators has been freed.
|
||||
auto* alloc = Parent::iterate();
|
||||
|
||||
@@ -198,4 +198,12 @@ namespace snmalloc
|
||||
}
|
||||
return sizeclass_to_size(size_to_sizeclass(size));
|
||||
}
|
||||
|
||||
/// Returns the alignment that this size naturally has, that is
|
||||
/// all allocations of size `size` will be aligned to the returned value.
|
||||
SNMALLOC_FAST_PATH static size_t natural_alignment(size_t size)
|
||||
{
|
||||
auto rsize = round_size(size);
|
||||
return bits::one_at_bit(bits::ctz(rsize));
|
||||
}
|
||||
} // namespace snmalloc
|
||||
|
||||
@@ -189,7 +189,7 @@ namespace snmalloc
|
||||
*/
|
||||
static SNMALLOC_FAST_PATH Alloc* get()
|
||||
{
|
||||
# ifdef USE_MALLOC
|
||||
# ifdef SNMALLOC_PASS_THROUGH
|
||||
return get_reference();
|
||||
# else
|
||||
auto*& alloc = get_reference();
|
||||
|
||||
Reference in New Issue
Block a user