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

Experimental message passing based pool implementation #4585

Open
wants to merge 3 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
2 changes: 1 addition & 1 deletion .github/workflows/pr.yml
Original file line number Diff line number Diff line change
Expand Up @@ -448,7 +448,7 @@ jobs:
directives: dtrace
- image: ghcr.io/ponylang/ponyc-ci-x86-64-unknown-linux-ubuntu24.04-builder:20250115
debugger: lldb
directives: pool_memalign
directives: pool_message_passing
- image: ghcr.io/ponylang/ponyc-ci-x86-64-unknown-linux-ubuntu24.04-builder:20250115
debugger: lldb
directives: runtimestats
Expand Down
5 changes: 5 additions & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -104,6 +104,11 @@ if(PONY_USE_POOL_MEMALIGN)
add_compile_options(-DUSE_POOL_MEMALIGN)
endif()

if(PONY_USE_POOL_MESSAGE_PASSING)
set(PONY_OUTPUT_SUFFIX "${PONY_OUTPUT_SUFFIX}-pool_message_passing")
add_compile_options(-DUSE_POOL_MESSAGE_PASSING)
endif()

# LibPonyC tests assume that our outputs are two directories above the root directory.
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_DEBUG "${CMAKE_BINARY_DIR}/../debug${PONY_OUTPUT_SUFFIX}")
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY_RELEASE "${CMAKE_BINARY_DIR}/../release${PONY_OUTPUT_SUFFIX}")
Expand Down
2 changes: 2 additions & 0 deletions Makefile
Original file line number Diff line number Diff line change
Expand Up @@ -148,6 +148,8 @@ define USE_CHECK
PONY_USES += -DPONY_USE_RUNTIMESTATS_MESSAGES=true
else ifeq ($1,pool_memalign)
PONY_USES += -DPONY_USE_POOL_MEMALIGN=true
else ifeq ($1,pool_message_passing)
PONY_USES += -DPONY_USE_POOL_MESSAGE_PASSING=true
else
$$(error ERROR: Unknown use option specified: $1)
endif
Expand Down
7 changes: 0 additions & 7 deletions benchmark/libponyrt/mem/pool.cc
Original file line number Diff line number Diff line change
Expand Up @@ -4,13 +4,6 @@

#define LARGE_ALLOC ponyint_pool_adjust_size(POOL_MAX + 1)

/// When we mmap, pull at least this many bytes.
#ifdef PLATFORM_IS_ILP32
#define POOL_MMAP (16 * 1024 * 1024) // 16 MB
#else
#define POOL_MMAP (128 * 1024 * 1024) // 128 MB
#endif

typedef char block_t[32];

class PoolBench: public ::benchmark::Fixture
Expand Down
1 change: 1 addition & 0 deletions src/libponyrt/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -40,6 +40,7 @@ set(_c_src
mem/pagemap.c
mem/pool.c
mem/pool_memalign.c
mem/pool_message_passing.c
options/options.c
platform/ponyassert.c
platform/threads.c
Expand Down
83 changes: 82 additions & 1 deletion src/libponyrt/mem/alloc.c
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
#ifdef __linux__
#define _GNU_SOURCE
#endif
#include "ponyassert.h"
#include "alloc.h"
#include <platform.h>
#include <string.h>
#include <stdio.h>
Expand Down Expand Up @@ -55,11 +57,90 @@ void* ponyint_virt_alloc(size_t bytes)
return p;
}

