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

Microoptimization: iterating with local variable vs. class member

本文关键字:成员 class 迭代 局部变量 优化      更新时间:2023-10-16

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

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上运行许多简单的平行"过程",其中这种微优化会有所不同。

当你在循环中声明循环变量时,它的作用域非常窄。编译器可以自由地始终将其保存在寄存器中,因此它甚至一次都不会提交到内存中。

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

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

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

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

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

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

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

@DarioOO补充说:

此外,编译器不能自由地将其临时存储在 注册,因为method3()可能会引发异常离开对象 处于不需要的状态(因为理论上没有人阻止您 写 int k=this->i; for(k=0;k<A;k++)method3(); this->i=k; .该代码 几乎和局部变量一样快,但你必须保持 帐户当method3()抛出时(我相信当有保证时 不抛出编译器将优化它与-O3-O4 已验证)