C 优化案例陈述
我们有一种将命令数据写入设备的方法。该方法首先将数据转换为设备接受的格式,然后将数据写入串行端口。数据转换使用下面给出的case语句完成。对于10个命令,我们需要转换数据。对于其他命令,我们不必转换数据(大约10个命令) 客户投诉代码未优化。一些不需要数据转换的命令经常使用 direct if else语句会优化代码吗?C 优化案例陈述,c,optimization,C,Optimization,我们有一种将命令数据写入设备的方法。该方法首先将数据转换为设备接受的格式,然后将数据写入串行端口。数据转换使用下面给出的case语句完成。对于10个命令,我们需要转换数据。对于其他命令,我们不必转换数据(大约10个命令) 客户投诉代码未优化。一些不需要数据转换的命令经常使用 direct if else语句会优化代码吗? 在这种情况下,是否有其他选项来优化代码 switch (cmd_no) { case CMD_WR_ACC: converted_command_data = (INT
在这种情况下,是否有其他选项来优化代码
switch (cmd_no)
{
case CMD_WR_ACC:
converted_command_data = (INT32)((((DOUBLE)cmd_data * CMD_WR_ACC_PARA1) / CMD_WR_ACC_PARA2) + 0.5);
break;
case CMD_WR_BIAS:
converted_command_data = (INT32)((((DOUBLE)cmd_data * CMD_WR_BIAS_PARA1) / CMD_WR_BIAS_PARA2) + 0.5);
break;
case CMD_WR_SUP:
converted_command_data = (INT32)((((DOUBLE)cmd_data * CMD_WR_SUP_PARA1) / CMD_WR_SUP_PARA2) + 0.5);
break;
case CMD_WR_FIL:
converted_command_data = (INT32)((((DOUBLE)cmd_data * CMD_WR_FIL_PARA1) / CMD_WR_FIL_PARA2) + 0.5);
break;
.
.
default:
converted_command_data = cmd_data;
break;
}
如果客户所说的“未优化”意味着重复,那么看起来您可以在表中列出大量的逻辑
假设您的命令类型是按顺序编号的,即
#define CMD_WR_ACC 0
#define CMD_WR_BIAS 1
#define CMD_WR_SUP 2
...
您可以这样定义表:
struct params {
int convert; // whether or not to convert
double param1;
double param2;
} params_table[] = {
{ 1, CMD_WR_ACC_PARA1, CMD_WR_ACC_PARA2 },
{ 1, CMD_WR_BIAS_PARA1, CMD_WR_BIAS_PARA2 },
{ 1, CMD_WR_SUP_PARA1, CMD_WR_SUP_PARA2 },
...
{ 0, 0, 0}
...
};
if (params_table[cmd_no].convert) {
converted_command_data = (INT32)((((DOUBLE)cmd_data *
params_table[cmd_no].param1) / params_table[cmd_no].param2) + 0.5);
} else {
converted_command_data = cmd_data;
}
然后,您的代码如下所示:
struct params {
int convert; // whether or not to convert
double param1;
double param2;
} params_table[] = {
{ 1, CMD_WR_ACC_PARA1, CMD_WR_ACC_PARA2 },
{ 1, CMD_WR_BIAS_PARA1, CMD_WR_BIAS_PARA2 },
{ 1, CMD_WR_SUP_PARA1, CMD_WR_SUP_PARA2 },
...
{ 0, 0, 0}
...
};
if (params_table[cmd_no].convert) {
converted_command_data = (INT32)((((DOUBLE)cmd_data *
params_table[cmd_no].param1) / params_table[cmd_no].param2) + 0.5);
} else {
converted_command_data = cmd_data;
}
如果命令的起始索引不是0,则需要从cmd\u no
中减去最低的命令编号,才能将索引放入表中。如果“未优化”意味着重复,则您可以将大量此逻辑放入表中
假设您的命令类型是按顺序编号的,即
#define CMD_WR_ACC 0
#define CMD_WR_BIAS 1
#define CMD_WR_SUP 2
...
您可以这样定义表:
struct params {
int convert; // whether or not to convert
double param1;
double param2;
} params_table[] = {
{ 1, CMD_WR_ACC_PARA1, CMD_WR_ACC_PARA2 },
{ 1, CMD_WR_BIAS_PARA1, CMD_WR_BIAS_PARA2 },
{ 1, CMD_WR_SUP_PARA1, CMD_WR_SUP_PARA2 },
...
{ 0, 0, 0}
...
};
if (params_table[cmd_no].convert) {
converted_command_data = (INT32)((((DOUBLE)cmd_data *
params_table[cmd_no].param1) / params_table[cmd_no].param2) + 0.5);
} else {
converted_command_data = cmd_data;
}
然后,您的代码如下所示:
struct params {
int convert; // whether or not to convert
double param1;
double param2;
} params_table[] = {
{ 1, CMD_WR_ACC_PARA1, CMD_WR_ACC_PARA2 },
{ 1, CMD_WR_BIAS_PARA1, CMD_WR_BIAS_PARA2 },
{ 1, CMD_WR_SUP_PARA1, CMD_WR_SUP_PARA2 },
...
{ 0, 0, 0}
...
};
if (params_table[cmd_no].convert) {
converted_command_data = (INT32)((((DOUBLE)cmd_data *
params_table[cmd_no].param1) / params_table[cmd_no].param2) + 0.5);
} else {
converted_command_data = cmd_data;
}
如果命令的起始索引不是0,则需要从
cmd\u no
中减去最低的命令编号,才能将索引放入表中。将代码从开关重写为If/then/else不会有太大的改进。首先要尝试的是将优化选项设置为最高值进行编译。我想,让编译器为您优化它。这就是低垂的果实通常所在的地方
这有什么帮助?
默认情况下,GCC平衡了代码速度优化与代码大小和调试容易性。默认情况下,gcc生成易于调试的代码,因为代码流与源代码更接近,并且存储了变量(速度较慢),但这使得使用gdb和断点进行调试更容易,因为变量在堆栈上很容易看到
当我们打开优化时,会进行更多的优化,包括只在处理器寄存器中存储一些局部变量(速度很快),以及通过重新排序代码来优化cpu执行管道中的代码流。缺点是当单步执行机器代码时更难遵循。编译也需要稍长的时间但这是值得的强>
要在优化标志设置为最高值的情况下进行编译,请尝试-O3,它会对代码大小和执行时间设置更多优化:
-O3
关于优化标志,这里有一个很好的讨论:
在使用优化标志集编译之后,请查看使用昂贵浮点运算的数学。你需要它们吗?双乘法和除法很昂贵,只是为了将它们转换成INT32?你确定需要这样做吗。你能把数学转换成只使用整数运算吗
您可以共享列出的常量的值吗?您可能能够将部分或全部数学转换为查找表。将代码从一个开关重写为if/then/else不会有太大的改进。首先要尝试的是将优化选项设置为最高值进行编译。我想,让编译器为您优化它。这就是低垂的果实通常所在的地方 这有什么帮助? 默认情况下,GCC平衡了代码速度优化与代码大小和调试容易性。默认情况下,gcc生成易于调试的代码,因为代码流与源代码更接近,并且存储了变量(速度较慢),但这使得使用gdb和断点进行调试更容易,因为变量在堆栈上很容易看到 当我们打开优化时,会进行更多的优化,包括只在处理器寄存器中存储一些局部变量(速度很快),以及通过重新排序代码来优化cpu执行管道中的代码流。缺点是当单步执行机器代码时更难遵循。编译也需要稍长的时间但这是值得的强> 要在优化标志设置为最高值的情况下进行编译,请尝试-O3,它会对代码大小和执行时间设置更多优化:
-O3
关于优化标志,这里有一个很好的讨论:
在使用优化标志集编译之后,请查看使用昂贵浮点运算的数学。你需要它们吗?双乘法和除法很昂贵,只是为了将它们转换成INT32?你确定需要这样做吗。你能把数学转换成只使用整数运算吗
您可以共享列出的常量的值吗?您可以将部分或全部数学转换为查找表
directif-else语句会优化代码吗
否,if-else if
的列表与在机器代码级别上使用开关
完全相同,因为if-else if
的性质相同
if(integer == 1)
{ ... }
else if (integer == 2)
{ ... }
其中1和2是任何类型的编译时整数常量。在这种情况下,它将产生100%的等效机器代码
switch(integer)
{
case 1: ... break;
case 2: ... break;
}
在这种情况下,是否有其他选项来优化代码
switch (cmd_no)
{
case CMD_WR_ACC:
converted_command_data = (INT32)((((DOUBLE)cmd_data * CMD_WR_ACC_PARA1) / CMD_WR_ACC_PARA2) + 0.5);
break;
case CMD_WR_BIAS:
converted_command_data = (INT32)((((DOUBLE)cmd_data * CMD_WR_BIAS_PARA1) / CMD_WR_BIAS_PARA2) + 0.5);
break;
case CMD_WR_SUP:
converted_command_data = (INT32)((((DOUBLE)cmd_data * CMD_WR_SUP_PARA1) / CMD_WR_SUP_PARA2) + 0.5);
break;
case CMD_WR_FIL:
converted_command_data = (INT32)((((DOUBLE)cmd_data * CMD_WR_FIL_PARA1) / CMD_WR_FIL_PARA2) + 0.5);
break;
.
.
default:
converted_command_data = cmd_data;
break;
}
有几件事:
- 如果开关使用的常数是相邻的,最好是从0到n,则可以用函数指针跳转表替换整个开关。现代编译器应该做到这一点,但较旧的编译器可能会遇到困难
- 虽然这里需要浮点运算,但这并不明显,因为您将结果强制转换为int。用整数运算替换浮点运算可能会大大提高性能,尤其是在缺少FPU的微控制器系统等上。如果浮点数的唯一目的是圆除法,则考虑