Skip to content

Commit 05b49f0

Browse files
committed
fix: reduce index load memory spike; MAP_POPULATE requires SVS_ENABLE_MAP_POPULATE
1 parent b3558f0 commit 05b49f0

File tree

1 file changed

+43
-20
lines changed

1 file changed

+43
-20
lines changed

include/svs/core/allocator.h

Lines changed: 43 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -20,6 +20,11 @@
2020
/// @file
2121
/// @brief Implements common large-scale allocators used by the many data structures.
2222
///
23+
/// Memory Population Control:
24+
/// Setting environment variable `SVS_ENABLE_MAP_POPULATE` enables MAP_POPULATE for
25+
/// hugepage and file-backed mappings (Linux only). By default population is disabled
26+
/// to avoid transient RSS spikes during large index loads. Use the variable for
27+
/// benchmarking scenarios where early page faulting is desired.
2328

2429
///
2530
/// @ingroup core
@@ -112,6 +117,13 @@ struct HugepageAllocation {
112117
assert(bytes != 0);
113118
void* ptr = MAP_FAILED;
114119
size_t sz = 0;
120+
// Allow disabling page population via environment variable to mitigate peak RSS.
121+
// If SVS_DISABLE_MAP_POPULATE is set to a non-empty value, MAP_POPULATE will not be
122+
// used. Population now disabled by default; opt-in via SVS_ENABLE_MAP_POPULATE.
123+
const bool enable_populate = (std::getenv("SVS_ENABLE_MAP_POPULATE") != nullptr);
124+
// Heuristic: Skip attempting huge pages whose rounded allocation would exceed 2x
125+
// requested. This avoids over-large temporary mappings when the user dataset is
126+
// smaller.
115127
for (auto params : hugepage_x86_options) {
116128
// Don't fallback to huge pages if `force == true`.
117129
if (force && params == hugepage_x86_options.back()) {
@@ -122,6 +134,11 @@ struct HugepageAllocation {
122134
auto flags = params.mmap_flags;
123135
sz = lib::round_up_to_multiple_of(bytes, pagesize);
124136

137+
if (sz > 2 * bytes) {
138+
// Skip this page size; too much waste for transient attempt.
139+
continue;
140+
}
141+
125142
int mmap_flags = MAP_PRIVATE;
126143
#if defined(MAP_ANONYMOUS)
127144
mmap_flags |= MAP_ANONYMOUS;
@@ -132,7 +149,9 @@ struct HugepageAllocation {
132149
#endif
133150
// Add Linux-specific flags
134151
#ifdef __linux__
135-
mmap_flags |= MAP_POPULATE;
152+
if (enable_populate) {
153+
mmap_flags |= MAP_POPULATE;
154+
}
136155
#endif // __linux__
137156

138157
ptr = mmap(nullptr, sz, PROT_READ | PROT_WRITE, mmap_flags | flags, -1, 0);
@@ -362,8 +381,9 @@ template <typename T> class MMapPtr {
362381
// Make the conversion from "void" to non-void "explicit" to avoid nasty unexpected
363382
// implicit conversions.
364383
template <typename OtherT>
365-
requires(std::is_same_v<OtherT, T> || std::is_same_v<OtherT, void>)
366-
MMapPtr(MMapPtr<OtherT>&& other) noexcept
384+
requires(std::is_same_v<OtherT, T> || std::is_same_v<OtherT, void>)
385+
MMapPtr(MMapPtr<OtherT>&& other)
386+
noexcept
367387
: ptr_{reinterpret_cast<T*>(other.ptr_)}
368388
, base_{other.base_}
369389
, size_{other.size_} {
@@ -517,9 +537,13 @@ class MemoryMapper {
517537
int mmap_flags = MAP_SHARED; // Accessible from all processes
518538
// Add Linux-specific flags
519539
#ifdef __linux__
540+
// Respect environment variable disabling population.
541+
const bool enable_populate = (std::getenv("SVS_ENABLE_MAP_POPULATE") != nullptr);
520542
mmap_flags |= MAP_NORESERVE; // Don't reserve space in DRAM for this until used
521-
mmap_flags |= MAP_POPULATE; // Populate page table entries in the DRAM
522-
#endif // __linux__
543+
if (enable_populate) {
544+
mmap_flags |= MAP_POPULATE; // Populate page table entries in the DRAM
545+
}
546+
#endif // __linux__
523547

524548
void* base = ::mmap(
525549
nullptr, bytes.value(), mmap_permissions(permission_), mmap_flags, fd, 0
@@ -536,7 +560,9 @@ class MemoryMapper {
536560
namespace detail {
537561

538562
template <typename Alloc>
539-
concept HasValueType = requires { typename Alloc::value_type; };
563+
concept HasValueType = requires {
564+
typename Alloc::value_type;
565+
};
540566

541567
// clang-format off
542568
template <typename Alloc>
@@ -601,10 +627,9 @@ template <typename T> class AllocatorHandle {
601627
using value_type = T;
602628

603629
template <detail::Allocator Impl>
604-
explicit AllocatorHandle(Impl&& impl)
605-
requires(!std::is_same_v<Impl, AllocatorHandle>) &&
606-
std::is_rvalue_reference_v<Impl&&> &&
607-
std::is_same_v<typename Impl::value_type, T>
630+
explicit AllocatorHandle(Impl&& impl
631+
) requires(!std::is_same_v<Impl, AllocatorHandle>) &&
632+
std::is_rvalue_reference_v<Impl&&>&& std::is_same_v<typename Impl::value_type, T>
608633
: impl_{new AllocatorImpl(std::move(impl))} {}
609634

610635
AllocatorHandle() {}
@@ -622,25 +647,23 @@ template <typename T> class AllocatorHandle {
622647
template <typename U> friend class AllocatorHandle;
623648

624649
template <typename U>
625-
AllocatorHandle(const AllocatorHandle<U>& other)
626-
requires std::is_same_v<T, float> && (!std::is_same_v<U, T>)
650+
AllocatorHandle(const AllocatorHandle<U>& other) requires std::is_same_v<T, float> &&
651+
(!std::is_same_v<U, T>)
627652
: impl_{other.impl_->rebind_float()} {}
628653
template <typename U>
629-
AllocatorHandle(const AllocatorHandle<U>& other)
630-
requires std::is_same_v<T, Float16> && (!std::is_same_v<U, T>)
654+
AllocatorHandle(const AllocatorHandle<U>& other) requires std::is_same_v<T, Float16> &&
655+
(!std::is_same_v<U, T>)
631656
: impl_{other.impl_->rebind_float16()} {}
632657

633658
template <typename U>
634-
AllocatorHandle& operator=(const AllocatorHandle<U>& other)
635-
requires std::is_same_v<T, float> && (!std::is_same_v<U, T>)
636-
{
659+
AllocatorHandle& operator=(const AllocatorHandle<U>& other
660+
) requires std::is_same_v<T, float> &&(!std::is_same_v<U, T>) {
637661
impl_.reset(other.impl_->rebind_float());
638662
return *this;
639663
}
640664
template <typename U>
641-
AllocatorHandle& operator=(const AllocatorHandle<U>& other)
642-
requires std::is_same_v<T, Float16> && (!std::is_same_v<U, T>)
643-
{
665+
AllocatorHandle& operator=(const AllocatorHandle<U>& other
666+
) requires std::is_same_v<T, Float16> &&(!std::is_same_v<U, T>) {
644667
impl_.reset(other.impl_->rebind_float16());
645668
return *this;
646669
}

0 commit comments

Comments
 (0)