可以使用函数指针数组来删除分支吗

Could arrays of function pointers be used to remove branches

函数指针数组是否可以通过删除分支来提高性能?

(function[number])();

if(number == 0)
    function1();
else if (number == 1)
    function2();

为了表现? (假设这是对性能极其关键的代码)

编辑:数字是 0 或 1(因为它应该只是 0 或 1 而被使用)

假设您的 numberunsigned int 类型,您的数组必须包含 UINT_MAX 个条目,除了 function[0] 之外所有条目都相同。如果不是这种情况,您的第一个代码 (function[number])(); 将省略一个不是未定义行为的条件:(function[number != 0])();

总结一下:#1 是(正确书写的)一个条件和两个间接寻址。巨型数组的另一个选项将破坏缓存局部性。 #2 是一个条件和一个直接函数调用。

不太可能提供帮助。函数指针会导致与条件相同的停顿*。如今,两者都有预测器,但条件预测器的成功率更高。不足为奇,因为他们只需要预测一位。

* 停滞:指令处理中的延迟,由现代 CPU 中指令解码器的流水线特性引起。指令解码在实际执行之前开始几个周期,但在本质上有点推测未来将执行哪条指令。当这个推测错误时,必须丢弃一些解码指令,并且指令执行被延迟直到解码器赶上。

正如 MSalters 已经非常彻底地指出的那样,它不太可能提供帮助。此外,几乎无法预测它是否有帮助。我已经为您创建了两个示例,您可以在其中看到这一点——一个使用指针数组,另一个使用 if-else 子句。

Example 1

#include <array>
#include <functional>



int test1(int* i)
{
    return *i * *i  * *i;
}

int test2(int* i)
{
    return *i * *i* *i* *i* *i* *i;
}

std::array<int (*)(int*), 2> funcs{test1, test2};

int testing(int v)
{
    int testme = v + 4 ;
    return funcs[v](&testme);
}

及其组件

test1(int*):                             # @test1(int*)
    movl    (%rdi), %ecx
    movl    %ecx, %eax
    imull   %ecx, %eax
    imull   %ecx, %eax
    retq
test2(int*):                             # @test2(int*)
    movl    (%rdi), %ecx
    movl    %ecx, %eax
    imull   %ecx, %eax
    imull   %ecx, %eax
    imull   %eax, %eax
    retq
testing(int):                            # @testing(int)
    pushq   %rax
    leal    4(%rdi), %eax
    movl    %eax, 4(%rsp)
    movslq  %edi, %rax
    leaq    4(%rsp), %rdi
    callq   *funcs(,%rax,8)
    popq    %rcx
    retq
funcs:
    .quad   test1(int*)
    .quad   test2(int*)

这里是Example2

int test1(int* i)
{
    return *i * *i  * *i;
}

int test2(int* i)
{
    return *i * *i* *i* *i* *i* *i;
}


int testing(int v)
{
    int testme = v + 4 ;
    return (v == 0) ? test1(&testme) : test2(&testme);
}

与其对应的程序集

test1(int*):                             # @test1(int*)
    movl    (%rdi), %ecx
    movl    %ecx, %eax
    imull   %ecx, %eax
    imull   %ecx, %eax
    retq
test2(int*):                             # @test2(int*)
    movl    (%rdi), %ecx
    movl    %ecx, %eax
    imull   %ecx, %eax
    imull   %ecx, %eax
    imull   %eax, %eax
    retq
testing(int):                            # @testing(int)
    leal    4(%rdi), %eax
    movl    %eax, %ecx
    imull   %eax, %ecx
    imull   %eax, %ecx
    testl   %edi, %edi
    movl    , %eax
    cmovnel %ecx, %eax
    imull   %ecx, %eax
    retq

在指令计数方面它们非常相似(也许专家可以在这里帮助我们并进一步解释)。 if-else-statement 中断了这条指令 cmovnel。我怀疑这会产生很大的不同。

我认为您总是需要在您的确切设置中进行测量。在示例情况下,编译器优化总是难以预测。

这里不会有正确或错误的答案