在Linux系统中,pthread库提供了一个函数(posix_memalign)用于缓存对齐以防止虚假共享。为了选择特定的NUMA节点,我们可以使用libnuma库。我想要的是需要同时使用这两个库。我想将某些线程绑定到特定的处理器,并希望为每个线程从相应的NUMA节点分配本地数据结构,以便减少线程内存操作的延迟。如何实现这一点?
void *my_malloc(struct node_memory *nm,int node,long size)
{
long off,obytes;
// round up size to the nearest cache line size
// (optional, though some rounding is essential to avoid misalignment problems)
if ((obytes = (size % CACHE_LINE_SIZE)) > 0)
size += CACHE_LINE_SIZE - obytes;
// atomically increase the offset for the requested node by size
if (((off = __sync_fetch_and_add(&(nm->off[node]),size)) + size) > nm->bytes) {
fprintf(stderr,"Out of allocated memory on node %d\n",node);
return(NULL);
}
else
return((void *) (nm->ptr[node] + off));
}
结构体node_memory是什么
struct node_memory {
long bytes; // the number of bytes of memory allocated on each node
char **ptr; // ptr array of ptrs to the base of the memory on each node
long *off; // array of offsets from those bases (in bytes)
int nptrs; // the size of the ptr[] and off[] arrays
};
通过使用libnuma函数numa_alloc_onnode(),nm->ptr[node]会被设置好。
我通常也会在结构体中存储允许的节点信息,这样my_malloc()可以检查节点请求是否合理,而无需进行函数调用。我还会检查nm是否存在以及size是否合理。函数__sync_fetch_and_add()是gcc内置的原子函数;如果您没有使用gcc编译,您需要使用其他函数。我使用原子操作,因为根据我的有限经验,在高线程/核心计数条件下(例如在4P NUMA机器上),它们比互斥锁快得多。
如果你只是想在NUMA分配器周围获得对齐功能,那么你可以轻松地构建自己的分配器。
思路就是调用非对齐的 malloc()
并多分配一点空间。然后返回第一个对齐的地址。为了能够释放它,你需要将基地址存储在已知位置。
这里有一个示例,只需将名称替换为适当的名称:
pint // An unsigned integer that is large enough to store a pointer.
NUMA_malloc // The NUMA malloc function
NUMA_free // The NUMA free function
void* my_NUMA_malloc(size_t bytes,size_t align, /* NUMA parameters */ ){
// The NUMA malloc function
void *ptr = numa_malloc(
(size_t)(bytes + align + sizeof(pint)),
/* NUMA parameters */
);
if (ptr == NULL)
return NULL;
// Get aligned return address
pint *ret = (pint*)((((pint)ptr + sizeof(pint)) & ~(pint)(align - 1)) + align);
// Save the free pointer
ret[-1] = (pint)ptr;
return ret;
}
void my_NUMA_free(void *ptr){
if (ptr == NULL)
return;
// Get the free pointer
ptr = (void*)(((pint*)ptr)[-1]);
// The NUMA free function
numa_free(ptr);
}
my_NUMA_malloc
分配的任何内容调用 my_NUMA_free
。numa.h
中的numa-alloc函数在操作系统/页面级别上运行。在某些情况下,会围绕numa-alloc函数构建类似malloc的包装器/库,以使其更有效。在这种情况下,对齐将不再是页面,并且将受到堆动态的影响。 - Mysticialmy_NUMA_free
中,我认为使用ptr = ((void **)ptr)[-1]
会更好看。 - CplusPuzzle
numa_alloc_*()
使用mmap(2)
系统调用来分配内存,然后使用mbind(2)
系统调用在新映射的区域上设置NUMA策略。malloc(3)
分配器通常映射大块内存(竞技场),然后将它们细分为没有任何内核干预的块。这就是为什么从性能角度来看,使用numa_alloc_*()
执行许多小型分配是巨大的不可取之处。 - Hristo Iliev