我们有一种将命令数据写入设备的方法。该方法首先将数据转换为设备接受的格式,然后将数据写入串行端口。数据转换使用下面给出的case语句完成。对于10个命令,我们需要转换数据。对于其他命令,我们不必转换数据(大约10个命令)。
客户投诉代码未经优化。经常使用一些不需要数据转换的命令。
是否会引用if else语句优化代码?
在这种情况下,还有其他选项来优化代码吗?
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 :(得分:5)
如果没有优化"你的客户意味着重复,看起来你可以将大量的逻辑放在表格中。
假设您的命令类型按顺序编号,即
#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;
}
如果命令的起始索引不为0,则您需要从cmd_no
中减去最低的命令编号,以使索引进入表中。
答案 1 :(得分:3)
将if if语句直接优化代码吗?
不,if - else if
列表与在机器代码级别使用switch
完全相同,因为if - else if
具有性质
if(integer == 1)
{ ... }
else if (integer == 2)
{ ... }
其中1和2是任何类型的编译时整数常量。在这种情况下,它将产生100%等效的机器代码
switch(integer)
{
case 1: ... break;
case 2: ... break;
}
在这种情况下,还有其他选项来优化代码吗?
一些事情:
由于您将结果转换为int,因此在此处需要浮点时并不明显。用整数计算代替浮点计算可能会提高性能,特别是在缺少FPU的微控制器系统等方面。如果浮点数的唯一目的是舍入除法,那么考虑
// probably slow
int32_t a = (int32_t)((((double)cmd_data * CMD_WR_ACC_PARA1) / CMD_WR_ACC_PARA2) + 0.5);
相当于
// probably faster
int32_t b = ( (cmd_data * CMD_WR_ACC_PARA1) + CMD_WR_ACC_PARA2/2 ) / CMD_WR_ACC_PARA2;
答案 2 :(得分:1)
这甚至不需要case语句,因为它易于矢量化。最简单的方法之一是使用X-macro。
由于没有提供任何值,我只使用顺序整数作为占位符
"optional": ["es7.classProperties"]
编辑: 实际上,如果经常调用代码,则switch语句可能更加优化,但是在紧密循环中不是这样,因为大多数编译器都可以生成跳转表。如果启用了正确的选项,则其他参数甚至可以编译为单个常量(例如-Ofast with gcc)。幸运的是,X-macro也可以处理你的case语句:
#define XMACRO(X,...) \
/*Label, Parameter1, Parameter2,...*/ \
X(ACC, 1.0, 2.0, __VA_ARGS__) \
X(BIAS, 3.0, 4.0, __VA_ARGS__) \
X(SUP, 5.0, 6.0, __VA_ARGS__) \
X(FILL, 7,0, 8.0, __VA_ARGS__) \
//more entries here
#define AS_ENUM(label,...) CMD_WR_##label,
enum commands { XMACRO(AS_ENUM) CMD_WR_COUNT };
#define AS_PREMULTIPLIED(label, p1, p2,...) ((p1)/(p2)),
const double convert_multipliers[CMD_WR_COUNT] = { XMACRO(AS_PREMULTIPLIED) };
int convert_data(int cmd_data, unsigned cmd_no){
return (cmd_no >= CMD_WR_COUNT)
? cmd_data
: (int) (cmd_data * convert_multipliers[cmd_no] + 0.5);
}
了解其性能是否已经过优化的唯一方法是测量,但X-macros将有助于消除重复代码,并允许您以易于阅读和修改的表格格式将数据保存在一起(并且至少看起来优化) 。当你必须在路上添加另一个参数时,你只需要添加一行而不是遍历每个函数。
答案 3 :(得分:0)
将代码从交换机重写为if / then / else不会有太大改善。首先要尝试的是使用设置为最高的优化选项进行编译。让编程器,gcc我假设,为您优化它。这就是低悬的果实。
这对此有何帮助? 默认情况下,GCC通过代码大小和调试简易性来平衡代码速度优化。默认情况下,生成的gcc很容易调试,因为代码流与源代码更紧密地匹配,并且存储了变量(这很慢)但是这使得使用gdb和断点更容易调试,因为变量很容易在堆栈上可见。
当我们启用优化时,会进行更多优化,包括仅将一些局部变量存储在处理器寄存器(速度很快)中,并通过重新排序代码来优化通过cpu执行管道的代码流。缺点是单步执行机器代码时更难以遵循。编译也需要稍长的时间。 但值得!
要将优化标志设置为最高,请尝试-O3,以便为代码大小和执行时间设置更多优化:
-O3
这里有关于优化标志的很好的讨论: https://stackoverflow.com/a/32941238/6693299
使用优化标志集进行编译后,查看使用昂贵的浮点运算的数学运算。你需要它们吗?双乘法和除法是昂贵的,只是将它们转换为int32s?你确定需要这样做吗?你可以将数学转换为仅使用整数运算吗?
您可以分享列出的常量的值吗?您可以将部分或全部数学转换为查找表。
答案 4 :(得分:0)
您可以将公共代码重新组合在一起,并在编译或初始化时执行除法,而不是执行。
我假设CMD_WR_ *是增量(枚举的元素)。
首先在这里进行分组:
SELECT onsite.[WorkOrder],
DATEDIFF(MINUTES, onsite.OnSiteSubmitted, offsite.OnSiteSubmitted)
FROM [DBName].[dbo].[sw_onsite_offsite] onsite join [DBName].[dbo].[sw_onsite_offsite] offsite on onsite.WorkOrder = offsite.WorkOrder
WHERE onsite.Name = 'On-Site' AND
offsite.Name = 'Off-Site'
ORDER BY WorkOrder
然后用if / else(如果有的话)进行计算:
double coef[] = {CMD_WR_ACC_PARA1/CMD_WR_ACC_PARA2, CMD_WR_BIAS_PARA1/CMD_WR_BIAS_PARA2, CMD_WR_SUP_PARA1/CMD_WR_SUP_PARA2, CMD_WR_FIL_PARA1/CMD_WR_FIL_PARA2, etc};
如果它们很多,可以使用开关:
if (cmd_no >= CMD_WR_ACC && cmd_no <= CMD_WR_FIL) {
converted_command_data = (INT32)((DOUBLE)cmd_data * coef[cmd_no-CMD_WR_ACC]) + 0.5;
} else {
converted_command_data = cmd_data;
}
(我不认为这会在性能方面产生重大影响)