c++ - 编译器分支预测 - 间接分支预测




分支预测和分支目标预测优化 (2)

你可以尝试这样的事情:

switch(s & 7) {
case 0:
    /* _process_mem_64 */
    break;
case 1:
case 3:
case 5:
case 7:
    /* _process_mem_8 */
    break;
case 2:
case 6:
    /* _process_mem_16 */
    break;
case 4:
    /* _process_mem_32 */
    break;
}

这只涉及跳转到一个跳转表,并不需要调用指令。

我的代码经常调用具有多个(不可预知的)分支的函数。 当我分析时,我发现它是一个小瓶颈,在有条件的JMP使用大部分CPU时间。

考虑以下两个功能,其中原始有多个显式分支。

void branch_example_original(void* mem, size_t s)
{
    if(!(s & 7)) {
        /* logic in _process_mem_64 inlined */
    }
    else if(!(s & 3)) {
        /* logic in _process_mem_32 inlined */
    }
    else if(!(s & 1)) {
        /* logic in _process_mem_16 inlined */
    }
    else {
        /* logic in _process_mem_8 inlined */
    }
}

这里是新的功能,我试图去除导致瓶颈的分支。

void branch_example_new(void* mem, size_t s)
{
    const fprocess_mem mem_funcs[] = {_process_mem_8, _process_mem_16, _process_mem_32, _process_mem_64};
    const uint32_t magic = 3 - !!(s & 7) - !!(s & 3) - !!(s & 1);
    mem_funcs[magic](mem, size >> magic);
}

但是,当我分析新代码时,性能仅增加了20%,而CALL本身(对mem_funcs数组中的func)花费了很长时间。

第二个变体是一个更隐含的条件,因为CPU仍然不能预测将被调用的函数? 我是否正确地认为这与分支目标预测有关?

为什么会发生这种情况,还有其他解决方案吗?

编辑:

感谢您的想法,但我想解释为什么会发生这种情况。


现代处理器不仅具有分支预测功能,而且具有跳转预测功能。 例如,如果你调用一个虚拟函数,它可以预测实际的函数和前面的调用一样,并且在实际读取函数的指针之前开始执行 - 如果跳转预测是错误的,事情会变得很慢。

同样的事情发生在你的代码中。 您不再使用分支预测,但是处理器使用跳转预测来预测四个函数指针中的哪一个被调用,并且当函数指针是不可预知的时,这会变慢。





branch-prediction