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 {
536560namespace detail {
537561
538562template <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
542568template <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