我已经读过现代C ++编译器将大switch
块转换为二进制树以便在运行时更快地查找,这是否适用于所有现代C ++编译器?我主要使用英特尔C ++编译器和G ++。
答案 0 :(得分:5)
如果您的case
值彼此接近,那么最有效的方法是跳转表,这通常是所有编译器的目标。如果值很稀疏,那么编译器通常会使用" binary-if-tree"。
最佳解决方案:不要考虑它。假设switch
速度相当快。其他一切都闻起来像是过早优化。如果您确实遇到switch
是程序的瓶颈,那么您只能通过查看生成的目标代码来了解生成的内容。
如果您只在每种情况下分配一个变量,编译器甚至可能生成完全不同的东西,如条件移动。所以,如果不看生成的装配,你永远不会知道。
如果你有很多稀疏值,那么使用哈希表可能比switch
更快,因为与{34}的O(1)
相比,它是O(log n)
-if树"
答案 1 :(得分:2)
当然,即使是GCC,例如:
int test(int x) {
switch (x) {
case 0:
return 2;
case 1:
return 7;
case 3:
return 11;
case 4:
return 2;
case 5:
return 133;
case 6:
return 500;
}
}
在定位x64时,在我测试的所有GCC版本上都变成了跳转表。 (甚至在-O0上)
“将大switch
块转换为二进制树以便更快地查找”也具有误导性,确定它比线性if
/ else
链更快(除非第一项非常极端比后来更有可能),但跳转表可能更快。
另一方面,跳转表不能很好地处理稀疏情况,除非你使用special techniques,但据我所知,还没有真正完成(但是?)。此外,CPU通常预测间接分支不太好 - 通常只是预测它总是与上次一样。
他们实际上只有不同的性能特征,switch
的所有可能实现都没有赢。这取决于。
答案 2 :(得分:2)
我建议测量执行时间。如果您的代码需要优化,那么您可以做出明智的决定。
你提到这是你的用例:
我有一个带有> 400长整数的开关,它们是CRC-64校验和 ISO 639语言名称,我使用它来快速查找ISO语言 名。
通常您会使用其中一种STL数据类型。例如,在您的情况下,std::map
或std::unordered_set
值得一试。 std::map
使用红色/黑色树,std::unordered_set
使用哈希表。
答案 3 :(得分:2)
过去曾在编译器上工作过......
首先处理switch语句的正常方法是排序 案例值,然后寻找紧凑的块,其中有 相邻或接近相邻的值。这样的块将是 分离出来,用跳台处理;一个数组 目标地址,由值索引(减去值的值) 表的第一个元素)。任何剩余的值都将是 使用硬编码二进制搜索进行检查。
这是大约25年前的标准程序。我会假设 如果它从那时起发生了变化,那是因为有人有 想出一个更好的解决方案。 (数学上,它是 很容易证明你无法在中获得更好的大O. 一般情况。)例如,现代编译器可能会使用 分析器信息,以确定哪些情况更有可能, 如果它们更有可能,请先测试它们。