MCPcopy Create free account
hub / github.com/QuEST-Kit/QuEST / cpu_allocNumaArray

Function cpu_allocNumaArray

quest/src/cpu/cpu_config.cpp:184–254  ·  view source on GitHub ↗

Source from the content-addressed store, hash-verified

182
183
184qcomp* cpu_allocNumaArray(qindex length) {
185#if ! NUMA_AWARE
186 return cpu_allocArray(length);
187
188#elif defined(_WIN32)
189 error_numaAllocOrDeallocAttemptedOnWindows();
190
191#else
192 // we will divide array's memory into pages
193 long pageSize = cpu_getPageSize();
194 qindex arraySize = length * sizeof(qcomp); // gauranteed no overflow
195
196 // if entire array fits within a single page, alloc like normal
197 if (arraySize <= pageSize)
198 return cpu_allocArray(length);
199
200 // otherwise we will bind pages across NUMA nodes
201 static int numNodes = numa_num_configured_nodes();
202 if (numNodes < 1)
203 error_gettingNumNumaNodesFailed();
204
205 qindex numPages = getNumPagesToContainArray(pageSize, arraySize);
206 qindex numBytes = numPages * pageSize; // prior validation gaurantees no overflow
207
208 // allocate memory, potentially more than arraySize (depending on page divisibility)
209 void *rawAddr = mmap(NULL, numBytes, PROT_READ | PROT_WRITE, MAP_PRIVATE | MAP_ANONYMOUS, -1, 0);
210
211 // indicate memory alloc failure to caller (no NUMA-specific validation error message)
212 if (rawAddr == MAP_FAILED)
213 return nullptr;
214
215 // if there is only a single NUMA node, then all memory access will occur within it
216 qcomp* outAddr = reinterpret_cast<qcomp*>(rawAddr);
217 if (numNodes == 1)
218 return outAddr;
219
220 // otherwise, we bind continguous pages to NUMA nodes, distributing the pages
221 // attemptedly uniformly and spreading remaining pages maximally apart
222 qindex baseNumPagesPerNode = numPages / numNodes; // floors
223 qindex remainingNumPagesTotal = numPages % numNodes;
224
225 // use integer type for safe address arithmetic below
226 uintptr_t offsetAddr = reinterpret_cast<uintptr_t>(rawAddr);
227
228 for (int node=0, shift=numNodes; node < numNodes; ++node) {
229
230 // decide number of pages to bind to NUMA node
231 shift -= remainingNumPagesTotal;
232 qindex numPagesInNode = baseNumPagesPerNode + (shift <= 0);
233 qindex numBytesInNode = numPagesInNode * pageSize; // validation prevents overflow
234
235 // bind those pages from the offset address to the node (identified by mask)
236 unsigned long nodeMask = 1UL << node;
237 unsigned long numBitsInMask = 8 * sizeof(nodeMask);
238 void* nodeAddr = reinterpret_cast<void*>(offsetAddr);
239 long success = mbind(nodeAddr, numBytesInNode, MPOL_BIND, &nodeMask, numBitsInMask, 0);
240
241 // treat bind failure as internal error (even though it can result from insufficient kernel mem),

Callers 1

Calls 6

cpu_allocArrayFunction · 0.85
cpu_getPageSizeFunction · 0.85
error_numaBindingFailedFunction · 0.85

Tested by

no test coverage detected