Warning: file_get_contents(/data/phpspider/zhask/data//catemap/4/c/63.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C 通过指针访问相同的结构还是将结构存储在局部变量中?_C_Pointers_Struct_Benchmarking - Fatal编程技术网

C 通过指针访问相同的结构还是将结构存储在局部变量中?

C 通过指针访问相同的结构还是将结构存储在局部变量中?,c,pointers,struct,benchmarking,C,Pointers,Struct,Benchmarking,我有一些遗留代码需要理解,我偶然发现,在代码中,同一个结构经常被访问。如果我事先保存结构的内容,然后访问本地副本而不是通过指针访问,会有什么不同吗 我已经通过一个在线汇编程序比较了一些测试代码,看看它是否能优化代码。使用ARM64 gcc8.2完成此操作 变体A typedef struct STRUCT_D{ int myInt1IND; int myInt2IND; int myInt3IND;

我有一些遗留代码需要理解,我偶然发现,在代码中,同一个结构经常被访问。如果我事先保存结构的内容,然后访问本地副本而不是通过指针访问,会有什么不同吗

我已经通过一个在线汇编程序比较了一些测试代码,看看它是否能优化代码。使用ARM64 gcc8.2完成此操作

变体A

typedef  struct STRUCT_D{
    int             myInt1IND;
    int             myInt2IND;
    int             myInt3IND;
    int             myInt4IND;
    int             myInt5IND;
    int             myInt6IND;
    int             myInt7IND;
    int             myInt8IND;
    int             myInt9IND;
} STRUCT_D;

typedef  struct STRUCT_C{
     STRUCT_D             myStructInDIntINC;
} STRUCT_C;

typedef  struct STRUCT_B{
    STRUCT_C *             myPointerB;
} STRUCT_B;

typedef  struct STRUCT_A{
    STRUCT_B *             myPointerA;
} STRUCT_A;


int square(void) {
    struct STRUCT_C myStructC;
    struct STRUCT_B myStructB;
    struct STRUCT_A myStructA;
    struct STRUCT_A* startPointer;
    myStructC.myStructInDIntINC.myInt1IND = 55;
    myStructB.myPointerB = &myStructC;
    myStructA.myPointerA = &myStructB;
    startPointer = &myStructA;

    int myresult = 
    startPointer->myPointerA->myPointerB->myStructInDIntINC.myInt1IND + 
    startPointer->myPointerA->myPointerB->myStructInDIntINC.myInt2IND +
    startPointer->myPointerA->myPointerB->myStructInDIntINC.myInt3IND +
    startPointer->myPointerA->myPointerB->myStructInDIntINC.myInt4IND +
    startPointer->myPointerA->myPointerB->myStructInDIntINC.myInt5IND +
    startPointer->myPointerA->myPointerB->myStructInDIntINC.myInt6IND +
    startPointer->myPointerA->myPointerB->myStructInDIntINC.myInt7IND +
    startPointer->myPointerA->myPointerB->myStructInDIntINC.myInt8IND +
    startPointer->myPointerA->myPointerB->myStructInDIntINC.myInt9IND;

    return myresult;
}
变体B

typedef  struct STRUCT_D{
    int             myInt1IND;
    int             myInt2IND;
    int             myInt3IND;
    int             myInt4IND;
    int             myInt5IND;
    int             myInt6IND;
    int             myInt7IND;
    int             myInt8IND;
    int             myInt9IND;
} STRUCT_D;

typedef  struct STRUCT_C{
     STRUCT_D             myStructInDIntINC;
} STRUCT_C;

typedef  struct STRUCT_B{
    STRUCT_C *             myPointerB;
} STRUCT_B;

typedef  struct STRUCT_A{
    STRUCT_B *             myPointerA;
} STRUCT_A;


int square(void) {
    struct STRUCT_C myStructC;
    struct STRUCT_B myStructB;
    struct STRUCT_A myStructA;
    struct STRUCT_A* startPointer;
    myStructC.myStructInDIntINC.myInt1IND = 55;
    myStructB.myPointerB = &myStructC;
    myStructA.myPointerA = &myStructB;
    startPointer = &myStructA;

    struct STRUCT_D myResultStruct =     startPointer->myPointerA->myPointerB->myStructInDIntINC;
    int myresult = 
    myResultStruct.myInt1IND + myResultStruct.myInt2IND +     myResultStruct.myInt3IND + 
    myResultStruct.myInt4IND + myResultStruct.myInt5IND +     myResultStruct.myInt6IND +
    myResultStruct.myInt7IND + myResultStruct.myInt8IND +     myResultStruct.myInt9IND;

    return myresult;
}

我知道STRUCT_D没有完全初始化,但对于本例来说并不相关。我的问题是变体B是否“更好”。当然,它的可读性更好,但保存指针的上下文是否有意义。正如我在文件中所说,同一个指针在同一个函数中被取消引用了大约150次。我知道我知道。。此函数必须进行重构:没有真正的区别,因为任何优化编译器(gcc,clang)都会将其优化为堆栈变量和/或寄存器。

没有真正的区别,因为任何优化编译器(gcc,clang)将其优化为堆栈变量和/或寄存器。

将数据复制到本地有助于编译器证明没有其他访问通过其他指针读取或写入数据

因此,基本上出于相同的原因,您将使用
int*restrict p
。如果使用
void func(struct foo*restrict ptr)
,则向编译器保证对
ptr->member
的任何访问都不会更改通过任何其他指针或全局范围变量读取的值

基于类型的别名分析已经有了很大的帮助;例如,通过
float*
进行访问不能影响任何
int
对象。(除非您的程序包含严格别名;某些编译器允许您定义该行为,例如,
gcc-fno严格别名

如果您没有执行赋值或读取其他指针(编译器必须假设这些指针可能指向结构的一个成员),这不会有什么区别:别名分析将成功,并让编译器在对内存的其他访问中将结构成员保留在寄存器中,就像对本地对象一样

(对当地人来说,别名分析通常很容易,特别是如果他们的地址从未被人记录过,那么就没有任何东西可以指向他们。)


顺便说一句,允许编译器优化非原子的易失性/non-
\u原子的
内存访问的原因是,在另一个线程读取或写入非原子对象的同时写入非原子对象是一种未定义的行为

这使得我们可以安全地假设,除非您自己编写变量,否则变量不会更改,并且不需要内存中的值与C抽象机“同步”,除非您进行非内联函数调用。(对于某些未知函数可能有指针指向的任何对象。本地变量(如循环计数器)通常不是这种情况,因此它们可以保存在保留调用的寄存器中,而不是溢出/重新加载。)


但是声明局部变量来保存全局变量或指向数据的副本有一个潜在的缺点:如果编译器最终没有将该局部变量保存在整个函数的寄存器中,那么它可能最终不得不将数据复制到堆栈内存中,以便从那里重新读取。(如果无法证明原始对象未更改。)



通常只支持可读性而不是这一级别的微优化,但是如果你好奇的话,可以看看你关心的某个平台的优化asm。如果发生了大量不必要的存储/重新加载,请尝试使用局部变量。

将数据复制到局部变量有助于编译器证明没有其他访问通过其他指针读取或写入数据

因此,基本上出于相同的原因,您将使用
int*restrict p
。如果使用
void func(struct foo*restrict ptr)
,则向编译器保证对
ptr->member
的任何访问都不会更改通过任何其他指针或全局范围变量读取的值

基于类型的别名分析已经有了很大的帮助;例如,通过
float*
进行访问不能影响任何
int
对象。(除非您的程序包含严格别名;某些编译器允许您定义该行为,例如,
gcc-fno严格别名

如果您没有执行赋值或读取其他指针(编译器必须假设这些指针可能指向结构的一个成员),这不会有什么区别:别名分析将成功,并让编译器在对内存的其他访问中将结构成员保留在寄存器中,就像对本地对象一样

(对当地人来说,别名分析通常很容易,特别是如果他们的地址从未被人记录过,那么就没有任何东西可以指向他们。)


顺便说一句,允许编译器优化非原子的易失性/non-
\u原子的
内存访问的原因是,在另一个线程读取或写入非原子对象的同时写入非原子对象是一种未定义的行为

这使得我们可以安全地假设,除非您自己编写变量,否则变量不会更改,并且不需要内存中的值与C抽象机“同步”,除非您进行非内联函数调用。(对于某些未知函数可能有指针指向的任何对象。本地变量(如循环计数器)通常不是这种情况,因此它们可以保存在保留调用的寄存器中,而不是溢出/重新加载。)


但是声明局部变量来保存全局变量或指向数据的副本有一个潜在的缺点:如果编译器最终没有将该局部变量保存在整个函数的寄存器中,那么它可能最终不得不将数据复制到堆栈内存中,以便从那里重新读取