Skip to content

Commit c2baec0

Browse files
tanjialiangfacebook-github-bot
authored andcommitted
MallocAllocator: add Options struct and malloc-based contiguous allocations (facebookincubator#16695)
Summary: X-link: facebookincubator/nimble#562 MallocAllocator currently uses mmap/munmap for contiguous allocations despite its name suggesting it delegates everything to malloc. The mmap/munmap syscall pair on every contiguous alloc/free cycle adds overhead. This diff: 1. Adds an `Options` struct to `MallocAllocator` (mirroring `MmapAllocator::Options`) for cleaner configuration. 2. Adds a `mallocContiguousEnabled` option that, when true, uses `aligned_alloc`/`free` for contiguous allocations instead of `mmap`/`munmap`. 3. Wires the option through `MemoryManager::Options`. 4. Updates `CachedBufferedInputTest` to use the new `Options` struct. The option defaults to false to preserve existing behavior. When enabled, contiguous allocations use `aligned_alloc(kPageSize, maxBytes)` which immediately commits physical memory (vs mmap's lazy page faulting), but this is acceptable for MallocAllocator's use case. `growContiguous` requires no changes since both mmap and malloc allocate `maxPages` upfront, so growth stays within already-allocated memory. Reviewed By: duxiao1212, xiaoxmeng Differential Revision: D95875673
1 parent b423dc1 commit c2baec0

File tree

6 files changed

+351
-33
lines changed

6 files changed

+351
-33
lines changed

velox/common/memory/MallocAllocator.cpp

Lines changed: 53 additions & 27 deletions
Original file line numberDiff line numberDiff line change
@@ -21,10 +21,11 @@
2121
#include <sys/mman.h>
2222

2323
namespace facebook::velox::memory {
24-
MallocAllocator::MallocAllocator(size_t capacity, uint32_t reservationByteLimit)
24+
MallocAllocator::MallocAllocator(const Options& options)
2525
: kind_(MemoryAllocator::Kind::kMalloc),
26-
capacity_(capacity),
27-
reservationByteLimit_(reservationByteLimit),
26+
mallocContiguousEnabled_(options.mallocContiguousEnabled),
27+
capacity_(options.capacity),
28+
reservationByteLimit_(options.reservationByteLimit),
2829
reserveFunc_(
2930
[this](uint32_t& counter, uint32_t increment, std::mutex& lock) {
3031
return incrementUsageWithReservationFunc(counter, increment, lock);
@@ -145,11 +146,7 @@ bool MallocAllocator::allocateContiguousImpl(
145146
}
146147
auto numContiguousCollateralPages = allocation.numPages();
147148
if (numContiguousCollateralPages > 0) {
148-
useHugePages(allocation, false);
149-
if (::munmap(allocation.data(), allocation.maxSize()) < 0) {
150-
VELOX_MEM_LOG(ERROR) << "munmap got " << folly::errnoStr(errno) << "for "
151-
<< allocation.data() << ", " << allocation.size();
152-
}
149+
dispatchFreeContiguous(allocation);
153150
numMapped_.fetch_sub(numContiguousCollateralPages);
154151
numAllocated_.fetch_sub(numContiguousCollateralPages);
155152
numExternalMapped_.fetch_sub(numContiguousCollateralPages);
@@ -175,19 +172,23 @@ bool MallocAllocator::allocateContiguousImpl(
175172
numAllocated_.fetch_add(numPages);
176173
numMapped_.fetch_add(numPages);
177174
numExternalMapped_.fetch_add(numPages);
178-
void* data = ::mmap(
179-
nullptr,
180-
AllocationTraits::pageBytes(maxPages),
181-
PROT_READ | PROT_WRITE,
182-
MAP_PRIVATE | MAP_ANONYMOUS,
183-
-1,
184-
0);
185-
// TODO: add handling of MAP_FAILED.
186-
allocation.set(
187-
data,
188-
AllocationTraits::pageBytes(numPages),
189-
AllocationTraits::pageBytes(maxPages));
190-
useHugePages(allocation, true);
175+
const auto maxBytes = AllocationTraits::pageBytes(maxPages);
176+
void* data = dispatchAllocateContiguous(maxBytes);
177+
if (FOLLY_UNLIKELY(data == nullptr)) {
178+
numAllocated_.fetch_sub(numPages);
179+
numMapped_.fetch_sub(numPages);
180+
numExternalMapped_.fetch_sub(numPages);
181+
decrementUsage(static_cast<int64_t>(AllocationTraits::pageBytes(numPages)));
182+
const auto errorMsg = fmt::format(
183+
"Failed to allocate {} of contiguous memory", succinctBytes(maxBytes));
184+
VELOX_MEM_LOG(WARNING) << errorMsg;
185+
setAllocatorFailureMessage(errorMsg);
186+
return false;
187+
}
188+
allocation.set(data, AllocationTraits::pageBytes(numPages), maxBytes);
189+
if (!mallocContiguousEnabled_) {
190+
useHugePages(allocation, true);
191+
}
191192
return true;
192193
}
193194

@@ -222,21 +223,46 @@ void MallocAllocator::freeContiguousImpl(ContiguousAllocation& allocation) {
222223
if (allocation.empty()) {
223224
return;
224225
}
225-
useHugePages(allocation, false);
226226
const auto bytes = allocation.size();
227227
const auto numPages = allocation.numPages();
228-
if (::munmap(allocation.data(), allocation.maxSize()) < 0) {
229-
VELOX_MEM_LOG(ERROR) << "Error for munmap(" << allocation.data() << ", "
230-
<< succinctBytes(bytes) << "): '"
231-
<< folly::errnoStr(errno) << "'";
232-
}
228+
dispatchFreeContiguous(allocation);
233229
numMapped_.fetch_sub(numPages);
234230
numAllocated_.fetch_sub(numPages);
235231
numExternalMapped_.fetch_sub(numPages);
236232
decrementUsage(bytes);
237233
allocation.clear();
238234
}
239235

236+
void* MallocAllocator::dispatchAllocateContiguous(size_t maxBytes) {
237+
if (testingHasInjectedFailure(InjectedFailure::kAllocate)) {
238+
return nullptr;
239+
}
240+
if (mallocContiguousEnabled_) {
241+
return ::aligned_alloc(AllocationTraits::kPageSize, maxBytes);
242+
}
243+
// TODO: add handling of MAP_FAILED.
244+
return ::mmap(
245+
nullptr,
246+
maxBytes,
247+
PROT_READ | PROT_WRITE,
248+
MAP_PRIVATE | MAP_ANONYMOUS,
249+
-1,
250+
0);
251+
}
252+
253+
void MallocAllocator::dispatchFreeContiguous(ContiguousAllocation& allocation) {
254+
if (mallocContiguousEnabled_) {
255+
::free(allocation.data());
256+
} else {
257+
useHugePages(allocation, false);
258+
if (::munmap(allocation.data(), allocation.maxSize()) < 0) {
259+
VELOX_MEM_LOG(ERROR) << "Error for munmap(" << allocation.data() << ", "
260+
<< succinctBytes(allocation.size()) << "): '"
261+
<< folly::errnoStr(errno) << "'";
262+
}
263+
}
264+
}
265+
240266
bool MallocAllocator::growContiguousWithoutRetry(
241267
MachinePageCount increment,
242268
ContiguousAllocation& allocation) {

velox/common/memory/MallocAllocator.h

Lines changed: 25 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,19 @@ namespace facebook::velox::memory {
2626
/// The implementation of MemoryAllocator using malloc.
2727
class MallocAllocator : public MemoryAllocator {
2828
public:
29-
MallocAllocator(size_t capacity, uint32_t reservationByteLimit);
29+
struct Options {
30+
/// Capacity in bytes, default unlimited.
31+
size_t capacity{kMaxMemory};
32+
33+
/// Allocation size threshold below which allocations use sharded local
34+
/// counters instead of updating the global counter. Default 1MB.
35+
uint32_t reservationByteLimit{1 << 20};
36+
37+
/// If true, use malloc for contiguous allocations instead of mmap/munmap.
38+
bool mallocContiguousEnabled{false};
39+
};
40+
41+
explicit MallocAllocator(const Options& options);
3042

3143
~MallocAllocator() override;
3244

@@ -102,6 +114,15 @@ class MallocAllocator : public MemoryAllocator {
102114
ContiguousAllocation& allocation,
103115
MachinePageCount maxPages);
104116

117+
// Allocates 'maxBytes' of contiguous memory using malloc or mmap depending
118+
// on 'mallocContiguousEnabled_'. Returns the allocated pointer, or nullptr
119+
// on failure.
120+
void* dispatchAllocateContiguous(size_t maxBytes);
121+
122+
// Frees contiguous memory previously allocated by
123+
// dispatchAllocateContiguous.
124+
void dispatchFreeContiguous(ContiguousAllocation& allocation);
125+
105126
void freeContiguousImpl(ContiguousAllocation& allocation);
106127

107128
void* allocateBytesWithoutRetry(uint64_t bytes, uint16_t alignment) override;
@@ -217,6 +238,9 @@ class MallocAllocator : public MemoryAllocator {
217238

218239
const Kind kind_;
219240

241+
// If true, use malloc for contiguous allocations instead of mmap/munmap.
242+
const bool mallocContiguousEnabled_;
243+
220244
// Capacity in bytes. Total allocation byte is not allowed to exceed this
221245
// value.
222246
const size_t capacity_;

velox/common/memory/Memory.cpp

Lines changed: 6 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -54,9 +54,12 @@ std::shared_ptr<MemoryAllocator> createAllocator(
5454
mmapOptions.mmapArenaCapacityRatio = options.mmapArenaCapacityRatio;
5555
return std::make_shared<MmapAllocator>(mmapOptions);
5656
} else {
57-
return std::make_shared<MallocAllocator>(
58-
options.allocatorCapacity,
59-
options.allocationSizeThresholdWithReservation);
57+
MallocAllocator::Options mallocOptions;
58+
mallocOptions.capacity = options.allocatorCapacity;
59+
mallocOptions.reservationByteLimit =
60+
options.allocationSizeThresholdWithReservation;
61+
mallocOptions.mallocContiguousEnabled = options.mallocContiguousEnabled;
62+
return std::make_shared<MallocAllocator>(mallocOptions);
6063
}
6164
}
6265

velox/common/memory/Memory.h

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -139,6 +139,12 @@ class MemoryManager {
139139
/// NOTE: this only applies for MallocAllocator.
140140
uint32_t allocationSizeThresholdWithReservation{1 << 20};
141141

142+
/// If true, MallocAllocator uses malloc for contiguous allocations instead
143+
/// of mmap/munmap.
144+
///
145+
/// NOTE: this only applies for MallocAllocator.
146+
bool mallocContiguousEnabled{false};
147+
142148
/// ================== 'MemoryArbitrator' settings =================
143149

144150
/// Memory capacity available for query/task memory pools. This capacity

0 commit comments

Comments
 (0)