这种方法是否会对分支预测产生积极影响?

Does this approach positvely impact branch prediction?

我一直在尝试制作一种回调函数,在这个过程中我意识到我更喜欢的那个,也可能会改变 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 指针映射是否可以避免 CPU 潜在的缓存未命中?据我所知,不必通过 switch case 预测 if 语句或 运行,而是根据设置的 myenum 变量跳转到特定函数,应该可以做到这一点,对吧?或者至少以某种方式影响它。

我很好奇 lambda_map[enum](); 在分支预测和避免缓存未命中方面是否比通过可能情况列表的 switch 语句更好。

Where I will run the lambda_mapmyenum; instead of the harder-to-track switch function.

这可能是误导你的原因。开关 语句 不一定比某些 map-like 结构 "harder to track"。事实上,由于编译器优化,它很可能实现为跳转 table,这是一个跳转指令数组,类似于您对第二个解决方案的想象。

在你的情况下,使用 std::map 几乎肯定会变慢,因为间接寻址(以及随之而来的缓存未命中),并且因为它使用二进制搜索来查找值。您想要的是一个函数指针数组 (std::array)(使用 std::function 或 lambda),使用枚举数作为索引。在数组不可行的情况下(由于键的性质),可以使用哈希图 (std::unordered_map) 代替。

Performance-wise,没有分析,无法判断 array-of-functions 方法是否击败了 compiler-optimized 开关。

话虽如此,由于从样式(块嵌套、放置...)到实际可维护性等个人喜好,我经常发现自己编写数组或哈希图(函数或非函数)来代替 switch 语句。也因为我不喜欢 switch 语句的结构。对最后一部分持保留态度:我敢肯定有些人更喜欢 switch-like 结构并鄙视替代方案。