#ifdef POOL_USE_MESSAGE_PASSING
void* ponyint_virt_alloc_aligned(size_t size)
{
// size must be a multiple of POOL_MMAP.
pony_assert((size & (POOL_MMAP - 1)) == 0);

// NOTE: This can likely be made more efficient depending on the system to
// avoid the extra allocation/freeing to get alingment.

// overallocate to ensure we can align.
size_t bytes = size + POOL_MMAP;

void* p;
bool ok = true;

#if defined(PLATFORM_IS_WINDOWS)
// This is not supported on Windows at the moment.
pony_assert(false);
p = VirtualAlloc(NULL, bytes, MEM_COMMIT | MEM_RESERVE, PAGE_READWRITE);
if(p == NULL)
ok = false;
#elif defined(PLATFORM_IS_POSIX_BASED)
#if defined(PLATFORM_IS_LINUX) || defined(PLATFORM_IS_EMSCRIPTEN)
p = mmap(0, bytes, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANONYMOUS | MAP_NORESERVE, -1, 0);
#elif defined(PLATFORM_IS_MACOSX)
p = mmap(0, bytes, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
#elif defined(PLATFORM_IS_DRAGONFLY)
p = mmap(0, bytes, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
#elif defined(PLATFORM_IS_OPENBSD)
p = mmap(0, bytes, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON, -1, 0);
#elif defined(PLATFORM_IS_BSD)
#ifndef MAP_ALIGNED_SUPER
#define MAP_ALIGNED_SUPER 0
#endif
p = mmap(0, bytes, PROT_READ | PROT_WRITE,
MAP_PRIVATE | MAP_ANON | MAP_ALIGNED_SUPER, -1, 0);
#endif
if(p == MAP_FAILED)
ok = false;

void* oldp = p;

// Align the pointer to first POOL_MMAP multiple in allocation.
p = (void*)(((uintptr_t)p + POOL_MMAP) & ~(POOL_MMAP - 1));

// Free the memory before the aligned pointer.
ponyint_virt_free(oldp, ((uintptr_t)p) - ((uintptr_t)oldp));

// Free the memory after the aligned pointer region end.
void* endp = (void*)((uintptr_t)p + size);
ponyint_virt_free(endp, ((uintptr_t)oldp + bytes) - ((uintptr_t)endp));

// ensure that the pointer is correctly aligned and the size is correct.
pony_assert((((uintptr_t)endp) - ((uintptr_t)p)) == size);
pony_assert((((uintptr_t)p) & (POOL_MMAP - 1)) == 0);
#endif

if(!ok)
{
perror("out of memory: ");
abort();
}

return p;
}
#endif

void ponyint_virt_free(void* p, size_t bytes)
{
#if defined(PLATFORM_IS_WINDOWS)
VirtualFree(p, 0, MEM_RELEASE);
#elif defined(PLATFORM_IS_POSIX_BASED)
munmap(p, bytes);
if (0 == bytes)
return;

int r = munmap(p, bytes);
if(0 != r)
{
perror("unable to free memory: ");
abort();
}
#endif
}
10 changes: 10 additions & 0 deletions src/libponyrt/mem/alloc.h
Original file line number Diff line number Diff line change
@@ -1,11 +1,21 @@
#ifndef mem_alloc_h
#define mem_alloc_h

#include "pool.h"

/**
* Allocates memory in the virtual address space.
*/
void* ponyint_virt_alloc(size_t bytes);

#ifdef POOL_USE_MESSAGE_PASSING
/**
* Allocates memory in the virtual address space aligned to POOL_MMAP.
* All allocations are required to be a multiple of POOL_MMAP.
*/
void* ponyint_virt_alloc_aligned(size_t bytes);
#endif

/**
* Deallocates a chunk of memory that was previously allocated with
* ponyint_virt_alloc.
Expand Down
11 changes: 0 additions & 11 deletions src/libponyrt/mem/pool.c
Original file line number Diff line number Diff line change
Expand Up @@ -30,17 +30,6 @@
#include <valgrind/helgrind.h>
#endif

/// When we mmap, pull at least this many bytes.
#ifdef PLATFORM_IS_ILP32
# define POOL_MMAP (16 * 1024 * 1024) // 16 MB
#else
# ifdef PLATFORM_IS_WINDOWS
# define POOL_MMAP (16 * 1024 * 1024) // 16 MB
# else
# define POOL_MMAP (128 * 1024 * 1024) // 128 MB
# endif
#endif

/// An item on a per-size thread-local free list.
typedef struct pool_item_t
{
Expand Down
22 changes: 21 additions & 1 deletion src/libponyrt/mem/pool.h
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,28 @@

#if defined(USE_POOL_MEMALIGN)
# define POOL_USE_MEMALIGN
#elif defined(USE_POOL_MESSAGE_PASSING)
# define POOL_USE_MESSAGE_PASSING
#else
# define POOL_USE_DEFAULT
#endif

/// When we mmap, pull at least this many bytes.
#ifdef PLATFORM_IS_ILP32
# define POOL_MMAP (16 * 1024 * 1024) // 16 MB
#else
# ifdef PLATFORM_IS_WINDOWS
# define POOL_MMAP (16 * 1024 * 1024) // 16 MB
# else
# define POOL_MMAP (128 * 1024 * 1024) // 128 MB
# endif
#endif

PONY_EXTERN_C_BEGIN

/* Because of the way free memory is reused as its own linked list container,
* the minimum allocation size is 32 bytes for the default pool implementation
* and 16 bytes for the memalign pool implementation.
* and 16 bytes for the memalign and message passing pool implementations.
*/

#ifndef POOL_USE_DEFAULT
Expand Down Expand Up @@ -101,6 +114,13 @@ size_t ponyint_pool_adjust_size(size_t size);
#define POOL_SIZE(INDEX) \
((size_t)1 << (POOL_MIN_BITS + INDEX))

#ifdef POOL_USE_MESSAGE_PASSING
typedef struct pool_remote_allocs_t pool_remote_allocs_t;
pool_remote_allocs_t* ponyint_initialize_pool_message_passing();
void ponyint_pool_gather_receive_remote_allocations(pool_remote_allocs_t* remote_allocs);
void ponyint_pool_receive_remote_allocations(pool_remote_allocs_t* remote_allocs);
#endif

#ifdef USE_RUNTIMESTATS
#define POOL_ALLOC_SIZE(TYPE) \
POOL_SIZE(POOL_INDEX(sizeof(TYPE)))
Expand Down
Loading
Loading