我一直在阅读有关bit twiddling hacks的内容并认为,编译器是否能够避免在以下代码中进行分支:
constexpr int min(const int lhs, const int rhs) noexcept {
if (lhs < rhs) {
return lhs;
}
return rhs
}
将其替换为(explanation):
constexpr int min(const int lhs, const int rhs) noexcept {
return rhs ^ ((lhs ^ rhs) & -(lhs < rhs));
}
答案 0 :(得分:7)
我的建议:
如果您需要获得特定系统的最后一点性能,那么只需尝试两种变体并进行测量(并每次验证结果,更新CPU和/或编译器)。对于任何其他情况,假设编译器至少与您进行低级优化一样好。我还建议您首先了解所有与优化相关的编译器标志,并在开始使用低级优化之前设置适当的基准。
我认为手部优化有时仍然有益的唯一区域是,如果您想要最佳地使用矢量单位。现代编译器可以自动向量化很多东西,但这仍然是一个相对较新的领域,并且有些东西不允许编译器执行,因为它违反了标准的某些保证(特别是涉及浮点运算的地方)。 p>
(1)有些人似乎认为,独立于代码的外观,编译器将始终生成提供相同语义的最佳代码。首先,编译器在有限的时间内可以做什么是有限的(有很多启发式工作最有效,但并非总是如此)。其次,在许多情况下,c ++标准要求编译器提供某些保证,目前您实际上并不感兴趣,但仍然阻止优化。
答案 1 :(得分:4)
clang++
(3.5.2-1)似乎足够聪明-O3
(我不使用c ++ 11或c ++ 14,constexpr
和{ {1}}从源代码中删除):
noexcept
08048760 <_Z3minii>:
8048760: 8b 44 24 08 mov 0x8(%esp),%eax
8048764: 8b 4c 24 04 mov 0x4(%esp),%ecx
8048768: 39 c1 cmp %eax,%ecx
804876a: 0f 4e c1 cmovle %ecx,%eax
804876d: c3 ret
(4.9.3)(gcc
)改为使用-O3
进行分支:
jle
(x86 32位)
此08048740 <_Z3minii>:
8048740: 8b 54 24 08 mov 0x8(%esp),%edx
8048744: 8b 44 24 04 mov 0x4(%esp),%eax
8048748: 39 d0 cmp %edx,%eax
804874a: 7e 02 jle 804874e <_Z3minii+0xe>
804874c: 89 d0 mov %edx,%eax
804874e: f3 c3 repz ret
(已损坏)是位备选方案(来自min2
):
gcc
答案 2 :(得分:0)
编译器可以检测此模式并将其替换为您的提议。
但是,clang ++和g ++都不进行此优化,例如参见g++ 5.2.0's assembly output。