微优化:使用局部变量与 class 成员进行迭代

Microoptimization: iterating with local variable vs. class member

我想如果我将迭代变量声明一次作为 class 成员会节省一些时间:

struct Foo {
  int i;
  void method1() {
    for(i=0; i<A; ++i) ...
  }
  void method2() {
    for(i=0; i<B; ++i) ...
  }
} foo;

然而,这似乎快了 20%

struct Foo {
  void method1() {
    for(int i=0; i<A; ++i) ...
  }
  void method2() {
    for(int i=0; i<B; ++i) ...
  }
} foo;

在此代码中

void loop() { // Arduino loops
  foo.method1();
  foo.method2();
}

你能解释一下性能差异吗?

(我需要在 Arduino 上 运行 许多简单的并行 "processes",这种微优化会产生影响。)

当您在循环内声明循环变量时,它的范围非常狭窄。编译器可以随时将其保存在寄存器中,因此它甚至一次都不会提交到内存中。

当您将循环变量声明为实例变量时,编译器没有这种灵活性。它必须将变量保存在内存中,以防您的某些方法想要检查其状态。例如,如果您在第一个代码示例中这样做

void method2() {
    for(i=0; i<B; ++i) { method3(); }
}
void method3() {
    printf("%d\n", i);
}

method3i 的值必须随着循环的进行而改变。编译器无法将其所有副作用提交给内存。此外,当您从 method3 返回时,它不能假设 i 保持不变,进一步增加内存访问次数。

处理内存中的更新比对基于寄存器的变量执行更新需要更多 CPU 周期。这就是为什么将循环变量的范围缩小到循环级别总是一个好主意。

Can you explain the performance difference?

对于这种性能差异,我能想到的最合理的解释是:

数据成员 i是在全局内存上声明的,不能一直保存在寄存器中,所以对它的操作会比对循环变量 i 由于范围非常广泛(数据成员 i 必须满足 class 的所有成员函数)。

@DarioOO 补充道:

In addition the compiler is not free to store it temporary in a register because method3() could throw an exception leaving the object in a unwanted state (because theoretically no one prevent to you to write int k=this->i; for(k=0;k<A;k++)method3(); this->i=k;. That code would be almost as fast as local variable but you have to keep into account when method3() throws (I believe when there is the guarantee it does not throw the compiler will optimize that with -O3 or -O4 to be verified)