声明变量替换一个只计算一次

计算科学 正则 表现 数值建模 效率
2021-12-23 23:52:25

问题的框架

我目前正在为海洋/循环模型编辑一个附加模块,该模型是用 Fortran 编写的。主模型的代码在较短的运行时间(而不是代码的可读性)方面进行了相当优化。附加模块中的一些代码部分似乎没有被优化——至少从我的角度来看是这样。

问题

在三种情况下,引入了一个临时变量来替代某些计算,如下所示:

! comment
a2 = a * a
b2 = b * b

! comment
c = a / (a2 + b2)

! comment
f = c * d

如果(1)我想让代码更好地可读或(2)a * a在代码中被替换几次,这对我来说似乎是合理的。然而,(1) 和 (2) 并非如此:a2并且b2只需要一次。为了减少运行时间,这个公式似乎更好:

f = d * a / (a * a + b * b)

计算步骤的数量保持不变。删除了三个变量的声明。我们在代码中松散了一些解释。

我认为在以前版本的代码a * a中多次出现,这就是为什么它被a2. 但是,它没有被删除。

问题

  • 关于“最佳”运行时间保持代码在第一个代码剪切中发布的一个很好的理由吗?
  • 最先进的编译器(即 ifort)是否会检测到它并删除不必要的变量?因此,两种配方都没有区别吗?
1个回答

常见的子表达式消除和内联代码就像编译器优化的面包和黄油,编译器可以进行的第一个也是最简单的优化。完全有理由认为代码的性能将完全相同(同时丢失注释),并且没有特别的理由认为编译器在执行这种代码转换时会遇到麻烦。这正是应该由编译器决定的事情。如果评论有意义,则更是如此。

删除了三个变量的声明。

这是错误的:由于代码将被编译为汇编,并且变量和子表达式都将存储在编译器想要使用的任何寄存器中,因此没有理由认为这会影响性能。有像 godbolt.org(用于 C++;示例)这样的网站,您可以在其中看到代码在编译后的样子:变量-子表达式的区别在性能方面基本上没有意义。

最后,不幸的是,优化往往是一门艺术,尤其是现在优化编译器功能强大且广泛可用——我不知道推荐什么读物。根据我的理解,您所描述的确切内容永远不会有任何帮助。因此,既然您无论如何都想优化代码,那么您首先需要的不是要尝试的优化列表,而是检查给定优化是否有效的方法。我不具体了解 Fortran,但第一步始终是获得一个好的分析器,它可以告诉您代码的哪些部分实际上是昂贵的,并且您可以使用它来衡量修改后代码的性能。花很多时间没有收获太容易了。

第二个主要观察是著名的原则,即最有用的优化是那些避免工作的优化,而不是那些做同样工作但速度更快的优化。

如果你真的最终做了这些微优化,那么总是从消除内存访问开始,并确保内存访问模式对缓存友好。编译器通常无法自行处理这些类型的事情。