我一直试图制作一种回调函数,在这个过程中我意识到我喜欢的那个,也可能会改变CPU处理分支预测的方式。我知道过度优化是应该避免的,特别是在早期,但这是一种非常适合我使用的方法,并且具有避免缓存未命中的效果将是一个很好的奖励。
我并不了解CPU架构足以自己回答这个问题,所以我很好奇是否有人知道这是否会影响分支预测的处理方式,以及是否以有利的方式实现
我的初始解决方案如下所示:(请注意更大的块)
enum _myenum { t1, t2, t3 }myenum; //enumerator and switch
switch (myenum){
case t1:
{ /*a block*/ }
break;
case t2:
{ /*another block*/ }
break;
case t3:
{ /*yet another block*/ }
break;
default: break;
}
适合我的解决方案:
enum _myenum { t1, t2, t3 }myenum; //same enum
//a map of lambdas/functions
std::map<_myenum, std::function<void()>>lambda_map{
{ t1, [&]() {/*a block*/} },
{ t2, [&]() {/*another block*/ }},
{ t3, [&]() {/*yet another block*/ }}
};
lambda_map[myenum](); //to run the callback
我将运行lambda_map[myenum]();
而不是更难跟踪的切换功能。
但lambda-pointer映射是否可以将CPU从潜在的缓存未命中中解放出来?就像我可以收集的那样,不必预测if语句或运行switch case,而是根据set myenum
变量跳转到特定函数,应该完成,对吧?或者至少以某种方式影响它。
我很好奇lambda_map[enum]();
是否可以更好地进行分支预测并避免缓存未命中,而不是通过可能情况列表的switch语句。
答案 0 :(得分:3)
我将运行lambda_mapmyenum;而不是更难以跟踪的开关功能。
这可能是误导你的。交换机语句不一定非常难以跟踪&#34;比一些类似地图的结构。实际上,由于编译器优化,它很可能被实现为跳转表,即跳转指令数组,类似于您想象的第二个解决方案。
在你的情况下,使用std::map
几乎可以保证因为间接(以及与它们一起出现的缓存未命中)而变慢,并且因为它使用二进制搜索来查找值。你想要的是一个函数指针的数组(std::array
)(使用std::function
或lambdas),使用枚举数作为索引。如果数组不可行(由于键的性质),可以使用散列映射(std::unordered_map
)。
在性能方面,没有分析,没有办法告诉函数数组方法是否胜过编译器优化的开关。
话虽如此,我经常发现自己编写数组或散列图(函数与否)代替switch语句,因为个人偏好从样式(块嵌套,放置......)到实际可维护性。还因为我不喜欢switch语句的结构。最后一部分用一粒盐:我确信有些人喜欢类似开关的结构,并且鄙视替代品。