使用函数指针比 switch 语句慢

Using function pointers is slower than switch statement

我有这段代码用于测试 switch 语句与函数指针的行为:

#include <stdio.h>
#include <time.h>


int mySum(int startingValue)
{
    return startingValue += 1;
}

int mySub(int startingValue)
{
    return startingValue -= 1;
}


int main()
{
    time_t rawtime;
    struct tm * timeinfo;

    time(&rawtime);
    timeinfo = localtime(&rawtime);
    printf("USING SWITCH\n");
    printf("Start time: %s", asctime(timeinfo));

    int startingValue = 25;
    int currentOperation = 0;

    // Using switch
    for (long long i = 0; i < 10000000000; i++)
    {
        switch (currentOperation)
        {
            case 1:
                startingValue = mySum(startingValue);
                break;
            case 0:
                startingValue = mySub(startingValue);
                break;
        }

        if (currentOperation)
            currentOperation = 0;
        else
            currentOperation = 1;
    }

    printf("Result is: %d\n", startingValue);

    time(&rawtime);
    timeinfo = localtime(&rawtime);
    printf("End time: %s", asctime(timeinfo));

    printf("\n\n\n");

    time(&rawtime);
    timeinfo = localtime(&rawtime);
    printf("USING FUNCTION POINTERS\n");
    printf("Start time: %s", asctime(timeinfo));

    startingValue = 25;
    currentOperation = 0;

    // Using function pointers
    int (*mySwitchOfFunctionPointers[2])(int x);
    mySwitchOfFunctionPointers[0] = &mySub;
    mySwitchOfFunctionPointers[1] = &mySum;
    for (long long i = 0; i < 10000000000; i++)
    {
        startingValue = (*mySwitchOfFunctionPointers[currentOperation])(startingValue);

        if (currentOperation)
            currentOperation = 0;
        else
            currentOperation = 1;
    }

    printf("Result is: %d\n", startingValue);

    time(&rawtime);
    timeinfo = localtime(&rawtime);
    printf("End time: %s", asctime(timeinfo));

    return 0;
}

输出是这样的,所以代码使用 switch 语句立即执行,但使用函数指针需要很多秒:

USING SWITCH
Start time: Mon Oct 25 18:04:06 2021
Result is: 25
End time: Mon Oct 25 18:04:06 2021



USING FUNCTION POINTERS
Start time: Mon Oct 25 18:04:06 2021
Result is: 25
End time: Mon Oct 25 18:04:34 2021

编译为

gcc -c main.c -o test.o -O3 -Wall -Wno-unused -std=c99
gcc test.o -o test

为什么第二种方法比第一种慢???代码有问题吗?有什么想法吗?

编译器可以内联开关,然后优化器足够聪明,可以意识到您的整个循环是空操作,因此将其优化掉。 它不会对函数指针执行此操作。 参见 https://godbolt.org/z/qxxoof5cn

如果您将代码更改为编译器无法优化的代码,那么您将获得截然不同的结果。例如:

int mySum(int startingValue)
{
    return startingValue += rand();
}

int mySub(int startingValue)
{
    return startingValue -= rand();
}

编译器优化参数 (-O3) 是造成此行为的原因。

编译器可以优化第一个 for-Loop,里面有 switch,因为它有关于这段代码的所有需要​​的信息。

不幸的是,编译器无法优化带有函数指针的代码。由于编译器不知道调用了哪些函数。

如果你在编译时删除-O3参数,你会发现,函数指针和switch-case语句需要相似的时间。