在 C 中访问结构成员的速度有多快?

问题描述

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

解决方法

没有理由为什么结构成员访问会比普通变量慢。

可能有一些特殊情况例外,例如当您有一个指向 struct 的指针时,编译器必须通过该指针来获取数据。然后你会得到间接访问,这通常比直接访问慢一点。但是,在编写代码时,您不必担心像这样的微优化。

一般来说,专注于编写可读的代码,你通常会同时获得快速的代码。初学者试图手动优化代码,从而将其变成一团糟,这是一个常见的问题。手动代码优化是一项非常合格的工作,在您拥有 10 年以上的编程经验之前,您不应担心。

,

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

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

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->a,但 bar 不需要。即使 zconst 限定,​​对 x 的赋值可能会改变它:

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,const struct S *z)
{
    int a = z->a;
    x->a = a;
    y->a = a;
}