Skip to content

Commit e66b684

Browse files
authored
[0.7] [MOD-7297] Replace Variable Length Array on stack with heap allocation (#761)
* [MOD-7297] Replace Variable Length Array on stack with heap allocation (#505) * vaseline for BM * fix to baseline * replace stack with allocation * use unique ptr * revert format batch iterator * fix lifetime * fix * fix * fix * rearrange * use ref to allocator instead of pointer * disable flow temp (cherry picked from commit ab96a8d) * introdcue getAlignment * cmake
1 parent c761e6f commit e66b684

File tree

8 files changed

+71
-31
lines changed

8 files changed

+71
-31
lines changed

.install/install_cmake.sh

Lines changed: 5 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,7 +6,11 @@ MODE=$1 # whether to install using sudo or not
66

77
if [[ $OS_TYPE = 'Darwin' ]]
88
then
9-
brew install cmake
9+
if ! command -v cmake &> /dev/null; then
10+
brew install cmake
11+
else
12+
echo "cmake already installed"
13+
fi
1014
else
1115
if [[ $processor = 'x86_64' ]]
1216
then

src/VecSim/algorithms/hnsw/hnsw.h

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1775,9 +1775,9 @@ AddVectorCtx HNSWIndex<DataType, DistType>::storeNewElement(labelType label,
17751775
// Create the new element's graph metadata.
17761776
// We must assign manually enough memory on the stack and not just declare an `ElementGraphData`
17771777
// variable, since it has a flexible array member.
1778-
char tmpData[this->elementGraphDataSize];
1779-
memset(tmpData, 0, this->elementGraphDataSize);
1780-
ElementGraphData *cur_egd = (ElementGraphData *)tmpData;
1778+
auto tmpData = this->allocator->allocate_unique(this->elementGraphDataSize);
1779+
memset(tmpData.get(), 0, this->elementGraphDataSize);
1780+
ElementGraphData *cur_egd = (ElementGraphData *)(tmpData.get());
17811781
// Allocate memory (inside `ElementGraphData` constructor) for the links in higher levels and
17821782
// initialize this memory to zeros. The reason for doing it here is that we might mark this
17831783
// vector as deleted BEFORE we finish its indexing. In that case, we will collect the incoming

src/VecSim/algorithms/hnsw/hnsw_serializer.h

Lines changed: 8 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -176,15 +176,15 @@ void HNSWIndex<DataType, DistType>::restoreGraph(std::ifstream &input, EncodingV
176176
unsigned int block_len = 0;
177177
readBinaryPOD(input, block_len);
178178
for (size_t j = 0; j < block_len; j++) {
179-
char cur_vec[this->dataSize];
180-
input.read(cur_vec, this->dataSize);
181-
this->vectorBlocks.back().addElement(cur_vec);
179+
auto cur_vec = this->getAllocator()->allocate_unique(this->dataSize);
180+
input.read(static_cast<char *>(cur_vec.get()), this->dataSize);
181+
this->vectorBlocks.back().addElement(cur_vec.get());
182182
}
183183
}
184184

185185
// Get graph data blocks
186186
ElementGraphData *cur_egt;
187-
char tmpData[this->elementGraphDataSize];
187+
auto tmpData = this->getAllocator()->allocate_unique(this->elementGraphDataSize);
188188
size_t toplevel = 0;
189189
for (size_t i = 0; i < num_blocks; i++) {
190190
this->graphDataBlocks.emplace_back(this->blockSize, this->elementGraphDataSize,
@@ -193,19 +193,20 @@ void HNSWIndex<DataType, DistType>::restoreGraph(std::ifstream &input, EncodingV
193193
readBinaryPOD(input, block_len);
194194
for (size_t j = 0; j < block_len; j++) {
195195
// Reset tmpData
196-
memset(tmpData, 0, this->elementGraphDataSize);
196+
memset(tmpData.get(), 0, this->elementGraphDataSize);
197197
// Read the current element top level
198198
readBinaryPOD(input, toplevel);
199199
// Allocate space and structs for the current element
200200
try {
201-
new (tmpData) ElementGraphData(toplevel, this->levelDataSize, this->allocator);
201+
new (tmpData.get())
202+
ElementGraphData(toplevel, this->levelDataSize, this->allocator);
202203
} catch (std::runtime_error &e) {
203204
this->log(VecSimCommonStrings::LOG_WARNING_STRING,
204205
"Error - allocating memory for new element failed due to low memory");
205206
throw e;
206207
}
207208
// Add the current element to the current block, and update cur_egt to point to it.
208-
this->graphDataBlocks.back().addElement(tmpData);
209+
this->graphDataBlocks.back().addElement(tmpData.get());
209210
cur_egt = (ElementGraphData *)this->graphDataBlocks.back().getElement(j);
210211

211212
// Restore the current element's graph data

src/VecSim/algorithms/hnsw/hnsw_tiered.h

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -524,11 +524,12 @@ void TieredHNSWIndex<DataType, DistType>::executeInsertJob(HNSWInsertJob *job) {
524524
HNSWIndex<DataType, DistType> *hnsw_index = this->getHNSWIndex();
525525
// Copy the vector blob from the flat buffer, so we can release the flat lock while we are
526526
// indexing the vector into HNSW index.
527-
DataType blob_copy[this->frontendIndex->getDim()];
528-
memcpy(blob_copy, this->frontendIndex->getDataByInternalId(job->id),
527+
auto blob_copy = this->getAllocator()->allocate_unique(this->frontendIndex->getDataSize());
528+
529+
memcpy(blob_copy.get(), this->frontendIndex->getDataByInternalId(job->id),
529530
this->frontendIndex->getDim() * sizeof(DataType));
530531

531-
this->insertVectorToHNSW<true>(hnsw_index, job->label, blob_copy);
532+
this->insertVectorToHNSW<true>(hnsw_index, job->label, blob_copy.get());
532533

533534
// Remove the vector and the insert job from the flat buffer.
534535
this->flatIndexGuard.lock();

src/VecSim/memory/vecsim_malloc.cpp

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -99,6 +99,17 @@ void *VecSimAllocator::callocate(size_t size) {
9999
return nullptr;
100100
}
101101

102+
std::unique_ptr<void, VecSimAllocator::Deleter>
103+
VecSimAllocator::allocate_aligned_unique(size_t size, size_t alignment) {
104+
void *ptr = this->allocate_aligned(size, alignment);
105+
return {ptr, Deleter(*this)};
106+
}
107+
108+
std::unique_ptr<void, VecSimAllocator::Deleter> VecSimAllocator::allocate_unique(size_t size) {
109+
void *ptr = this->allocate(size);
110+
return {ptr, Deleter(*this)};
111+
}
112+
102113
void *VecSimAllocator::operator new(size_t size) { return vecsim_malloc(size); }
103114

104115
void *VecSimAllocator::operator new[](size_t size) { return vecsim_malloc(size); }

src/VecSim/memory/vecsim_malloc.h

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -25,6 +25,8 @@ struct VecSimAllocator {
2525
static size_t allocation_header_size;
2626
static VecSimMemoryFunctions memFunctions;
2727

28+
// Forward declaration of the deleter for the unique_ptr.
29+
struct Deleter;
2830
VecSimAllocator() : allocated(std::atomic_uint64_t(sizeof(VecSimAllocator))) {}
2931

3032
public:
@@ -36,6 +38,10 @@ struct VecSimAllocator {
3638
void *reallocate(void *p, size_t size);
3739
void free_allocation(void *p);
3840

41+
// Allocations for scope-life-time memory.
42+
std::unique_ptr<void, Deleter> allocate_aligned_unique(size_t size, size_t alignment);
43+
std::unique_ptr<void, Deleter> allocate_unique(size_t size);
44+
3945
void *operator new(size_t size);
4046
void *operator new[](size_t size);
4147
void operator delete(void *p, size_t size);
@@ -55,8 +61,14 @@ struct VecSimAllocator {
5561
static size_t getAllocationOverheadSize() { return allocation_header_size; }
5662

5763
private:
58-
// Retrive the original requested allocation size. Required for remalloc.
64+
// Retrieve the original requested allocation size. Required for remalloc.
5965
inline size_t getPointerAllocationSize(void *p) { return *(((size_t *)p) - 1); }
66+
67+
struct Deleter {
68+
VecSimAllocator &allocator;
69+
explicit constexpr Deleter(VecSimAllocator &allocator) : allocator(allocator) {}
70+
void operator()(void *ptr) const { allocator.free_allocation(ptr); }
71+
};
6072
};
6173

6274
/**

src/VecSim/vec_sim_index.h

Lines changed: 13 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -106,6 +106,7 @@ struct VecSimIndexAbstract : public VecSimIndexInterface {
106106
inline VecSimMetric getMetric() const { return metric; }
107107
inline size_t getDataSize() const { return dataSize; }
108108
inline size_t getBlockSize() const { return blockSize; }
109+
inline auto getAlignment() const { return alignment; }
109110
virtual inline VecSimIndexStatsInfo statisticInfo() const override {
110111
return VecSimIndexStatsInfo{
111112
.memory = this->getAllocationSize(),
@@ -214,33 +215,37 @@ struct VecSimIndexAbstract : public VecSimIndexInterface {
214215

215216
protected:
216217
virtual int addVectorWrapper(const void *blob, labelType label, void *auxiliaryCtx) override {
217-
char PORTABLE_ALIGN aligned_mem[this->dataSize];
218-
const void *processed_blob = processBlob(blob, aligned_mem);
218+
auto aligned_mem =
219+
this->getAllocator()->allocate_aligned_unique(this->dataSize, this->alignment);
220+
const void *processed_blob = processBlob(blob, aligned_mem.get());
219221

220222
return this->addVector(processed_blob, label, auxiliaryCtx);
221223
}
222224

223225
virtual VecSimQueryReply *topKQueryWrapper(const void *queryBlob, size_t k,
224226
VecSimQueryParams *queryParams) const override {
225-
char PORTABLE_ALIGN aligned_mem[this->dataSize];
226-
const void *processed_blob = processBlob(queryBlob, aligned_mem);
227+
auto aligned_mem =
228+
this->getAllocator()->allocate_aligned_unique(this->dataSize, this->alignment);
229+
const void *processed_blob = processBlob(queryBlob, aligned_mem.get());
227230

228231
return this->topKQuery(processed_blob, k, queryParams);
229232
}
230233

231234
virtual VecSimQueryReply *rangeQueryWrapper(const void *queryBlob, double radius,
232235
VecSimQueryParams *queryParams,
233236
VecSimQueryReply_Order order) const override {
234-
char PORTABLE_ALIGN aligned_mem[this->dataSize];
235-
const void *processed_blob = processBlob(queryBlob, aligned_mem);
237+
auto aligned_mem =
238+
this->getAllocator()->allocate_aligned_unique(this->dataSize, this->alignment);
239+
const void *processed_blob = processBlob(queryBlob, aligned_mem.get());
236240

237241
return this->rangeQuery(processed_blob, radius, queryParams, order);
238242
}
239243

240244
virtual VecSimBatchIterator *
241245
newBatchIteratorWrapper(const void *queryBlob, VecSimQueryParams *queryParams) const override {
242-
char PORTABLE_ALIGN aligned_mem[this->dataSize];
243-
const void *processed_blob = processBlob(queryBlob, aligned_mem);
246+
auto aligned_mem =
247+
this->getAllocator()->allocate_aligned_unique(this->dataSize, this->alignment);
248+
const void *processed_blob = processBlob(queryBlob, aligned_mem.get());
244249

245250
return this->newBatchIterator(processed_blob, queryParams);
246251
}

src/VecSim/vec_sim_tiered_index.h

Lines changed: 14 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -104,31 +104,37 @@ class VecSimTieredIndex : public VecSimIndexInterface {
104104

105105
private:
106106
virtual int addVectorWrapper(const void *blob, labelType label, void *auxiliaryCtx) override {
107-
char PORTABLE_ALIGN aligned_mem[this->backendIndex->getDataSize()];
108-
const void *processed_blob = this->backendIndex->processBlob(blob, aligned_mem);
107+
auto aligned_mem = this->getAllocator()->allocate_aligned_unique(
108+
this->backendIndex->getDataSize(), this->backendIndex->getAlignment());
109+
const void *processed_blob = this->backendIndex->processBlob(blob, aligned_mem.get());
110+
109111
return this->addVector(processed_blob, label, auxiliaryCtx);
110112
}
111113

112114
virtual VecSimQueryReply *topKQueryWrapper(const void *queryBlob, size_t k,
113115
VecSimQueryParams *queryParams) const override {
114-
char PORTABLE_ALIGN aligned_mem[this->backendIndex->getDataSize()];
115-
const void *processed_blob = this->backendIndex->processBlob(queryBlob, aligned_mem);
116+
auto aligned_mem = this->getAllocator()->allocate_aligned_unique(
117+
this->backendIndex->getDataSize(), this->backendIndex->getAlignment());
118+
const void *processed_blob = this->backendIndex->processBlob(queryBlob, aligned_mem.get());
119+
116120
return this->topKQuery(processed_blob, k, queryParams);
117121
}
118122

119123
virtual VecSimQueryReply *rangeQueryWrapper(const void *queryBlob, double radius,
120124
VecSimQueryParams *queryParams,
121125
VecSimQueryReply_Order order) const override {
122-
char PORTABLE_ALIGN aligned_mem[this->backendIndex->getDataSize()];
123-
const void *processed_blob = this->backendIndex->processBlob(queryBlob, aligned_mem);
126+
auto aligned_mem = this->getAllocator()->allocate_aligned_unique(
127+
this->backendIndex->getDataSize(), this->backendIndex->getAlignment());
128+
const void *processed_blob = this->backendIndex->processBlob(queryBlob, aligned_mem.get());
124129

125130
return this->rangeQuery(processed_blob, radius, queryParams, order);
126131
}
127132

128133
virtual VecSimBatchIterator *
129134
newBatchIteratorWrapper(const void *queryBlob, VecSimQueryParams *queryParams) const override {
130-
char PORTABLE_ALIGN aligned_mem[this->backendIndex->getDataSize()];
131-
const void *processed_blob = this->backendIndex->processBlob(queryBlob, aligned_mem);
135+
auto aligned_mem = this->getAllocator()->allocate_aligned_unique(
136+
this->backendIndex->getDataSize(), this->backendIndex->getAlignment());
137+
const void *processed_blob = this->backendIndex->processBlob(queryBlob, aligned_mem.get());
132138

133139
return this->newBatchIterator(processed_blob, queryParams);
134140
}

0 commit comments

Comments
 (0)