很抱歉不同意当前接受的答案。这是 2021 年。现代编译器及其优化器不应再区分switch
等价if
链。如果他们仍然这样做,并且为任一变体创建优化不佳的代码,则写信给编译器供应商(或在此处公开,这具有更高的受尊重变化),但不要让微优化影响您的编码风格。
所以,如果你使用:
switch (numError) { case ERROR_A: case ERROR_B: ... }
或者:
if(numError == ERROR_A || numError == ERROR_B || ...) { ... }
或者:
template<typename C, typename EL>
bool has(const C& cont, const EL& el) {
return std::find(cont.begin(), cont.end(), el) != cont.end();
}
constexpr std::array errList = { ERROR_A, ERROR_B, ... };
if(has(errList, rnd)) { ... }
不应该对执行速度产生影响。但是根据您正在从事的项目,它们可能会在编码清晰度和代码可维护性方面产生重大影响。例如,如果您必须在代码的许多地方检查某个错误列表,那么模板has()
可能更容易维护,因为 errList 只需要在一个地方更新。
谈到当前的编译器,我已经编译了下面引用的测试代码clang++ -O3 -std=c++1z
(版本 10 和 11)和g++ -O3 -std=c++1z
. 两个 clang 版本都给出了类似的编译代码和执行时间。所以我从现在开始只谈论版本 11。最值得注意的是,functionA()
(which uses if
) 和functionB()
(which uses switch
) 使用 ! 生成完全相同的汇编程序输出clang
。并functionC()
使用跳台,尽管许多其他海报认为跳台是switch
. 然而,尽管许多人认为跳转表是最优的,但这实际上是最慢的解决方案clang
:需要比orfunctionC()
多 20% 的执行时间。functionA()
functionB()
手动优化的版本functionH()
是迄今为止最快的clang
。它甚至部分展开循环,在每个循环上进行两次迭代。
实际上,clang
计算了在functionH()
、functionA()
和中明确提供的位域functionB()
。但是,它在 and 中使用了条件分支functionA()
,functionB()
这使得这些分支变得很慢,因为分支预测经常失败,而它adc
在functionH()
. 虽然它未能在其他变体中应用这种明显的优化,但我不知道。
生成的代码g++
看起来比 - 复杂得多,clang
但实际上运行速度要functionA()
快一些,对于functionC()
. 在非手动优化的函数中,functionC()
是最快的g++
,比clang
. 相反,functionH()
编译时需要两倍的执行时间g++
而不是 with clang
,主要是因为g++
不进行循环展开。
以下是详细结果:
clang:
functionA: 109877 3627
functionB: 109877 3626
functionC: 109877 4192
functionH: 109877 524
g++:
functionA: 109877 3337
functionB: 109877 4668
functionC: 109877 2890
functionH: 109877 982
如果常量在整个代码32
中更改为,则性能会发生巨大变化:63
clang:
functionA: 106943 1435
functionB: 106943 1436
functionC: 106943 4191
functionH: 106943 524
g++:
functionA: 106943 1265
functionB: 106943 4481
functionC: 106943 2804
functionH: 106943 1038
加速的原因是,如果测试的最高值是 63,编译器会删除一些不必要的绑定检查,因为rnd
无论如何, 的值都绑定到 63。请注意,删除绑定检查后,未优化的functionA()
using simple if()
ong++
执行速度几乎与 hand-optimized 一样快functionH()
,并且它还产生相当相似的汇编器输出。
结论是什么?如果您大量手动优化和测试编译器,您将获得最快的解决方案。任何假设是否switch
更好if
,都是无效的——它们在clang
. 并且检查值的易于编码的解决方案array
实际上是最快的情况g++
(如果省略手动优化和按事件匹配列表的最后一个值)。
未来的编译器版本会越来越好地优化你的代码,越来越接近你的手工优化。所以不要在这上面浪费你的时间,除非周期对你来说真的很重要。
这里是测试代码:
#include <iostream>
#include <chrono>
#include <limits>
#include <array>
#include <algorithm>
unsigned long long functionA() {
unsigned long long cnt = 0;
for(unsigned long long i = 0; i < 1000000; i++) {
unsigned char rnd = (((i * (i >> 3)) >> 8) ^ i) & 63;
if(rnd == 1 || rnd == 7 || rnd == 10 || rnd == 16 ||
rnd == 21 || rnd == 22 || rnd == 63)
{
cnt += 1;
}
}
return cnt;
}
unsigned long long functionB() {
unsigned long long cnt = 0;
for(unsigned long long i = 0; i < 1000000; i++) {
unsigned char rnd = (((i * (i >> 3)) >> 8) ^ i) & 63;
switch(rnd) {
case 1:
case 7:
case 10:
case 16:
case 21:
case 22:
case 63:
cnt++;
break;
}
}
return cnt;
}
template<typename C, typename EL>
bool has(const C& cont, const EL& el) {
return std::find(cont.begin(), cont.end(), el) != cont.end();
}
unsigned long long functionC() {
unsigned long long cnt = 0;
constexpr std::array errList { 1, 7, 10, 16, 21, 22, 63 };
for(unsigned long long i = 0; i < 1000000; i++) {
unsigned char rnd = (((i * (i >> 3)) >> 8) ^ i) & 63;
cnt += has(errList, rnd);
}
return cnt;
}
// Hand optimized version (manually created bitfield):
unsigned long long functionH() {
unsigned long long cnt = 0;
const unsigned long long bitfield =
(1ULL << 1) +
(1ULL << 7) +
(1ULL << 10) +
(1ULL << 16) +
(1ULL << 21) +
(1ULL << 22) +
(1ULL << 63);
for(unsigned long long i = 0; i < 1000000; i++) {
unsigned char rnd = (((i * (i >> 3)) >> 8) ^ i) & 63;
if(bitfield & (1ULL << rnd)) {
cnt += 1;
}
}
return cnt;
}
void timeit(unsigned long long (*function)(), const char* message)
{
unsigned long long mintime = std::numeric_limits<unsigned long long>::max();
unsigned long long fres = 0;
for(int i = 0; i < 100; i++) {
auto t1 = std::chrono::high_resolution_clock::now();
fres = function();
auto t2 = std::chrono::high_resolution_clock::now();
auto duration = std::chrono::duration_cast<std::chrono::microseconds>(t2 - t1).count();
if(duration < mintime) {
mintime = duration;
}
}
std::cout << message << fres << " " << mintime << std::endl;
}
int main(int argc, char* argv[]) {
timeit(functionA, "functionA: ");
timeit(functionB, "functionB: ");
timeit(functionC, "functionC: ");
timeit(functionH, "functionH: ");
timeit(functionA, "functionA: ");
timeit(functionB, "functionB: ");
timeit(functionC, "functionC: ");
timeit(functionH, "functionH: ");
timeit(functionA, "functionA: ");
timeit(functionB, "functionB: ");
timeit(functionC, "functionC: ");
timeit(functionH, "functionH: ");
return 0;
}