一. 前言
openGauss向量化执行引擎是指原来计算过程中一行元组一行元组的处理的方式改成一批元组一批地处理,从而减少调用栈和CPU切换开销,一般在大数据量聚合查询中会有较大的性能提升。本文主要以向量化做sum聚合为例走读代码了解openGauss中是怎么实现向量化计算能力的。

向量化操作的执行计划如上所示,可以看到,向量化计算时,需要将tablescan的数据转成向量话数据计算,计算完成后再转成普通的行存数据输出。本文也按照这思路走读代码。
二. 生成向量化执行计划
执行计划生成是指将普通的执行计划转成向量化执行计划,入口在try_vectorize_plan中:
try_vectorize_plan
vectorize_plan
// 对各种算子进行向量化转换,以SeqScan为例
switch (nodeTag(result_plan)) {
case T_SeqScan:
make_rowtove_plan
make_rowtovec
RowToVec* node = makeNode(RowToVec); // 生成行转向量算子
plan->lefttree = lefttree;
}
if (IsVecOutput(top_plan))
top_plan = (Plan*)make_vectorow(top_plan); // 在最后的输出,还需要将向量转回行输出
三. 行转向量化
因为openGauss的默认数据是行存的,为了能向量化执行,需要转成列存,列存后每行数据就可以保存很多行元组的数据,从而方便处理。openGauss中行转列的函数入口为ExecVecToRow,如下为代码走读过程:
ExecVecToRow
if (BatchIsNull(current_batch)) {
VectorEngine
ExecRowToVec // 现将所有行转成向量的batch
ExecRowToVecBatchMode
ScanBatchResult *scanSlotBatch = ExecProcNode((PlanState*)seqScanState); // 扫描一个页的元祖
ExecSeqScan
HeapamGetNextBatchMode
HeapGetTupPageBatchmode
VectorizeTupleBatchMode // 一个页的元祖转换成向量
for (j = 0; j < rows; j++) { // 一个页的每一行处理
tableam_tslot_formbatch(slots[j], pBatch, j, scanstate->maxcolId);
heap_slot_formbatch
ScalarVector* pVector = &batch->m_arr[attno]; // 取出该列的列向量
pVector->m_vals[cur_rows] = heapGetInitDefVal // 在该列指定的行中保存元素,从而实现了行存到列存之间的转换
}
pOutBatch = ApplyProjectionAndFilterBatch // 谓词初步裁量
}
for (int i = 0; i < state->nattrs; i++) {
tuple->tts_values[i] = state->m_ttsvalues[tuple_subscript + i]; // 向量化中在转换成普通行元祖
}
四. 向量化计算
如下以sum为例介绍怎么向量化进行批量计算的:
HashAggRunner::Run()
Build();
outer_batch = m_hashSource->getBatch();
(this->*m_buildFun)(outer_batch); // 构建Hash表
HashAggRunner::buildAggTbl
hashBatch // 计算每行的hash值
for (i = 0; i < rows; i++) {
AllocHashSlot<simple, true>(batch, i); // 分区Hash存储空间并且将数据存放到对应的Hash位置上
HashAggRunner::AllocHashSlot
cell = (hashCell*)palloc(m_cellSize); // 分配空间
initCellValue<simple, false>(batch, cell, i) // 往对应的Hash位置上存放数据
}
BatchAggregation(batch); // 批量做聚合
for (i = 0; i < m_aggNum; i++) { // 所有的聚合算子
AggregationOnScalar
VecFunctionCallInvoke(fcinfo) // 调用向量聚合的
vinterval_sum // 以向量化做sum计算为例,会调用vinterval_sum
for (i = 0; i < nrows; i++) { // 批量聚合
result = interval_pl
}
}
p_res = Probe(); // 列存聚合的结果
五. 向量化转行
向量化计算完成后,需要重新转成普通的行存数据进行输出,代码入口在ExecVecToRow中
ExecVecToRow
DevectorizeOneBatch
for (i = 0; i < cols; i++) { // 列存又转回行存
column = ¤t_batch->m_arr[i];
for (j = 0; j < rows; j++)
state->m_ttsisnull[j * cols + i] = IS_NULL(column->m_flag[j]);
state->devectorizeFunRuntime[i](state, column, rows, cols, i);
DevectorizeOneColumn
for (int j = 0; j < rows; j++) {
k = j * cols + i; // 行存列存下标转换
switch (typid) {
state->m_ttsvalues[k] = xxx // 根据类型填充行存的数据
}
}
}

808

被折叠的 条评论
为什么被折叠?



