传递许多变量与传递struct

时间:2016-08-04 09:45:23

标签: c performance pointers struct

在向函数传递多个指针时,仅在性能方面: 最好将指针传递给一个包含所有指针的结构,然后执行类似代码1的操作或直接将指针传递给变量?

我认为对于少量变量,选项2肯定胜过选项1,但是当我们有100个变量时就是这种情况吗?

我发现了一些关于此的问题,但在性能方面没有一个非常有说服力。

What is better practise in high-performance computing: passing a struct of data into a function or a set of variables?

Efficiency of passing a struct to a function without instantiating a local variable

Declaring/passing structs vs. declaring/passing individual values

选项1

void myFunctionOne(void *pointerToStruct){
    struct myTypeOfStruct *localPointerToStruct;
    localPointerToStruct = (struct myTypeOfStruct *)pointerToStruct;

    for(i=0;i<n;++i){
        *(localPointerToStruct->a+i) = sth;
        *(localPointerToStruct->b+i) = sthElse;
        ...
        *(localPointerToStruct->z+i) = sthElsez;
    }
}

选项2

void myFunctionOne(double *a, double *b,...,double *z){
    for(i=0;i<n;++i){
        *(a+i) = sth;
        *(b+i) = sthElse;
        ...
        *(z+i) = sthElsex;
    }
}

选项3

可能看起来有点奇怪的第三个选项是第一种情况,但不是迭代结构内的指针,而是将它们复制到本地指针变量。

void myFunctionOne(void *pointerToStruct){
    struct myTypeOfStruct *localPointerToStruct;
    localPointerToStruct = (struct myTypeOfStruct *)pointerToStruct;

        double *a = localPointerToStruct->a;
        double *b = localPointerToStruct->b;
        ...
        double *z = localPointerToStruct->z;

    for(i=0;i<n;++i){
        *(a+i) = sth;
        *(b+i) = sthElse;
        ...
        *(z+i) = sthElsex;
    }
}

4 个答案:

答案 0 :(得分:2)

这取决于编译器做了很多背景优化和环境。一般情况下,不要试图超越编译器,但如果你真的,真的需要它做一个测试,每个选项有几百万个调用,当然在3个不同的程序中,并在每次测试后重新启动你的电脑后。然后,您知道在确切的设置确切时刻,它的价值更高。

答案 1 :(得分:2)

通常,使用struct来聚合属于一起的变量,并将指向该struct的指针传递给您的函数。这将是最快的方式。但当然在这种情况下,函数可以修改struct,这些修改将反映在调用者中。您可以使用const来消除此问题。

但总的来说,相信编译器可以为您进行优化,只有在代码区域被识别为瓶颈的情况下才会担心微优化。

答案 2 :(得分:2)

为了评估方法的整体效率,您需要考虑函数调用的方式,因为第一种方法的效率在很大程度上取决于struct的来源。

如果你必须在每次调用之前执行相同的struct设置,第一种方法与第二种方法相同,除非你现在负责为函数准备堆栈帧。

另一方面,如果您可以设置struct一次,然后使用它进行多次通话,则最终会减少复制。

为了完整起见,如果你必须动态分配你的结构,性能将会明显变差。

struct方法的一个重要优点是维护。如果您决定将另一个字段添加到struct,则只需向struct添加另一个字段,一切都将继续编译。但是,向函数添加额外参数会强制您重新访问进行调用的代码中的所有位置,并为新添加的参数添加新的参数表达式。

我会修改第一种方法来取代myTypeOfStruct而不是void*,因为没有必要隐藏参数类型。毕竟,替代方案直接传递double数组,没有void*强制转换。我也更喜欢数组语法到指针操作:

void myFunctionOne(struct myTypeOfStruct *pointerToStruct){
    for(i=0;i<n;++i){
        pointerToStruct->a[i] = sth;
        pointerToStruct->b[i] = sthElse;
        ...
        pointerToStruct->z[i] = sthElsez;
    }
}

答案 3 :(得分:1)

性能(参数和结果传递)取决于C实现使用的calling conventionsABI。 顺便说一句,优化的C编译器通常会inline一个函数调用(即使对于未声明为inline的函数,只要它知道被调用函数的定义)。一些编译器能够进行链接时优化(使用最近的GCC,编译并使用gcc -flto -O2链接

请注意,在Linux / x86-64上,ABI specification指示前6个标量参数通常由寄存器传递,并且两个标量的struct结果通过两个寄存器。这通常比通过内存快得多(例如在call stack上)。

最后,CPU cache对性能至关重要。因此,真正了解的唯一方法是对应用程序进行基准测试。