openlang

running_mean 很慢,时间花在 memmove 而不是算术运算上

平滑传感器数据。数组是 readings(nstation, ntime),站点在前,因为数据文件就是这样排布的,其他工具也要读。对站点做一层循环,每个站点调用一次 running_mean:

do i = 1, nstation
  call running_mean(readings(i, :), ntime, window, smooth(i, :))
end do

模块里是这样:

subroutine running_mean(x, n, w, y)
  integer, intent(in) :: n, w
  real(dp), intent(in) :: x(n)
  real(dp), intent(out) :: y(n)

规模是 1500 个站点,20000 个采样点,窗口 32。大约耗时 1.9 s,Release,-O2,gfortran 来自 Homebrew,Apple silicon。每个采样点的计算量很小,所以这个速度太慢了。

我复制了一份程序,把数组改成 (ntime, nstation),其他都没动。结果快了好几倍。

性能分析器显示大部分时间花在类似 memmove/memcpy 的调用上,而不是 running_mean 里。我想不通。running_mean 里的循环是按 k 顺序走的,外面只有一层循环。我试过改 -O2 和 CMake 的 profile,没有效果。内存占用看起来也比我预期的高,不过我没测过。

时间到底花在哪了?我能保留 (nstation, ntime) 的布局吗?

从英语翻译查看原文

后续讨论 1

顺着彼此的依据读下去
  1. 固定链接

    是拷贝的问题。readings(i, :) 是带步长的,而显式形状的 x(n) 要求内存连续,所以 gfortran 每次调用都会打包一个临时数组,调用结束后还要把 y 拷回去。试试假定形状的 x(:)、y(:)。用 -Warray-temporaries 可以看到。

    从英语翻译查看原文

同一个问题的其他角度

  1. 简单循环非常慢,gfortran,200000 x 64 数组

    一个小型标定工具,代码在下面。输出是对的,但太慢了。detrend+clip 要好几秒,rms 也比该有的速度慢,每个元素只有一次乘法和一次加法。我试过 -O3 和 -march=native,没什么变化。怀疑是 Apple silicon 上的 s…

    回复 2最近活动 fortran
  2. 你说的是哪个构建时间?

    每个讨论拆 crate 的帖子,最后都会有人甩出一个构建时间。其实有两个,而且变化方向不一样。 干净构建指的是从空的 target 开始的全部过程。拆分可能会让它稍微变差,因为每个 crate 都带着自己的元数据,链接器还是得看到全部内容。编辑循环指…

    回复 2最近活动 rust
  3. 这个队列需要缓存个数吗?

    调用方对这个队列问的只有一件事,就是 List.length items > 8,而且问得很频繁。这个队列就是一个不可变的 OCaml 列表,每次更新都返回一个新队列。现在有两个选项。带一个精确的个数,或者暴露一个阈值查询。 用 (items, co…

    回复 2最近活动 ocaml