首页 > 解决方案 > 访问 C 中的结构成员有多快?

问题描述

访问结构成员的过程是否比访问直接变量慢?如果我在多个地方使用了相同的结构成员,我应该在变量中声明它并将其保存在那里以节省多次访问同一成员的成本吗?我应该重复Struct.Member Struct.Member Struct.Member还是应该将其保存在变量中int Member = Struct.Member Member Member Member?在什么时候我应该使用后者而不是前者?如果这些成员是位域怎么办?

标签: cperformancestructmicro-optimization

解决方案


虽然访问结构成员与分配“直接”对象存在一些复杂性,但您的场景中更重要的区别是编译器可能能够更好地优化局部变量的使用而不是结构成员的使用。通过了。

这是因为结构通常是通过指针(即参数或来自另一个结构的指针)间接传递的。考虑这段代码:

struct S { int a; };

void baz(int);

void foo(struct S *s)
{
    baz(s->a);
    baz(s->a);
}

void bar(struct S *s)
{
    int a = s->a;
    baz(a);
    baz(a);
}

bar中,编译器知道baz不能改变a,因为a是一个永远不会占用地址的局部变量。因此,编译器可以s->a从内存中加载一次并将其传递给baz两次。

foo中,编译器无法知道baz没有改变s->a,因为baz可能有某种访问方式,s->a例如通过存储在外部对象中的地址。因此,它必须s->a在第一次调用之前baz以及在第一次调用和第二次调用之间再次加载。

使用启用了优化的 Apple Clang 11.0.0 进行测试证实了这一点;为例程生成的汇编代码foo包含两个s->a来自内存的加载,而代码bar只包含一个。

尽管这经常发生在结构中,因为结构通常是通过地址传递的,但这并不是作为结构成员的固有结果。此代码中出现相同的问题:

void baz(int);

void foo(int *p)
{
    baz(*p);
    baz(*p);
}

void bar(int *p)
{
    int a = *p;
    baz(a);
    baz(a);
}

即使没有调用子程序,它也可能发生;参数可以“干扰”其他参数。在下面的代码中,foo必须重新加载z->abar不需要。即z使用 限定const,分配 tox也可能会改变它:

struct S { int a; };

void foo(struct S *x, struct S *y, const struct S *z)
{
    x->a = z->a;
    y->a = z->a;
}

void bar(struct S *x, struct S *y, const struct S *z)
{
    int a = z->a;
    x->a = a;
    y->a = a;
}

推荐阅读