如何优化 CFD C 代码中的决策

How to optimize a decision in a CFD C code

我想优化 CFD 代码中不同功能的使用,用户可以在运行时通过程序读取的配置文件选择这些功能。

我想出了一个最小的工作示例,其中有两个单独的函数和一个输入。一种将输入平方,一种将其立方。通过命令行选项,用户可以选择要使用的功能。 squares/cubes 一堆数字的代码(它计算 x^2 或 x^3 从 0 到 1 的积分,具体取决于选择的函数)在 for 循环中并输出结果。第一个变体只是 for 循环 (case1) 中的一个 switch case。我尝试的第二件事是一个函数指针,它被设置在循环之前 (case2)。我做的第三件事是有选择地只编译用户打算使用预处理器命令(案例 3)使用的函数。

案例 1:

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

double f_square(double x) {return x * x;}

double f_cube(double x) {return x * x * x;}

int main(int argc, char *argv[])
{
    double x;
    double sum = 0;
    double del_x = 4e-10;

    printf("Speed test -- no optimisation\n");

    clock_t startClock = clock();
    for (x = 0; x < 1; x += del_x) {
        switch (argv[1][0]) {
        case '2':
            sum += f_square(x) * del_x;
            break;
        case '3':
            sum += f_cube(x) * del_x;
            break;
        default:
            printf("Invalid choice! Abort\n");
            exit(1);
        }
    }
    clock_t endClock = clock();

    printf("Int_{0}^{1} x^%c: %.8g\n", argv[1][0], sum);
    printf("Execution time: %.6f\n", (endClock - startClock) / (double)CLOCKS_PER_SEC);
}

案例2:

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

double f_square(double x) {return x * x;}

double f_cube(double x) {return x * x * x;}

int main(int argc, char *argv[])
{
    double x;
    double sum = 0;
    double del_x = 4e-10;
    double (*f)(double);

    printf("Speed test -- function pointers\n");

    switch (argv[1][0]) {
    case '2':
        f = &f_square;
        break;
    case '3':
        f = &f_cube;
        break;
    default:
        printf("Invalid choice! Abort\n");
        exit(1);
    }

    clock_t startClock = clock();
    for (x = 0; x < 1; x += del_x) {
        sum += f(x) * del_x;
    }
    clock_t endClock = clock();

    printf("Int_{0}^{1} x^%c: %.8g\n", argv[1][0], sum);
    printf("Execution time: %.6f\n", (endClock - startClock) / (double)CLOCKS_PER_SEC);
}

案例 3:

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

#ifdef SQUARE
double f(double x) {return x * x;}
#endif

#ifdef CUBE
double f(double x) {return x * x * x;}
#endif

int main(void)
{
    double x;
    double sum = 0;
    double del_x = 4e-10;

    printf("Speed test -- selective compilation\n");

    clock_t startClock = clock();
    for (x = 0; x < 1; x += del_x) {
        sum += f(x) * del_x;
    }
    clock_t endClock = clock();

    #ifdef SQUARE
    printf("Int_{0}^{1} x^2: %.8g\n", sum);
    #endif
    #ifdef CUBE
    printf("Int_{0}^{1} x^3: %.8g\n", sum);
    #endif
    printf("Execution time: %.6f\n", (endClock - startClock) / (double)CLOCKS_PER_SEC);
}

在测量执行时间时我发现了一些奇怪的事情:

这里有一些比较执行时间的图片

这让我很困惑,我想知道我可以做些什么才能在不损失性能的情况下使用函数指针,因为出于灵活性原因,我真的很想使用函数指针。

=> 为什么函数指针这么慢?

我想补充一点,我不是软件工程师,而是航空航天工程专业的学生,​​遗憾的是我们没有太多的编程课程, 所以每个细节都可能有所帮助。

下面是类似功能的两个实现的反汇编视图:https://c.godbolt.org/z/l24Zhl

请注意,使用 -O2,第一个方法内联对 f_cubef_square 的调用(注意没有调用程序集中的函数),但第二个版本没有。

第一个版本很可能会进一步 sped-up 由于处理器上的 Branch Prediction

您是否分析过您的代码并发现此区域是瓶颈?请记住,首先优化 most-used 代码可以最大程度地提高速度。记住:先做起来,再做起来快。