优化Swtich case结构为双层表查询+Lamda表达式
目录
- 优化Swtich case结构为双层表查询+Lamda表达式
-
- 1. 优化背景
- 2.修改思路
- 3. 解决过程
-
- 3.1 Q1:这个switch我想给他改成表查询的结构以提升在case特别多的情况下的性能,但是这个case下还有嵌套if的情况,那么要怎么改成表查询呢?
- 3.2 Answer1:
- 3.3 思考
- 3.4 Q2:`using ArithInstFunc = std::function<void(uint32_t)>`这行代码什么意思
- 3.5 Answer2
- 3.6 思考
- 3.7 Q3:lambda表达式是什么
- 3.8 Answer3
- 3.9 思考
- 3.10 Q4:this cannot be used inside the body of this lambda
- 3.11 Answer4
- 3.12 思考
- 3.13 Q5:
- 3.14 Answer5:
- 3.15 Q6:
- 3.16 Answer6
- 3.17 Q7:这种查找表我想嵌套查找表该怎么引用呢
- 3.18 Answer7
- 3.19 Q8:一个外层查找表可以包含多个内层查找表么
- 3.20 Answer9
- 3.21 Q10:
- 3.22 Answer10
- 3.23 思考
- 3.24 Q11:为什么在例化的收不打印op exe这段代码,打印op init这段代码,而在调用exeOpmvv的时候打印op exe这段代码,不打印op init这段代码呢
- 3.25 Answer11
- 4. 大功告成
- 5.扩展
1. 优化背景
最近在工作中遇到了这样一段代码,是三层switch嵌套的结构,第一次swtich有4个case,第二层switch有上百个case,第二层switch下有的case下会有第三层swtich结构。
- 整段代码是可以正常工作的。
- 多次调用这段代码时,如果第二个switch结构中多次访问的是靠后的几个case,由于处理器设计的原因,会逐case去刷新缓存,这会让代码执行效率变得很差。那有什么办法可以去优化这个结构呢?
- 导师一眼看过来,就对着这坨说到改成表查询。那现在就开始修改这坨代码吧。(虽然什么表查询都学过,但是早就随风远去,这几年更是一直再写C,对C++也不熟练,趁此机会正好恶补一下吧)。
2.修改思路
首先让我们看一下要优化的结构伪代码:
#define OPMVV 0
#define OPIVV 1
#define OPSVV 2
#define OPDVV 3
int main(int argc, char** argv) {
int opcode_H = 0;
switch(opcode_H) {
switch(opcode_M) {
case 1:
break;
case 2:
switch(opcode_L) {
case 1:
break;
case 2
break;
//假设还有其他很多case
}
break;
//假设还有其他很多case
}
case OPMVV:
break;
case OPIVV:
break;
case OPSVV:
break;
case OPDVV:
break;
}
return 0;
}
在这段代码中,任一层的switch结构下的case都会调同一个函数,只是在调这个函数时,传的参数不一样。接下来让我记录一下我整个优化的过程。
这里留下一个还没解决的问题,那就是如果不同的case下调用的函数不同该怎么处理
3. 解决过程
3.1 Q1:这个switch我想给他改成表查询的结构以提升在case特别多的情况下的性能,但是这个case下还有嵌套if的情况,那么要怎么改成表查询呢?
switch(ncc_inst.ncc_ct_opcode.range(31,26)){
case OPMVV_VMANDNOT:
fifo_inst.SetArithInst(ncc_inst,id,VMANDNOT,0);
break;
case OPMVV_VMAND:
fifo_inst.SetArithInst(ncc_inst,id,VMAND,0);
break;
case OPMVV_VMOR:
fifo_inst.SetArithInst(ncc_inst,id,VMOR,0);
break;
case OPMVV_VCPOP:
if (ncc_inst.ncc_ct_opcode.range(19,15)==0b10000)
fifo_inst.SetArithInst(ncc_inst,id,VCPOP,0);
else if(ncc_inst.ncc_ct_opcode.range(19,15)==0b10001)
fifo_inst.SetArithInst(ncc_inst,id,VFIRST,0);
else
this->vcsr.SetVill(1);
break;
default:
break;
}
3.2 Answer1:
将 switch-case
结构改为表查询是一种常见的优化方法,尤其是当 case
条件很多时。要实现这种优化,我们可以通过使用函数指针或 lambda 表达式的方式,将 case
中的处理逻辑放到一个查找表中。对于存在嵌套 if
逻辑的情况,也可以通过合并不同的条件到表中的不同函数来处理。
方案 1:使用函数指针查找表
你可以将每个 case
的处理逻辑封装成独立的函数或 lambda 表达式,并创建一个映射表。对于嵌套的 if
条件,可以在处理函数内处理这些逻辑。
实现步骤:
- 定义一个类型为函数指针或 lambda 表达式的数组或映射表。
- 使用 opcode 值查找对应的函数。
- 嵌套
if
可以在处理函数内完成。
示例代码:
#include <functional>
#include <unordered_map>
// 定义处理函数类型
using ArithInstFunc = std::function<void(uint32_t)>;
// 假设定义的这些常量
constexpr uint32_t OPMVV_VMANDNOT = 0x01;
constexpr uint32_t OPMVV_VMAND = 0x02;
constexpr uint32_t OPMVV_VMOR = 0x03;
constexpr uint32_t OPMVV_VCPOP = 0x04;
// 创建查找表
std::unordered_map<uint32_t, ArithInstFunc> opcodeTable;
// 初始化查找表
void initOpcodeTable() {
opcodeTable[OPMVV_VMANDNOT] = [&](uint32_t ncc_inst) {
fifo_inst.SetArithInst(ncc_inst, id, VMANDNOT, 0);
};
opcodeTable[OPMVV_VMAND] = [&](uint32_t ncc_inst) {
fifo_inst.SetArithInst(ncc_inst, id, VMAND, 0);
};
opcodeTable[OPMVV_VMOR] = [&](uint32_t ncc_inst) {
fifo_inst.SetArithInst(ncc_inst, id, VMOR, 0);
};
opcodeTable[OPMVV_VCPOP] = [&](uint32_t ncc_inst) {
// 嵌套的 if 在这里处理
if (ncc_inst.ncc_ct_opcode.range(19,15) == 0b10000) {
fifo_inst.SetArithInst(ncc_inst, id, VCPOP, 0);
} else if (ncc_inst.ncc_ct_opcode.range(19,15) == 0b10001) {
fifo_inst.SetArithInst(ncc_inst, id, VFIRST, 0);
} else {
this->vcsr.SetVill(1);
}
};
}
// 查找并执行对应的指令处理函数
void executeOpcode(uint32_t opcode, uint32_t ncc_inst) {
if (opcodeTable.find(opcode) != opcodeTable.end()) {
opcodeTable[opcode](ncc_inst);
} else {
// 默认情况处理
}
}
解释:
- 函数类型定义:
ArithInstFunc
是一个std::function
类型,代表处理某个opcode
的函数。 - 查找表初始化:
opcodeTable
是一个映射表,将每个opcode
与其对应的处理函数关联。 - 处理函数:每个
case
语句中的逻辑都封装在 lambda 表达式中,并且嵌套的if
逻辑也在对应的 lambda 内处理。 - 查找并执行:
executeOpcode
函数根据opcode
查找并执行对应的处理函数。
方案 2:使用数组作为查找表
如果 opcode
值是连续的,可以使用数组而不是 unordered_map
来提高查找效率。
using ArithInstFunc = std::function<void(uint32_t)>;
// 假设 opcode 的范围是 0 到 255
ArithInstFunc opcodeTable[256];
// 初始化查找表
void initOpcodeTable() {
opcodeTable[OPMVV_VMANDNOT] = [&](uint32_t ncc_inst) {
fifo_inst.SetArithInst(ncc_inst, id, VMANDNOT, 0);
};
// 其他初始化同前
}
// 查找并执行
void executeOpcode(uint32_t opcode, uint32_t ncc_inst) {
if (opcode < 256 && opcodeTable[opcode]) {
opcodeTable[opcode](ncc_inst