提高 Fortran 代码性能的提示和技巧 [关闭]
Posted
技术标签:
【中文标题】提高 Fortran 代码性能的提示和技巧 [关闭]【英文标题】:Tips and tricks on improving Fortran code performance [closed] 【发布时间】:2011-10-15 18:15:38 【问题描述】:作为我博士学位的一部分。研究方面,我正在开发大气和海洋环流的数值模型。这些涉及在约 10^6 个网格点的数量级上以约 10^4 个时间步长对 PDE 系统进行数值求解。因此,在数十个 CPU 上以 MPI 运行时,典型的模型模拟需要数小时到数天才能完成。当然,尽可能提高模型效率很重要,同时确保结果字节与字节相同。
虽然我对我的 Fortran 编程感到很自在,并且知道一些使代码更高效的技巧,但我觉得仍有改进的空间,以及我不知道的技巧。
目前,我确保使用尽可能少的除法,并尽量不使用文字常量(我很早就被教导要这样做,例如在实际计算中使用 half=0.5 而不是 0.5),使用 as尽可能少的超越函数等。
还有哪些其他性能敏感因素?目前,我想知道一些:
1) 数学运算的顺序重要吗?例如,如果我有:
a=1E-7 ; b=2E4 ; c=3E13
d=a*b*c
会根据乘法的顺序以不同的效率进行评估吗?如今,这必须是特定于编译器的,但有直接的答案吗?我注意到 d 根据顺序(精度限制)获得(略微)不同的值,但这会影响效率吗?
2) 将大量(例如几十个)数组作为参数传递给子程序,而不是从子程序中的模块访问这些数组?
3) Fortran 95 构造(FORALL 和 WHERE)与 DO 和 IF?我知道这些在 90 年代很重要,当时代码向量化是一件大事,但是现在现代编译器能够向量化显式 DO 循环有什么不同吗? (我在工作中使用 PGI、Intel 和 IBM 编译器)
4) 将数字提高到整数幂与乘法?例如:
b=a**4
或
b=a*a*a*a
我被教导要尽可能使用后者。这会影响效率和/或精度吗? (可能也依赖于编译器)
请讨论和/或添加您知道的有关提高 Fortran 代码效率的任何技巧和提示。还有什么?如果您知道上述每个编译器与此问题相关的具体内容,请也包括在内。
添加:请注意,我本身没有任何瓶颈或性能问题。我在问是否有任何一般规则来优化操作意义上的代码。
谢谢!
【问题讨论】:
不可能列出所有代码中可能会变慢的事情的清单。正如其他人所提到的,对其进行概要分析。此外,您是否正在编写自己的 PDE 求解器?除非那是你研究的一部分,否则最好得到一个已经尽可能被欺骗的东西。现代编译器、分析器、数值库和内存技巧的现状是,最好熟悉这些并专注于关于仍然缓慢的具体问题,而不是仅仅寻找一份清单。 我经常看到这个。当您在代码中看到的是call dgemm('n','n', ...)
时,人们会问“优化器会不会这样做……”。只是假设那个窃听器必须尽可能优化,而实际上,对于合理大小的矩阵,它花费大部分时间调用一个函数来对这些字符标志进行分类。
@Iterator - 就我个人而言,我不会(除非在一些非常特殊的商业案例中;博士不是其中之一)甚至不会走上优化今天的 num 的道路。图书馆。一个人的时间可以得到更好的利用,而运行程序所节省的时间将是微不足道的。程序员的时间比机器的时间贵好几倍。
@Idigas:我们同意。不过,事实是这取决于您使用了多少台机器。 :)
【参考方案1】:
抱歉,您提到的所有技巧都只是......荒谬。更确切地说,它们在实践中没有任何意义。例如:
使用 half(=0.5) 而不是 0.5 有什么优势? 计算a**4
或a*a*a*a
的同上。 (a*a)** 2
也是另一种可能性。我个人的喜好是 **4,因为一个好的编译器会自动选择最佳方式。
对于**
,唯一重要的一点是a ** 4
和a ** 4.
之间的区别,后者需要更多的CPU 时间。但是,如果没有实际模拟中的测量,即使这一点也是没有意义的。
事实上,你的方法是错误的。尽可能地开发你的代码。之后,客观地衡量代码不同部分的成本。在没有测量之前进行优化是毫无意义的。
如果某个部分的 CPU 占用率很高,例如 50%,请不要忘记仅优化该部分不能将整个代码的成本除以两倍以上。无论如何,从最昂贵的部分(瓶颈)开始优化工作。
不要忘记,主要的改进通常来自更好的算法。
【讨论】:
感谢您的回答。我同意瓶颈和改进算法,但这是一个不同的问题,我们很高兴我们在哪里。具体代码几乎接近第一次公开发布,但我的问题与低级操作和编译器/处理器实现更相关。【参考方案2】:我赞同你所学的这些技巧在这个时代很愚蠢的建议。编译器现在为您执行此操作;这种微优化不太可能产生重大影响,并且可能不可移植。编写清晰易懂的代码。仔细选择你的算法。可以产生影响的一件事是以正确的顺序使用多维数组的索引...将 M X N 数组重铸为 N X M 可以根据程序的数据访问模式提供帮助。在此之后,如果您的程序太慢,请测量 CPU 消耗的位置并仅改进那些部分。经验表明,猜测经常是错误的,并导致编写更多不透明的代码。如果您创建一个代码部分,其中您的程序将 1% 的时间花费两倍的速度,它不会有任何区别。
这里是之前关于 FORALL 和 WHERE 的答案:How can I ensure that my Fortran FORALL construct is being parallelized? 和 Do Fortran 95 constructs such as WHERE, FORALL and SPREAD generally result in faster parallel code?
【讨论】:
即使在 70 年代,我也不记得其中一些重要的事情。不过同意答案;永远不要猜测哪里最优化。需要。 @M.S.B.谢谢,很有帮助。【参考方案3】:你对做什么有先验的想法,其中一些可能实际上会有所帮助,
但最大的回报是后验分析。
(已添加: 换句话说,将a*b*c
设置为不同的顺序可能会节省几个周期(我对此表示怀疑),同时您不知道自己没有失明-无缘无故地花费 1000 个周期。)
无论您多么仔细地编写代码,都会有您没有预见到的加速机会。这是我找到它们的方法。 (有些人认为this method 有争议)。
执行此操作时,最好先关闭优化标志,这样代码就不会全部乱码。 稍后您可以打开它们并让编译器完成它的工作。
让它在具有足够工作负载的调试器下运行,使其运行一段合理的时间。 在它运行时,手动中断它,仔细看看它在做什么以及为什么。 这样做几次,比如 10 次,这样你就不会得出关于它花费时间的错误结论。
以下是您可能会发现的示例:
由于某些表达式的编码方式或使用与之前调用相同的参数值,可能会花费大量时间不必要地调用数学库函数。 可能会花费大量时间进行文件 I/O 或打开/关闭文件,深入某个看似无害的例程调用。 它可以在通用库函数中,调用从属子例程,以检查上层函数的参数标志。在这种情况下,可以通过编写一个特殊用途的函数并调用它来消除大部分时间。如果您将整个操作执行两到三遍,那么您将删除那些在首次编写软件时就进入任何软件的愚蠢内容。 之后,您可以打开优化、并行性或其他任何东西,并确信没有时间花在愚蠢的事情上。
【讨论】:
谢谢,这很有帮助,会记住的。以上是关于提高 Fortran 代码性能的提示和技巧 [关闭]的主要内容,如果未能解决你的问题,请参考以下文章
提高 ASP.NET Web 应用性能的 24 种方法和技巧
提高 ASP.NET Web 应用性能的 24 种方法和技巧