Skip to main content

Memory Backends Guide

Overview

Memory backends provide the underlying memory regions that allocators manage. A backend is always created first, then an allocator is constructed on top of it. HSHM supports shared memory, private memory, and GPU memory backends.

Backend Lifecycle

Every backend supports two operations:

  • shm_init() — Create and initialize a new memory region (the owner)
  • shm_attach() — Attach to an existing memory region created by another process

MallocBackend

Wraps malloc for private (non-shared) in-process memory. Useful for single-process tests and allocators that don't need cross-process sharing.

#include "clio_ctp/memory/allocator/buddy_allocator.h"
#include "clio_ctp/memory/backend/malloc_backend.h"

void example() {
ctp::ipc::MallocBackend backend;
size_t heap_size = 128 * 1024 * 1024; // 128 MB
// Reserve room for the allocator object plus the heap it manages.
size_t backend_size = sizeof(ctp::ipc::BuddyAllocator) + heap_size;
backend.shm_init(ctp::ipc::MemoryBackendId(0, 0), backend_size);

// Create an allocator on top of this backend
auto *alloc = backend.MakeAlloc<ctp::ipc::BuddyAllocator>();
(void)alloc;

backend.shm_destroy();
}

PosixShmMmap

The primary backend for cross-process shared memory. Uses shm_open and mmap to create memory-mapped regions accessible by multiple processes.

#include "clio_ctp/memory/backend/posix_shm_mmap.h"

void example() {
// Process 0 (owner): create the shared-memory region by name
ctp::ipc::PosixShmMmap owner;
owner.shm_init(ctp::ipc::MemoryBackendId::GetRoot(),
512 * 1024 * 1024, "my_shm_region");

// Process 1+ : attach to the existing region using the same name
ctp::ipc::PosixShmMmap client;
client.shm_attach("my_shm_region");

client.shm_detach(); // non-owner releases its mapping
owner.shm_destroy(); // owner unmaps and unlinks the region
}

Ownership model: The process that calls shm_init() is the owner and is responsible for cleanup. Use SetOwner() / UnsetOwner() to transfer ownership between processes.

GpuMalloc

Allocates memory directly on the GPU using cudaMalloc (CUDA) or hipMalloc (ROCm).

// Only available when CTP_ENABLE_CUDA or CTP_ENABLE_ROCM is set
#include "clio_ctp/memory/backend/gpu_malloc.h"

void example() {
#if CTP_ENABLE_CUDA || CTP_ENABLE_ROCM
ctp::ipc::GpuMalloc backend;
size_t data_capacity = 1024 * 1024; // 1 MB (enforced minimum)
backend.shm_init(ctp::ipc::MemoryBackendId(0, 0), data_capacity,
"gpu_region", /*gpu_id=*/0);
backend.shm_destroy();
#endif
}

Memory Layout:

GPU Memory: [MemoryBackendHeader | GpuMallocPrivateHeader | Data...]

Characteristics:

  • Allocates entire region on GPU via GpuApi::Malloc()
  • Creates an IPC handle (GpuIpcMemHandle) for cross-process GPU memory sharing
  • Enforces minimum 1MB data size
  • Freed via GpuApi::Free()
  • Conditionally compiled: #if CTP_ENABLE_CUDA || CTP_ENABLE_ROCM

GpuShmMmap

GPU-accessible POSIX shared memory. Combines host shared memory with GPU registration for zero-copy GPU access.

// Only available when CTP_ENABLE_CUDA, CTP_ENABLE_ROCM, or CTP_ENABLE_SYCL is set
#include "clio_ctp/memory/backend/gpu_shm_mmap.h"

void example() {
#if CTP_ENABLE_CUDA || CTP_ENABLE_ROCM || CTP_ENABLE_SYCL
ctp::ipc::GpuShmMmap backend;
size_t backend_size = 1024 * 1024; // 1 MB (enforced minimum)
backend.shm_init(ctp::ipc::MemoryBackendId(0, 0), backend_size,
"gpu_shm_region", /*gpu_id=*/0);
backend.shm_destroy();
#endif
}

Memory Layout:

POSIX SHM File: [4KB backend header | 4KB shared header | Data...]
Virtual Memory: [4KB private header | 4KB shared header | Data...]

Characteristics:

  • Creates POSIX shared memory object (shm_open)
  • Maps with combined private/shared access (MapMixedMemory)
  • Registers memory with GPU via GpuApi::RegisterHostMemory()
  • GPU can access the memory directly without explicit transfers
  • Supports shm_attach() for other processes to join
  • Enforces minimum 1MB backend size
  • Conditionally compiled: #if CTP_ENABLE_CUDA || CTP_ENABLE_ROCM

Key Difference from GpuMalloc:

  • Memory lives on the host (CPU) but is GPU-accessible
  • Inherently shareable via POSIX shared memory (no IPC handle needed)
  • Better for data that both CPU and GPU need to access

GPU Compatibility

GpuApi

The GpuApi class provides an abstraction over CUDA and ROCm:

MethodDescription
GpuApi::Malloc(size)Allocate GPU memory
GpuApi::Free(ptr)Free GPU memory
GpuApi::Memcpy(dst, src, size, kind)Copy memory between host/device
GpuApi::RegisterHostMemory(ptr, size)Register host memory for GPU access
GpuApi::UnregisterHostMemory(ptr)Unregister host memory
GpuApi::GetIpcMemHandle(ptr)Get IPC handle for GPU memory sharing

Conditional Compilation

GPU backends are only compiled when CUDA or ROCm is enabled:

#if CTP_ENABLE_CUDA || CTP_ENABLE_ROCM
// GPU-specific code
#endif

#if CTP_IS_HOST
// Host-only operations (initialization, IPC setup)
#endif

#if CTP_IS_GPU
// GPU kernel operations
#endif