Update threadpool_controler.cpp

This commit is contained in:
Nemoo 2023-09-30 14:18:56 +08:00
parent 93af93324b
commit a4a404f207
1 changed files with 202 additions and 167 deletions

View File

@ -7,16 +7,12 @@
*
* http://license.coscl.org.cn/MulanPSL2
*
* THIS SOFTWARE IS PROVIDED ON AN "AS IS" BASIS, WITHOUT WARRANTIES OF ANY KIND,
* EITHER EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO NON-INFRINGEMENT,
* MERCHANTABILITY OR FIT FOR A PARTICULAR PURPOSE.
* See the Mulan PSL v2 for more details.
* -------------------------------------------------------------------------
*
* threadpool_controler.cpp
* Controler for thread pool. Class ThreadPoolControler is defined to
* initilize thread pool's worker and listener threads, and dispatch
* new session from postmaster thread to suitable thread group.
* 线 ThreadPoolControler
* 线线线
* 线线
*
*
* IDENTIFICATION
@ -71,32 +67,33 @@ ThreadPoolControler* g_threadPoolControler = NULL;
static const long one_hundred_micro_sec = 100;
// 线程池控制器的构造函数
ThreadPoolControler::ThreadPoolControler()
{
m_threadPoolContext = NULL;
m_sessCtrl = NULL;
m_groups = NULL;
m_scheduler = NULL;
m_groupNum = 1;
m_threadNum = 0;
m_maxPoolSize = 0;
m_maxStreamPoolSize = 0;
m_streamProcRatio = 0;
m_threadPoolContext = NULL; // 线程池上下文
m_sessCtrl = NULL; // 会话控制
m_groups = NULL; // 线程组
m_scheduler = NULL; // 调度器
m_groupNum = 1; // 线程组数量
m_threadNum = 0; // 线程数量
m_maxPoolSize = 0; // 最大线程池大小
m_maxStreamPoolSize = 0; // 最大流线程池大小
m_streamProcRatio = 0; // 流处理比例
}
ThreadPoolControler::~ThreadPoolControler()
{
delete m_scheduler;
delete m_sessCtrl;
MemoryContextDelete(m_threadPoolContext);
m_threadPoolContext = NULL;
m_groups = NULL;
m_sessCtrl = NULL;
delete m_scheduler; // 删除调度器对象
delete m_sessCtrl; // 删除会话控制对象
MemoryContextDelete(m_threadPoolContext); // 删除线程池上下文
m_threadPoolContext = NULL; // 将线程池上下文置为空
m_groups = NULL; // 将线程组指针置为空
m_sessCtrl = NULL; // 将会话控制指针置为空
}
void ThreadPoolControler::Init(bool enableNumaDistribute)
{
// 创建线程池上下文
m_threadPoolContext = AllocSetContextCreate(g_instance.instance_context,
"ThreadPoolContext",
ALLOCSET_DEFAULT_MINSIZE,
@ -104,12 +101,18 @@ void ThreadPoolControler::Init(bool enableNumaDistribute)
ALLOCSET_DEFAULT_MAXSIZE,
SHARED_CONTEXT);
// 切换到线程池上下文
AutoContextSwitch memSwitch(m_threadPoolContext);
// 分配线程组数组
m_groups = (ThreadPoolGroup**)palloc(sizeof(ThreadPoolGroup*) * m_groupNum);
// 创建线程池会话控制对象
m_sessCtrl = New(CurrentMemoryContext) ThreadPoolSessControl(CurrentMemoryContext);
// 检查是否绑定 CPU
bool bindCpu = CheckCpuBind();
// 检查是否绑定 CPU NUMA
bool bindCpuNuma = CheckCpuNumaBind();
int maxThreadNum = 0;
int expectThreadNum = 0;
@ -123,16 +126,14 @@ void ThreadPoolControler::Init(bool enableNumaDistribute)
while (m_cpuInfo.cpuArrSize[numaId] == 0)
numaId++;
/*
* Invoke numa_set_preferred before starting worker thread to make
* more memory allocation local to worker thread.
*/
#ifdef __USE_NUMA
// 如果启用 NUMA 分配,则设置首选 NUMA 节点
if (enableNumaDistribute) {
numa_set_preferred(numaId);
}
#endif
// 获取当前 NUMA 节点的 CPU 数量
Assert(numaId < m_cpuInfo.totalNumaNum);
expectThreadNum = (int)round(
(double)m_threadNum * ((double)m_cpuInfo.cpuArrSize[numaId] / (double)m_cpuInfo.activeCpuNum));
@ -143,30 +144,35 @@ void ThreadPoolControler::Init(bool enableNumaDistribute)
cpuNum = m_cpuInfo.cpuArrSize[numaId];
cpuArr = m_cpuInfo.cpuArr[numaId];
// 增加 NUMA ID以处理下一个 NUMA 节点
numaId++;
} else {
// 如果未绑定 CPU则均匀分配线程和流的数量
expectThreadNum = m_threadNum / m_groupNum;
maxThreadNum = m_maxPoolSize / m_groupNum;
maxStreamNum = m_maxPoolSize / m_groupNum;
numaId = -1;
}
// 创建线程池组对象并初始化
m_groups[i] = New(CurrentMemoryContext)ThreadPoolGroup(maxThreadNum, expectThreadNum,
maxStreamNum, i, numaId, cpuNum, cpuArr, bindCpuNuma);
m_groups[i]->Init(enableNumaDistribute);
}
// 等待所有线程池组准备就绪
for (int i = 0; i < m_groupNum; i++) {
m_groups[i]->WaitReady();
}
#ifdef __USE_NUMA
if (enableNumaDistribute) {
/* Set to interleave mode for other than worker thread */
/* 设置为交织模式以供除工作线程之外的其他线程使用 */
numa_set_interleave_mask(numa_all_nodes_ptr);
}
#endif
// 创建线程池调度器对象并启动
m_scheduler = New(CurrentMemoryContext) ThreadPoolScheduler(m_groupNum, m_groups);
m_scheduler->StartUp();
}
@ -194,9 +200,10 @@ void AdjustThreadAffinity(void) {
cpu_set_t m_cpuset;
CPU_ZERO(&m_cpuset);
/* Check if the instance has been attch to some specific CPUs. */
// 检查实例是否已绑定到某些特定的 CPU
int ret = pthread_getaffinity_np(PostmasterPid, sizeof(cpu_set_t), &m_cpuset);
if (ret == 0) {
// 如果绑定到 CPU 0 而未绑定到 CPU 1则重新设置 CPU 亲和性,以避免绑定到单一 CPU
if ((CPU_ISSET(0, &m_cpuset)) && (!CPU_ISSET(1, &m_cpuset))) {
int num_processors = sysconf(_SC_NPROCESSORS_CONF);
CPU_ZERO(&m_cpuset);
@ -204,7 +211,7 @@ void AdjustThreadAffinity(void) {
CPU_SET(j, &m_cpuset);
}
// set CPU affinity of a thread
// 设置线程的 CPU 亲和性
int s = pthread_setaffinity_np(PostmasterPid, sizeof(cpu_set_t), &m_cpuset);
if (s != 0) {
ereport(WARNING, (errmsg("AdjustThreadAffinity fail to bind thread %lu, errno: %d", PostmasterPid, ret)));
@ -213,17 +220,16 @@ void AdjustThreadAffinity(void) {
}
}
void ThreadPoolControler::GetInstanceBind(cpu_set_t *cpuset)
{
/* this function is used to avoid the libgomp bug on some specified OS */
void ThreadPoolControler::GetInstanceBind(cpu_set_t *cpuset) {
// 修复 libgomp 在某些指定操作系统上的 bug
AdjustThreadAffinity();
/* Check if the instance has been attch to some specific CPUs. */
// 检查实例是否已绑定到某些特定的 CPU
int ret = pthread_getaffinity_np(PostmasterPid, sizeof(cpu_set_t), cpuset);
if (ret == 0) {
return;
} else {
// 初始化 cpuset以避免未绑定的情况
errno_t rc = memset_s(cpuset, sizeof(cpu_set_t), 0, sizeof(cpu_set_t));
securec_check(rc, "\0", "\0");
}
@ -235,7 +241,7 @@ void ThreadPoolControler::ParseAttr()
m_attr.groupNum = DEFAULT_THREAD_POOL_GROUPS;
m_attr.bindCpu = NULL;
/* Do str copy and remove space. */
/* 复制字符串并移除空格 */
char* attr = TrimStr(g_instance.attr.attr_common.thread_pool_attr);
if (IS_NULL_STR(attr))
return;
@ -244,13 +250,13 @@ void ThreadPoolControler::ParseAttr()
char* psave = NULL;
const char* pdelimiter = ",";
/* Get thread num */
/* 获取线程数 */
ptoken = TrimStr(strtok_r(attr, pdelimiter, &psave));
if (!IS_NULL_STR(ptoken))
m_attr.threadNum = pg_strtoint32(ptoken);
pfree_ext(ptoken);
/* Ger group num */
/* 获取线程组数 */
ptoken = TrimStr(strtok_r(NULL, pdelimiter, &psave));
if (!IS_NULL_STR(ptoken))
m_attr.groupNum = pg_strtoint32(ptoken);
@ -258,15 +264,15 @@ void ThreadPoolControler::ParseAttr()
if (m_attr.threadNum < 0 || m_attr.threadNum > MAX_THREAD_POOL_SIZE)
INVALID_ATTR_ERROR(
errdetail("Current thread num %d is out of range [%d, %d].", m_attr.threadNum, 0, MAX_THREAD_POOL_SIZE));
errdetail("当前线程数 %d 超出范围 [%d, %d]。", m_attr.threadNum, 0, MAX_THREAD_POOL_SIZE));
if (m_attr.groupNum < 0 || m_attr.groupNum > MAX_THREAD_POOL_GROUPS)
INVALID_ATTR_ERROR(
errdetail("Current group num %d is out of range [%d, %d].", m_attr.groupNum, 0, MAX_THREAD_POOL_GROUPS));
errdetail("当前线程组数 %d 超出范围 [%d, %d]。", m_attr.groupNum, 0, MAX_THREAD_POOL_GROUPS));
/* Get attach cpu */
/* 获取绑定 CPU */
m_attr.bindCpu = TrimStr(psave);
ParseBindCpu();
pfree_ext(attr);
}
@ -276,7 +282,8 @@ void ThreadPoolControler::ParseStreamAttr()
m_stream_attr.procRatio = DEFAULT_THREAD_POOL_STREAM_PROC_RATIO;
m_stream_attr.groupNum = DEFAULT_THREAD_POOL_GROUPS;
m_stream_attr.bindCpu = NULL;
// 获取流式线程池属性配置字符串
char* attr = TrimStr(g_instance.attr.attr_common.thread_pool_stream_attr);
if (IS_NULL_STR(attr)) {
return;
@ -285,36 +292,36 @@ void ThreadPoolControler::ParseStreamAttr()
char* ptoken = NULL;
char* psave = NULL;
const char* pdelimiter = ",";
/* Get stream_thread_pool_stream max thread num */
// 解析流式线程池的最大线程数
ptoken = TrimStr(strtok_r(attr, pdelimiter, &psave));
if (IS_NULL_STR(ptoken) || !isdigit((unsigned char)*ptoken)) {
INVALID_ATTR_ERROR(
errdetail("Current thread_pool_stream_attr format is error, stream_thread_num must be digital."));
errdetail("当前 thread_pool_stream_attr 格式错误stream_thread_num 必须为数字。"));
}
m_stream_attr.threadNum = pg_strtoint32(ptoken);
pfree_ext(ptoken);
if (m_stream_attr.threadNum < 0 || m_stream_attr.threadNum > MAX_THREAD_POOL_SIZE) {
INVALID_ATTR_ERROR(
errdetail("Current stream_thread_num %d is out of range [%d, %d].",
errdetail("当前 stream_thread_num %d 超出范围 [%d, %d]。",
m_stream_attr.threadNum, 0, MAX_THREAD_POOL_SIZE));
}
/* Get proc ratio of stream threads */
// 解析流式线程池的处理比例
ptoken = TrimStr(strtok_r(NULL, pdelimiter, &psave));
if (IS_NULL_STR(ptoken) || !isdigit((unsigned char)*ptoken)) {
INVALID_ATTR_ERROR(
errdetail("Current thread_pool_stream_attr format is error, stream_proc_ratio must be digital."));
errdetail("当前 thread_pool_stream_attr 格式错误stream_proc_ratio 必须为数字。"));
}
m_stream_attr.procRatio = atof(ptoken);
pfree_ext(ptoken);
if (m_stream_attr.procRatio <= 0 || m_stream_attr.procRatio > MAX_THREAD_POOL_STREAM_PROC_RATIO) {
INVALID_ATTR_ERROR(
errdetail("Current stream_proc_ratio %f is out of range (%d, %d].",
errdetail("当前 stream_proc_ratio %f 超出范围 (%d, %d]。",
m_stream_attr.procRatio, 0, MAX_THREAD_POOL_STREAM_PROC_RATIO));
}
pfree_ext(attr);
return;
}
@ -325,6 +332,7 @@ void ThreadPoolControler::ParseBindCpu()
return;
}
// 复制属性字符串以进行解析
char* pattr = pstrdup(m_attr.bindCpu);
char* scpu = pattr;
char* ptoken = NULL;
@ -332,15 +340,18 @@ void ThreadPoolControler::ParseBindCpu()
const char* pdelimiter = ":";
int bindNum = 0;
// 检查属性字符串的格式是否正确
if (scpu[0] != '(' || scpu[strlen(scpu) - 1] != ')')
INVALID_ATTR_ERROR("Use '(' ')' to indicate cpu bind info.");
INVALID_ATTR_ERROR("使用 '(' ')' 来表示 CPU 绑定信息。");
scpu++;
scpu[strlen(scpu) - 1] = '\0';
// 解析属性字符串并转换为小写
ptoken = TrimStr(strtok_r(scpu, pdelimiter, &psave));
ptoken = pg_strtolower(ptoken);
// 根据不同的属性类型进行解析
if (strncmp("nobind", ptoken, strlen("nobind")) == 0) {
m_cpuInfo.bindType = NO_CPU_BIND;
return;
@ -360,16 +371,16 @@ void ThreadPoolControler::ParseBindCpu()
m_cpuInfo.isBindCpuNumaArr = (bool*)palloc0(sizeof(bool) * m_cpuInfo.totalCpuNum);
bindNum = ParseRangeStr(psave, m_cpuInfo.isBindCpuNumaArr, m_cpuInfo.totalCpuNum, "numabind");
} else {
INVALID_ATTR_ERROR(errdetail("Only 'nobind', 'allbind', 'cpubind', 'nodebind' and 'numabind' "
"are valid attribute."));
INVALID_ATTR_ERROR(errdetail("只有 'nobind', 'allbind', 'cpubind', 'nodebind' 和 'numabind' "
"是有效的属性。"));
}
// 检查是否找到有效的 CPU 进行线程绑定
if (bindNum == 0)
INVALID_ATTR_ERROR(
errdetail("Can not find valid CPU for thread binding, there are two possible reasons:\n"
"1. These CPUs are not active, use lscpu to check On-line CPU(s) list.\n"
"2. The process has been bind to other CPUs and there is no intersection,"
"use taskset -pc to check process CPU bind info.\n"));
errdetail("无法找到有效的 CPU 进行线程绑定,可能的原因有两个:\n"
"1. 这些 CPU 不处于活动状态,请使用 lscpu 命令检查在线 CPU 列表。\n"
"2. 进程已绑定到其他 CPU且没有交集请使用 taskset -pc 命令检查进程 CPU 绑定信息。\n"));
pfree_ext(ptoken);
pfree_ext(pattr);
}
@ -381,6 +392,7 @@ int ThreadPoolControler::ParseRangeStr(char* attr, bool* arr, int totalNum, char
const char* pdelimiter = ",";
int retNum = 0;
// 解析属性字符串
ptoken = TrimStr(strtok_r(attr, pdelimiter, &psave));
while (!IS_NULL_STR(ptoken)) {
@ -390,25 +402,29 @@ int ThreadPoolControler::ParseRangeStr(char* attr, bool* arr, int totalNum, char
int startid = -1;
int endid = -1;
// 解析范围字符串,可能包含起始和结束值
pt = TrimStr(strtok_r(ptoken, pd, &ps));
if (!IS_NULL_STR(pt))
startid = pg_strtoint32(pt);
if (!IS_NULL_STR(ps))
endid = pg_strtoint32(ps);
// 检查解析的值是否有效
if (startid < 0 && endid < 0)
INVALID_ATTR_ERROR(errdetail("Can not parse attribute %s", pt));
INVALID_ATTR_ERROR(errdetail("无法解析属性 %s", pt));
if (startid >= totalNum)
INVALID_ATTR_ERROR(
errdetail("The %s attribute %d is out of valid range [%d, %d]", bindtype, startid, 0, totalNum - 1));
errdetail("属性 %s 中的 %d 超出了有效范围 [%d, %d]", bindtype, startid, 0, totalNum - 1));
if (endid >= totalNum)
INVALID_ATTR_ERROR(
errdetail("The %s attribute %d is out of valid range [%d, %d]", bindtype, endid, 0, totalNum - 1));
errdetail("属性 %s 中的 %d 超出了有效范围 [%d, %d]", bindtype, endid, 0, totalNum - 1));
if (endid == -1) {
// 单个 CPU 绑定
retNum += arr[startid] ? 0 : 1;
arr[startid] = true;
} else {
// 范围内的多个 CPU 绑定
if (startid > endid) {
int tmpid = startid;
startid = endid;
@ -421,7 +437,7 @@ int ThreadPoolControler::ParseRangeStr(char* attr, bool* arr, int totalNum, char
}
}
/* Don't need to free when error ocurrs, errors here are FATAL level! */
// 释放临时字符串内存,不需要在错误发生时释放,这里的错误是致命的!
pfree_ext(pt);
pfree_ext(ptoken);
ptoken = TrimStr(strtok_r(NULL, pdelimiter, &psave));
@ -438,9 +454,8 @@ bool* ThreadPoolControler::GetMcsCpuInfo(int totalCpuNum)
bool* isMcsCpuArr = (bool*)palloc0(sizeof(bool) * totalCpuNum);
/*
* When the database is deplyed on MCS, we need to read cpuset.cpus to find
* available CPUs in this MCS. If we can read this file, then we think all
* CPUs are available.
* MCS上时cpuset.cpus文件以查找此MCS中可用的CPU
* CPU都是可用的
*/
fp = fopen("/sys/fs/cgroup/cpuset/cpuset.cpus", "r");
if (fp == NULL) {
@ -473,11 +488,12 @@ bool* ThreadPoolControler::GetMcsCpuInfo(int totalCpuNum)
void ThreadPoolControler::GetActiveCpu(NumaCpuId *numaCpuIdList, int *num)
{
*num = 0;
*num = 0; // 初始化传出参数 num 为 0
char buf[BUFSIZE];
FILE* fp = popen("lscpu -b -e=cpu,node", "r");
FILE* fp = popen("lscpu -b -e=cpu,node", "r"); // 打开一个用于读取 CPU 信息的流
if (fp == NULL) {
ereport(WARNING, (errmsg("Unable to use 'lscpu' to read CPU info.")));
ereport(WARNING, (errmsg("Unable to use 'lscpu' to read CPU info."))); // 如果打开流失败,发出警告消息
return;
}
@ -486,45 +502,49 @@ void ThreadPoolControler::GetActiveCpu(NumaCpuId *numaCpuIdList, int *num)
const char* pdelimiter = " ";
int cpuid = 0;
int numaid = 0;
/* try to read the header. */
// 尝试读取头部信息
if (fgets(buf, sizeof(buf), fp) != NULL) {
while (fgets(buf, sizeof(buf), fp) != NULL) {
ptoken = strtok_r(buf, pdelimiter, &psave);
if (!IS_NULL_STR(ptoken)) {
cpuid = pg_strtoint32(ptoken);
cpuid = pg_strtoint32(ptoken); // 解析 CPU ID
}
ptoken = strtok_r(NULL, pdelimiter, &psave);
if (!IS_NULL_STR(ptoken)) {
numaid = pg_strtoint32(ptoken);
numaid = pg_strtoint32(ptoken); // 解析 NUMA ID
}
numaCpuIdList[*num].cpuId = cpuid;
numaCpuIdList[*num].numaId = numaid;
(*num)++;
numaCpuIdList[*num].cpuId = cpuid; // 将 CPU ID 存入传出参数
numaCpuIdList[*num].numaId = numaid; // 将 NUMA ID 存入传出参数
(*num)++; // 递增传出参数 num
}
}
pclose(fp);
pclose(fp); // 关闭流
}
void ThreadPoolControler::GetSysCpuInfo()
{
if (m_cpuInfo.totalNumaNum == 0 || m_cpuInfo.totalCpuNum == 0) {
ereport(WARNING, (errmsg("Fail to read cpu num or numa num.")));
ereport(WARNING, (errmsg("Fail to read cpu num or numa num."))); // 如果没有读取到 CPU 数量或 NUMA 数量,发出警告消息
return;
}
m_cpuInfo.isMcsCpuArr = GetMcsCpuInfo(m_cpuInfo.totalCpuNum);
m_cpuInfo.isMcsCpuArr = GetMcsCpuInfo(m_cpuInfo.totalCpuNum); // 获取 MCS CPU 信息
m_cpuInfo.cpuArr = (int**)palloc0(sizeof(int*) * m_cpuInfo.totalNumaNum);
m_cpuInfo.cpuArrSize = (int*)palloc0(sizeof(int) * m_cpuInfo.totalNumaNum);
int cpu_per_numa = m_cpuInfo.totalCpuNum / m_cpuInfo.totalNumaNum;
for (int i = 0; i < m_cpuInfo.totalNumaNum; i++) {
m_cpuInfo.cpuArr[i] = (int*)palloc0(sizeof(int) * cpu_per_numa);
m_cpuInfo.cpuArr[i] = (int*)palloc0(sizeof(int) * cpu_per_numa); // 为每个 NUMA 节点分配内存
}
m_cpuInfo.activeCpuNum = 0;
NumaCpuId *sysNumaCpuIdList = (NumaCpuId*)palloc0(sizeof(NumaCpuId) * m_cpuInfo.totalCpuNum);
int sysNumaCpuIdNum = 0;
GetActiveCpu(sysNumaCpuIdList, &sysNumaCpuIdNum);
GetActiveCpu(sysNumaCpuIdList, &sysNumaCpuIdNum); // 获取激活的 CPU 信息
if (sysNumaCpuIdNum == 0) {
return;
@ -533,56 +553,58 @@ void ThreadPoolControler::GetSysCpuInfo()
for (int i = 0; i < sysNumaCpuIdNum; ++i) {
int cpuid = sysNumaCpuIdList[i].cpuId;
int numaid = sysNumaCpuIdList[i].numaId;
if (IsActiveCpu(cpuid, numaid)) {
m_cpuInfo.cpuArr[numaid][m_cpuInfo.cpuArrSize[numaid]] = cpuid;
m_cpuInfo.cpuArrSize[numaid]++;
m_cpuInfo.activeCpuNum++;
m_cpuInfo.cpuArr[numaid][m_cpuInfo.cpuArrSize[numaid]] = cpuid; // 将 CPU ID 存入相应 NUMA 节点的数组中
m_cpuInfo.cpuArrSize[numaid]++; // 递增相应 NUMA 节点的数组大小
m_cpuInfo.activeCpuNum++; // 递增激活的 CPU 数量
}
}
pfree_ext(sysNumaCpuIdList);
pfree_ext(sysNumaCpuIdList); // 释放内存
for (int i = 0; i < m_cpuInfo.totalNumaNum; i++) {
if (m_cpuInfo.cpuArrSize[i] > 0)
m_cpuInfo.activeNumaNum++;
m_cpuInfo.activeNumaNum++; // 统计激活的 NUMA 节点数量
}
}
void ThreadPoolControler::InitCpuInfo()
{
m_cpuInfo.totalCpuNum = 0;
m_cpuInfo.activeCpuNum = 0;
m_cpuInfo.totalNumaNum = 0;
m_cpuInfo.activeNumaNum = 0;
m_cpuInfo.cpuArrSize = NULL;
m_cpuInfo.cpuArr = NULL;
m_cpuInfo.totalCpuNum = 0; // 初始化总 CPU 数量为 0
m_cpuInfo.activeCpuNum = 0; // 初始化激活的 CPU 数量为 0
m_cpuInfo.totalNumaNum = 0; // 初始化总 NUMA 节点数量为 0
m_cpuInfo.activeNumaNum = 0; // 初始化激活的 NUMA 节点数量为 0
m_cpuInfo.cpuArrSize = NULL; // 初始化 CPU 数组大小为 NULL
m_cpuInfo.cpuArr = NULL; // 初始化 CPU 数组为 NULL
m_cpuInfo.bindType = NO_CPU_BIND;
m_cpuInfo.isBindCpuArr = NULL;
m_cpuInfo.isBindNumaArr = NULL;
m_cpuInfo.isMcsCpuArr = NULL;
m_cpuInfo.bindType = NO_CPU_BIND; // 初始化 CPU 绑定类型为 NO_CPU_BIND
m_cpuInfo.isBindCpuArr = NULL; // 初始化 CPU 绑定数组为 NULL
m_cpuInfo.isBindNumaArr = NULL; // 初始化 NUMA 节点绑定数组为 NULL
m_cpuInfo.isMcsCpuArr = NULL; // 初始化 MCS CPU 数组为 NULL
}
void ThreadPoolControler::GetCpuAndNumaNum(int32 *totalCpuNum, int32 *totalNumaNum)
{
char buf[BUFSIZE];
FILE* fp = NULL;
// 打开 "lscpu" 命令的输出以获取 CPU 和 NUMA 节点数量信息
if ((fp = popen("LANG=en_US.UTF-8;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) {
// 当遇到包含 "CPU(s)" 的行时,解析并获取总 CPU 数量
char* loc = strchr(buf, ':');
*totalCpuNum = pg_strtoint32(loc + 1);
} else if (strncmp("NUMA node(s)", buf, strlen("NUMA node(s)")) == 0) {
// 当遇到包含 "NUMA node(s)" 的行时,解析并获取总 NUMA 节点数量
char* loc = strchr(buf, ':');
*totalNumaNum = pg_strtoint32(loc + 1);
}
}
pclose(fp);
pclose(fp); // 关闭文件流
}
}
@ -591,67 +613,72 @@ bool ThreadPoolControler::IsActiveCpu(int cpuid, int numaid)
switch (m_cpuInfo.bindType) {
case NO_CPU_BIND:
case ALL_CPU_BIND:
// 如果未进行 CPU 绑定,或者进行了全局 CPU 绑定,检查 CPU 是否激活
return (m_cpuInfo.isMcsCpuArr[cpuid] && CPU_ISSET(cpuid, &m_cpuset));
case NODE_BIND:
// 如果进行了 NUMA 节点绑定,检查 CPU 和 NUMA 节点是否激活
return (m_cpuInfo.isBindNumaArr[numaid] && m_cpuInfo.isMcsCpuArr[cpuid] && CPU_ISSET(cpuid, &m_cpuset));
case CPU_BIND:
// 如果进行了 CPU 绑定,检查 CPU 是否激活
return (m_cpuInfo.isBindCpuArr[cpuid] && m_cpuInfo.isMcsCpuArr[cpuid] && CPU_ISSET(cpuid, &m_cpuset));
case NUMA_BIND:
// 如果进行了 NUMA 节点和 CPU 绑定,检查 CPU 和 NUMA 节点是否激活
return (m_cpuInfo.isBindCpuNumaArr[cpuid] && m_cpuInfo.isMcsCpuArr[cpuid] && CPU_ISSET(cpuid, &m_cpuset));
}
return false;
return false; // 默认情况下,返回 false
}
bool ThreadPoolControler::CheckCpuBind() const
{
if (m_cpuInfo.bindType == NO_CPU_BIND)
return false;
return false; // 如果没有进行 CPU 绑定,则返回 false
if (m_groupNum != m_cpuInfo.activeNumaNum) {
ereport(WARNING,
(errmsg("Can not bind worker thread to CPU because the "
"thread group num must equal to active NUMA num.")));
return false;
}
if (m_cpuInfo.activeCpuNum == 0 || m_cpuInfo.cpuArr == NULL) {
ereport(WARNING, (errmsg("Can not bind worker thread to CPU because no valid CPUs.")));
return false;
(errmsg("无法将工作线程绑定到 CPU因为线程组数必须等于激活的 NUMA 节点数。")));
return false; // 如果线程组数不等于激活的 NUMA 节点数,返回 false
}
return true;
if (m_cpuInfo.activeCpuNum == 0 || m_cpuInfo.cpuArr == NULL) {
ereport(WARNING, (errmsg("无法将工作线程绑定到 CPU因为没有有效的 CPU。")));
return false; // 如果没有有效的 CPU返回 false
}
return true; // 其他情况下,返回 true
}
bool ThreadPoolControler::CheckCpuNumaBind() const
{
return m_cpuInfo.bindType == NUMA_BIND;
return m_cpuInfo.bindType == NUMA_BIND; // 如果进行了 NUMA 绑定,返回 true否则返回 false
}
bool ThreadPoolControler::CheckNumaDistribute(int numaNodeNum) const
{
if (m_cpuInfo.bindType == NO_CPU_BIND) {
ereport(WARNING,
(errmsg("allbind should be used to replace nobind in thread_pool_attr when NUMA is activated.")));
return false;
(errmsg("在激活 NUMA 时,应使用 allbind 来替代 nobind 在 thread_pool_attr 中。")));
return false; // 如果未进行 CPU 绑定,给出警告并返回 false
}
if (!CheckCpuBind()) {
return false;
return false; // 如果 CPU 绑定检查失败,返回 false
}
if (m_cpuInfo.totalNumaNum != numaNodeNum || !m_cpuInfo.cpuArrSize) {
ereport(WARNING,
(errmsg("Can not activate NUMA distribute because no multiple NUMA nodes or CPUs are available.")));
return false;
(errmsg("无法激活 NUMA 分布,因为没有多个 NUMA 节点或可用的 CPU。")));
return false; // 如果 NUMA 节点数不等于给定的 numaNodeNum或者 cpuArrSize 为空,返回 false
}
for (int i = 0; i < m_cpuInfo.totalNumaNum; ++i) {
if (m_cpuInfo.cpuArrSize[i] <= 0) {
ereport(WARNING,
(errmsg("Can not activate NUMA distribute because no available cpu in node %d.", i)));
return false;
(errmsg("无法激活 NUMA 分布,因为节点 %d 中没有可用的 CPU。", i)));
return false; // 如果某个 NUMA 节点中没有可用的 CPU返回 false
}
}
return true;
return true; // 其他情况下,返回 true
}
CPUBindType ThreadPoolControler::GetCpuBindType() const
@ -669,55 +696,54 @@ void ThreadPoolControler::SetGroupAndThreadNum()
{
if (m_attr.groupNum == 0) {
if (m_cpuInfo.totalNumaNum > 0)
m_groupNum = m_cpuInfo.activeNumaNum;
m_groupNum = m_cpuInfo.activeNumaNum; // 如果未指定线程组数且存在 NUMA 节点,则使用激活的 NUMA 节点数
else
m_groupNum = DEFAULT_THREAD_POOL_GROUPS;
m_groupNum = DEFAULT_THREAD_POOL_GROUPS; // 否则使用默认的线程组数
} else {
m_groupNum = m_attr.groupNum;
m_groupNum = m_attr.groupNum; // 如果指定了线程组数,则使用指定的线程组数
}
if (m_attr.threadNum == 0) {
if (m_cpuInfo.activeCpuNum > 0)
m_threadNum = m_cpuInfo.activeCpuNum * THREAD_CORE_RATIO;
m_threadNum = m_cpuInfo.activeCpuNum * THREAD_CORE_RATIO; // 如果未指定线程数且存在激活的 CPU则计算线程数
else
m_threadNum = DEFAULT_THREAD_POOL_SIZE;
m_threadNum = DEFAULT_THREAD_POOL_SIZE; // 否则使用默认的线程数
} else {
m_threadNum = m_attr.threadNum;
m_threadNum = m_attr.threadNum; // 如果指定了线程数,则使用指定的线程数
}
ConstrainThreadNum();
ConstrainThreadNum(); // 调用 ConstrainThreadNum 方法进行线程数约束
}
void ThreadPoolControler::ConstrainThreadNum()
{
/* Thread pool size should not be larger than max_connections. */
/* 线程池大小不应超过 max_connections。 */
if (MAX_THREAD_POOL_SIZE > g_instance.attr.attr_network.MaxConnections) {
ereport(LOG, (errcode(ERRCODE_OPERATE_INVALID_PARAM),
errmsg("Max thread pool size %d should not be larger than max_connections %d, "
"so reduce max thread pool size to max_connections",
errmsg("最大线程池大小 %d 不应超过 max_connections %d因此将最大线程池大小减小到 max_connections",
MAX_THREAD_POOL_SIZE, g_instance.attr.attr_network.MaxConnections)));
}
m_maxPoolSize = Min(MAX_THREAD_POOL_SIZE, g_instance.attr.attr_network.MaxConnections);
m_threadNum = Min(m_threadNum, m_maxPoolSize);
m_maxPoolSize = Min(MAX_THREAD_POOL_SIZE, g_instance.attr.attr_network.MaxConnections); // 最大线程池大小受限于 max_connections
m_threadNum = Min(m_threadNum, m_maxPoolSize); // 线程数不应超过最大线程池大小
}
int ThreadPoolControler::GetThreadNum()
{
return m_maxPoolSize;
return m_maxPoolSize; // 返回最大线程池大小
}
ThreadPoolStat* ThreadPoolControler::GetThreadPoolStat(uint32* num)
{
ThreadPoolStat* result = (ThreadPoolStat*)palloc(m_groupNum * sizeof(ThreadPoolStat));
ThreadPoolStat* result = (ThreadPoolStat*)palloc(m_groupNum * sizeof(ThreadPoolStat)); // 分配存储线程池统计信息的内存
int i;
for (i = 0; i < m_groupNum; i++) {
m_groups[i]->GetThreadPoolGroupStat(&result[i]);
m_groups[i]->GetThreadPoolGroupStat(&result[i]); // 获取每个线程组的统计信息
}
*num = m_groupNum;
return result;
*num = m_groupNum; // 返回线程组数量
return result; // 返回线程池统计信息数组
}
void ThreadPoolControler::CloseAllSessions()
@ -725,14 +751,14 @@ void ThreadPoolControler::CloseAllSessions()
ereport(LOG, (errmodule(MOD_THREAD_POOL),
errmsg("pmState:%d, start to close all sessions in threadpool.", pmState)));
m_sessCtrl->MarkAllSessionClose();
(void)SignalCancelAllBackEnd();
m_sessCtrl->MarkAllSessionClose(); // 标记所有会话为关闭状态
(void)SignalCancelAllBackEnd(); // 发送取消信号以取消所有后端任务
for (int i = 0; i < m_groupNum; i++) {
m_groups[i]->GetListener()->SendShutDown();
m_groups[i]->GetListener()->SendShutDown(); // 发送关闭信号给所有监听器
}
/* Check until all groups have closed their sessions. */
/* 检查直到所有组都关闭了它们的会话。 */
bool allclose = false;
while (!allclose) {
if (m_sessCtrl->IsActiveListEmpty()) {
@ -741,9 +767,9 @@ void ThreadPoolControler::CloseAllSessions()
allclose = true;
for (int i = 0; i < m_groupNum; i++) {
allclose = (m_groups[i]->AllSessionClosed() && allclose);
allclose = (m_groups[i]->AllSessionClosed() && allclose); // 检查每个组的会话是否都已关闭
}
pg_usleep(one_hundred_micro_sec);
pg_usleep(one_hundred_micro_sec); // 短暂休眠以减少 CPU 使用
}
ereport(LOG, (errmodule(MOD_THREAD_POOL),
@ -753,21 +779,21 @@ void ThreadPoolControler::CloseAllSessions()
void ThreadPoolControler::ShutDownThreads(bool forceWait)
{
for (int i = 0; i < m_groupNum; i++) {
m_groups[i]->ShutDownThreads();
m_groups[i]->ShutDownThreads(); // 关闭所有线程组中的线程
}
ereport(LOG, (errmodule(MOD_THREAD_POOL),
errmsg("pmState:%d, shut down all threadpool threads.", pmState)));
if (forceWait) {
/* Check until all groups have shut down their workers. */
/* 检查直到所有组都关闭了它们的工作线程。 */
bool allshut = false;
while (!allshut) {
allshut = true;
for (int i = 0; i < m_groupNum; i++) {
allshut = (m_groups[i]->AllThreadShutDown() && allshut);
allshut = (m_groups[i]->AllThreadShutDown() && allshut); // 检查每个组的线程是否都已关闭
}
pg_usleep(one_hundred_micro_sec);
pg_usleep(one_hundred_micro_sec); // 短暂休眠以减少 CPU 使用
}
ereport(LOG, (errmodule(MOD_THREAD_POOL),
@ -778,16 +804,16 @@ void ThreadPoolControler::ShutDownThreads(bool forceWait)
void ThreadPoolControler::ShutDownListeners(bool forceWait)
{
for (int i = 0; i < m_groupNum; i++) {
m_groups[i]->GetListener()->ShutDown();
m_groups[i]->GetListener()->ShutDown(); // 关闭所有线程组的监听器
}
if (forceWait) {
bool allshut = false;
while (!allshut) {
allshut = true;
for (int i = 0; i < m_groupNum; i++) {
allshut = (m_groups[i]->GetListener()->GetThreadId() == 0) && allshut;
allshut = (m_groups[i]->GetListener()->GetThreadId() == 0) && allshut; // 检查监听器线程是否都已关闭
}
pg_usleep(one_hundred_micro_sec);
pg_usleep(one_hundred_micro_sec); // 短暂休眠以减少 CPU 使用
}
}
}
@ -828,20 +854,20 @@ void ThreadPoolControler::AddWorkerIfNecessary()
ThreadPoolGroup* ThreadPoolControler::FindThreadGroupWithLeastSession()
{
int idx = 0;
float4 least_session = 0.0;
float4 session_per_thread = 0.0;
int idx = 0; // 用于记录具有最少会话的线程组的索引
float4 least_session = 0.0; // 用于记录最少会话数
float4 session_per_thread = 0.0; // 用于记录每个线程的平均会话数
least_session = m_groups[0]->GetSessionPerThread();
least_session = m_groups[0]->GetSessionPerThread(); // 获取第一个线程组的平均会话数作为初始值
for (int i = 1; i < m_groupNum; i++) {
session_per_thread = m_groups[i]->GetSessionPerThread();
if (session_per_thread < least_session) {
least_session = session_per_thread;
idx = i;
session_per_thread = m_groups[i]->GetSessionPerThread(); // 获取当前线程组的平均会话数
if (session_per_thread < least_session) { // 如果当前线程组的平均会话数更小
least_session = session_per_thread; // 更新最少会话数
idx = i; // 更新具有最少会话的线程组的索引
}
}
return m_groups[idx];
return m_groups[idx]; // 返回具有最少会话的线程组的指针
}
bool ThreadPoolControler::StayInAttachMode()
@ -855,15 +881,14 @@ int ThreadPoolControler::DispatchSession(Port* port)
knl_session_context* sc = NULL;
/*
* In comm_proxy mode, each accepted fd is combined with a fixed communicator thread in one NUMA group,
* we no longer distribute it with old mothod "group with latest sessions",
* so just return the communicator's NUMA group.
* comm_proxy模式下fd
* 线NUMA组中使
* 线NUMA组即可
*
* Note: We assume that each connected user session can be equal-possibily distributed to communicators,
* fortunately,it looks like Euler OS can guarantee this(proved),
* otherwise we need revisit it.
*
*
*
* Performance optimization with comm_proxy when thread_pool m_groupNum same as comm_proxy numa groups
* 使`comm_proxy``thread_pool``m_groupNum``comm_proxy`NUMA组数相同时
*/
if (AmIProxyModeSockfd(port->sock) && m_groupNum == g_comm_proxy_config.s_numa_num) {
CommSockDesc* comm_sock = g_comm_controller->FdGetCommSockDesc(port->sock);
@ -876,7 +901,7 @@ int ThreadPoolControler::DispatchSession(Port* port)
Assert(false);
return STATUS_ERROR;
}
/* if this group is hanged, we don't accept new session */
/* 如果这个组挂起了,我们不会接受新的会话。 */
if (grp->IsGroupHanged()) {
ereport(WARNING,
(errmodule(MOD_THREAD_POOL),
@ -893,29 +918,39 @@ int ThreadPoolControler::DispatchSession(Port* port)
}
/*
* Bind the specified thread to all the available CPUs.
* This is invoked by auxiliary thread, such as WALSender.
* 线CPU
* 线WAL发送者
*/
void ThreadPoolControler::BindThreadToAllAvailCpu(ThreadId thread) const
{
// 如果不需要绑定CPU直接返回
if (!CheckCpuBind()) {
return;
}
// 如果绑定方式是ALL_CPU_BIND也直接返回
if (m_cpuInfo.bindType == ALL_CPU_BIND) {
return;
}
// 创建一个CPU集合初始化为空
cpu_set_t availCpuSet;
CPU_ZERO(&availCpuSet);
// 遍历每个NUMA节点
for (int numaNo = 0; numaNo < m_cpuInfo.totalNumaNum; ++numaNo) {
int cpuNumber = m_cpuInfo.cpuArrSize[numaNo];
// 将每个NUMA节点上的CPU添加到CPU集合中
for (int i = 0; i < cpuNumber; ++i) {
CPU_SET(m_cpuInfo.cpuArr[numaNo][i], &availCpuSet);
}
}
// 使用pthread_setaffinity_np函数将线程绑定到CPU集合中
int ret = pthread_setaffinity_np(thread, sizeof(cpu_set_t), &availCpuSet);
// 如果绑定失败,输出警告信息
if (ret != 0)
ereport(WARNING, (errmsg("BindThreadToAllAvailCpu fail to bind thread %lu, errno: %d", thread, ret)));
}