NUMA(Non-uniform memory access,非统一内存访问架构)出现前,CPU通过内存控制器访问内存,随着CPU核的增加,内存控制器成为评价。内存控制器一般拆分内存平均分配到各个node节点上,CPU访问本地内存速度快,跨片访问慢。NUMA距离定义为:NUMA node的处理器和内存块的物理距离。通过numactl工具可以查看到CPU访问的距离信息。
避免线程在运行中在不同核上漂移,从而引起访问NUMA远端内存。Openguass通过配置参数thread_pool_attr控制CPU绑核分配,该参数仅在enable_thread_pool打开后生效。参数分为3部分:’thread_num,group_num,cpubind_info’。
其中,
在ServerLoop函数中接收用户端连接,并进行CPU绑定
通过lscpu命令来计算CPU核、NUMA个数。
void ThreadPoolControler::GetCpuAndNumaNum()
{
??? char buf[BUFSIZE];
??? FILE* fp = NULL;
??? if ((fp = popen("lscpu", "r")) != NULL) {
??????? while (fgets(buf, sizeof(buf), fp) != NULL) {
??????????? if (strncmp("CPU(s)", buf, strlen("CPU(s)")) == 0 &&
??????????????? strncmp("On-line CPU(s) list", buf, strlen("On-line CPU(s) list")) != 0 &&
??????????????? strncmp("NUMA node", buf, strlen("NUMA node")) != 0) {
??????????????? char* loc = strchr(buf, ‘:‘);
??????????????? m_cpuInfo.totalCpuNum = pg_strtoint32(loc + 1);
??????????? } else if (strncmp("NUMA node(s)", buf, strlen("NUMA node(s)")) == 0) {
??????????????? char* loc = strchr(buf, ‘:‘);
??????????????? m_cpuInfo.totalNumaNum = pg_strtoint32(loc + 1);
??????????? }
??????? }
??????? pclose(fp);
??? }
}
通过CPU_ISSET判断CPU是否绑定,最后计算出活跃未绑定的CPU个数m_cpuInfo.activeNumaNum
???????m_listener->StartUp();//开启一个新线程
???????InitWorkerSentry();
???????|--???AddWorker
??????????????|--???AttachThreadToNodeLevel:: pthread_setaffinity_np
???????CPU_SET(m_groupCpuArr[i], &m_nodeCpuSet);//循环将CPU加入CPU集合
Opengauss中所有numa相关函数都可以通过宏定义ifdef __USE_NUMA找到其定义及调用的地方。
int numa_available(void):NUMA的API是否可以在平台上正常使用
int numa_max_node(void):当前系统上最大NUMA节点号
void * numa_alloc_onnode(size_t size,int node):在一个指定NUMA节点分配内存
void numa_free(void *start,size_t size):释放起始地址指定的内存
int numa_run_on_node(int node):运行当前任务在指定NUMA节点上
void numa_set_localalloc(void):设置当前的任务内存分配策略为本地化分配
void numa_set_preferred(int node):为当前任务设置偏好NUMA节点
void numa_set_interleave_mask(struct bitmask*nodemask):在一系列numa节点上分配交叉内存
int pthread_getaffinity_np(pthread_t thread,size_t cpusetsize,cpu_set_t *cpuset):设置线程在某个CPU上运行。
1)sched_getaffinity和pthread_getaffinity_np都是绑核的函数。
2)numa_set_preferred设置当前线程优先分配内的结点。内存分配器先尝试从这个结点上分配内存。如果这个结点没有足够的空间,它会尝试其他结点。
numa_set_interleave_mask函数可以让当前线程以交错(interleaving)方式分配内存。未来所有的内存,将会从掩码给定的结点上轮询(round robing)分配。numa_all_nodes将内存分配交错(interleaving)在所有的node上。numa_no_nodes将会关闭交错分配内存。numa_get_interleave_mask函数返回当前的交错掩码。这可以将当前的内存分配策略保存到文件中,在策略修改后,再次恢复。
https://www.bilibili.com/video/BV1gD4y1o7qB?from=search&seid=11985947230954507904
#导入MD文档图片#openguass NUMA适配之线程绑核
原文:https://blog.51cto.com/yanzongshuai/3260168