我在C / C ++中有很多代码序列 分支,这样的事情:
if( condition1 )
return true;
if( condition2 )
return true;
...
return false;
(相当于返回condition1 || condition2 || ...;)
评估每个条件需要多次内存访问(所有只读),但编译器在评估先前条件之前不移动内存访问,从而错过了一个重要的优化机会。当condition1为真时,condition2的内存访问的原因可能是段错误。 嗯,我知道他们没有,我希望编译器能够做出明智的事情并将这些代码序列中的一些混合在一起,以适应性能,例如:利用指令级并行性。 我也不想将条件更改为逻辑或(不是短路),因为其中一个分支可能会跳出来。
关于如何实现这一点的任何想法(最好使用gcc)?
感谢。
答案 0 :(得分:6)
评估每个条件需要多次内存访问
为什么不避免在个别条件下进行短路评估,而是让条件出现?
你究竟如何实现前者取决于这些条件的性质(即你的代码中condition1
,condition2
) - 如果你对它们一无所知我只能谈论一般性:它们在内部的位置包含短路运算符,而不是将布尔值转换为整数表示并使用例如按位或(如果它读得更好并且在您的特定用途中工作,甚至是“+”或“*”)。按位运算符通常更安全,因为它们的优先级较低 - 只有在条件已经包含按位运算符时才需要小心。
举例说明:
OLD: return (a > 4 && b == 2 && c < a) || // condition1
(a == 3 && b != 2 && c == -a); // condition2
NEW: return (a > 4 & b == 2 & c < a) ||
(a == 3 & b != 2 & c == -a);
如果您之前使用隐式转换数字/指针到bool
,请注意......您希望将它们标准化为bool
,以便它们的最低有效位反映它们的布尔值:
OLD: return my_int && my_point && !my_double;
NEW: return bool(my_int) & bool(my_point) & !my_double; // ! normalises before bitwise-&
您可能还希望以...
为基准 bool condition1 = a > 4 & b == 2 & c < a;
bool condition2 = a == 3 & b != 2 & c == -a;
return condition1 || condition2;
...可能更快 - 可能只在整个“返回错误”的情况下,也许当最后一个条件N或2是“返回true”的决定因素时。
单独对具有重载逻辑运算符的对象禁用短路评估,这为您使用现有表示法进行检查提供了另一种途径,但您必须更改或增强数据类型。
更一般地说,如果你在每个条件中组合了大量的断言,那么你只会受益于此 - 如果函数倾向于通过返回false则更多。
“AProgrammer”也是一个很好的观点 - 在现代CPU上可以使用推测执行,CPU可能已经超过了短路评估所暗示的顺序(在某些特殊模式下,可以避免或抑制任何因解除引用而导致的内存故障无效指针,除以0等)。因此,整个优化尝试可能会毫无意义甚至适得其反。需要对所有替代品进行基准测试。
答案 1 :(得分:5)
您可以自己移动条件的部分吗?
即
const bool bCondition1Result = <condition1>;
const bool bCondition2Result = <condition2>;
等等
为了更好地进行优化,请重新按照您的条件顺序进行操作,以便最受欢迎的一个是第一个要检查的。通过这种方式,它会提前出现(这可能会产生很小的差别)。
答案 2 :(得分:2)
查看gcc提供的__builtin_expect函数。当使用linux内核定义likely / unlikely宏时,可以直观地使用这些宏,而对代码可读性几乎没有影响。
答案 3 :(得分:1)
不要打扰。 CPU已经具有无序执行,推测执行和分支预测。这个级别的任何差异都不太可能产生任何差异。指令级并行性由CPU隐式完成,而不是由编译器显式完成。也许GCC没有做任何事情,因为没有什么可以获得。
在这个注意事项中,你必须有一个地狱的条件来改变一个非平凡的应用程序的运行时间。
哦,逻辑或是标准保证是短路。