【问题标题】:Fortran implied do write speedupFortran 隐含写加速
【发布时间】:2016-02-09 21:06:44
【问题描述】:

tl;dr:我发现在某些情况下“隐式执行”写入比显式写入慢,并且想了解为什么/如果我可以改进这一点。

详情
我有一个代码可以起到以下作用:

DO i=1,n
  calculations...

  !m, x, and y all change each pass through the loop
  IF(m.GT.1)THEN   
    DO j=1,m
      WRITE(10,*)x(j),y(j)  !where 10 is an output file
    ENDDO
  ENDIF
ENDDO

输出文件最终变得相当大,所以看起来写作是一个很大的性能因素,所以我想优化它。在任何人问之前,不,由于各种下游要求,远离 ASCII 不是一种选择。因此,我将IF 语句(和内容)重写为:

IF(m.GT.1)THEN
  !build format statement for write
  WRITE(mm1,*)m-1
  mm1=ADJUSTL(mm1)
  !implied do write statement
  WRITE(10,'('//TRIM(mm1)//'(i9,1x,f7.5/),i9,1x,f7.5)')(x(j),y(j),j=1,m)
ELSEIF(m.EQ.1)THEN
  WRITE(10,'(i9,1x,f7.5)')x(1),y(1)
ENDIF

这会根据要写入的值 # 构建格式语句,然后执行单个写入语句来输出内容。我发现使用此公式的代码实际上运行速度较慢。作为参考,当要写入的数据量固定时,我已经看到在同一系统(硬件和软件)上执行隐含的 do write 语句时的显着加速。假设 WRITE 语句本身更快,那么这将意味着构建该语句的几行的开销是需要额外时间的,但这似乎很难相信。作为参考,m 可以变化很大,但可能平均至少为 1000。字符串的串联// 是一个非常慢的运算符,还是我还缺少其他东西?提前致谢。

【问题讨论】:

  • 如果你只使用write(10,'(i9,f8.5)') (...) 并依赖格式转换呢?
  • Ore 使用 * 作为乘数,或者像 999999 这样的大数。
  • 实际的输出格式有更多的值,这样* 可能会导致不需要的换行。但是,我不知道 francescalus 的不指定方法或 VladimirF 的仅使用始终大于 m 的数字是有效的选项。我会试用并更新。
  • @TTT,我相信 Vladimir F 对 * 的使用是作为无限重复计数说明符(参见 F2008)而不是列表导向输出。所以,你会得到write(10,'(*(i9,1x,f7,5,:,/))')(我建议的冒号)。
  • @francescalus 啊,我误解了,虽然我也没有注意到那个选项。这与您提供write(10,'(i9,f8.5)') (...) 的想法有什么区别吗?也许如果指定了ADVANCE='NO'

标签: performance io fortran


【解决方案1】:

我没有要添加的具体时间信息,但是使用隐含的 do 循环进行数据传输是不必要的复杂。

在第一个片段中,通过显式循环,您将每对数字写入不同的记录,并且您希望使用隐含的 do 循环重复此输出。为此,您使用斜线编辑描述符在写入一对记录后终止每条记录。

不必要的复杂性来自两个方面:

  • 您有一对/多对不同的情况;
  • 对于不止一种情况,您可以构建一个包含“动态”重复计数的格式。

作为Vladimir F comments,您可以只使用非常大的重复计数:当没有更多要写入的项目时处理编辑描述符并没有错误。当到达这样一个不匹配的描述符时,输出终止(成功)。那么,你可以写

 WRITE(10,'(*(i9,1x,f7.5/))') (x(j),y(j),j=1,m)  ! * replacing a large count

而不是 if 构造和格式创建。

现在,这与您的第一个输出不太匹配。正如我上面提到的,当没有相应的项目输出时到达数据编辑描述符时,输出终止发生。这意味着 / 将在此之前被处理:你有一个最终的空记录。

冒号编辑描述符在这里很有用:

 WRITE(10,'(*(i9,1x,f7.5,:,/))') (x(j),y(j),j=1,m)

在到达: 时,如果没有剩余的输出项要处理,处理将立即停止。

但我更喜欢的方法要简单得多

 WRITE(10,'(i9,1x,f7.5)') (x(j),y(j),j=1,m) ! No repeat count

您有更详细的格式来​​包括记录终止。但是,我们有所谓的格式反转:如果到达格式结尾并且还有更多内容要输出,则记录将终止并且处理返回到格式的开头。

这些东西是否能让你的输出更快还有待观察,但它们肯定会让代码本身更加干净和清晰。

最后一点,避免额外的X 编辑过去很流行。如果您的数字适合宽度为 7 的字段,则 1x,f7.5 可以替换为 f8.5 并具有相同的外观:表示在字段中是右对齐的。据称,这种减少在减少描述符切换的情况下具有性能优势。

【讨论】:

  • 我认为你有一个错字。当我执行WRITE(10,'(i9,1x,f7.5/)') 时,它会在每一行输出之间添加一个额外的空白行,包括最后一行。但是没有斜线WRITE(10,'(i9,1x,f7.5)'),它工作得很好。
  • 关于加速的详细信息:该文件为 114 GB,我在一个相当高效的 NAS 上运行,该 NAS 面向大文件。您推荐的方法WRITE(10,'(i9,1x,f7.5)') (x(j),y(j),j=1,m) 将总运行时间缩短了 52 分钟。我没有尝试确定仅用于输出文件的运行时间有多少,但根据我对代码性能的了解,这相当于该部分代码至少有 100% 的加速,可能会提高到 ~500%。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2014-06-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多