diff --git a/common/ImageBuffer.cpp b/common/ImageBuffer.cpp index 3145998b..5b2809b3 100644 --- a/common/ImageBuffer.cpp +++ b/common/ImageBuffer.cpp @@ -6,28 +6,39 @@ #include "JFJochException.h" #include "ZeroCopyReturnValue.h" +#include #include +#include +#include +#include -#ifdef JFJOCH_USE_NUMA -#include -#endif +namespace { + // Zero the buffer in parallel so that each page is first-touched by whichever NUMA node the + // scheduler placed the zeroing thread on. With RAM headroom this approximates an interleaved + // placement for the random-access ring buffer, while also slashing the one-time cost of + // faulting in a 150-200 GB allocation. Replaces numa_alloc_interleaved + a single-threaded + // memset, so this file no longer needs libnuma. + void parallel_first_touch(uint8_t *buffer, size_t buffer_size) { + const unsigned n = std::max(1u, std::thread::hardware_concurrency()); + const size_t chunk = (buffer_size + n - 1) / n; + std::vector threads; + for (size_t begin = 0; begin < buffer_size; begin += chunk) { + size_t len = std::min(chunk, buffer_size - begin); + threads.emplace_back([=] { memset(buffer + begin, 0, len); }); + } + for (auto &t : threads) + t.join(); + } +} ImageBuffer::ImageBuffer(size_t buffer_size_bytes) : buffer_size(buffer_size_bytes) { - -#ifdef JFJOCH_USE_NUMA - // Interleave the buffer across NUMA nodes - throughput-critical path (receiver), Linux-only. - buffer = static_cast(numa_alloc_interleaved(buffer_size)); -#else - // The non-NUMA mapping carried no huge-page / mbind flags, so a plain heap allocation is - // equivalent (and portable). The buffer is zeroed below. buffer = static_cast(std::malloc(buffer_size)); -#endif if (buffer == nullptr) throw JFJochException(JFJochExceptionCategory::MemAllocFailed, "Failed to allocate image buffer"); - memset(buffer, 0, buffer_size); + parallel_first_touch(buffer, buffer_size); } ImageBuffer::~ImageBuffer() { @@ -35,11 +46,7 @@ ImageBuffer::~ImageBuffer() { std::unique_lock ul(m); FinalizeInternal(ul); -#ifdef JFJOCH_USE_NUMA - numa_free(buffer, buffer_size); -#else std::free(buffer); -#endif } void ImageBuffer::StartMeasurement(size_t in_location_size) {