openlang

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

一个小型标定工具,代码在下面。输出是对的,但太慢了。detrend+clip 要好几秒,rms 也比该有的速度慢,每个元素只有一次乘法和一次加法。我试过 -O3 和 -march=native,没什么变化。怀疑是 Apple silicon 上的 sum 和 real(dp)。这两个慢的阶段是同一个问题还是两个问题?我不明白为什么开销这么大。

Makefile

FC = gfortran
FFLAGS = -O3 -march=native -g

sensor_cal: src/sensor_mod.f90 src/main.f90
	$(FC) $(FFLAGS) -o $@ $^

clean:
	rm -f sensor_cal *.mod

src/sensor_mod.f90

module sensor_mod
  implicit none
  integer, parameter :: dp = kind(1.0d0)
contains

  ! remove the mean of one record (all channels at one time step)
  subroutine detrend_record(v, n)
    integer, intent(in) :: n
    real(dp), intent(inout) :: v(n)
    real(dp) :: m
    m = sum(v) / n
    v = v - m
  end subroutine detrend_record

  subroutine clip_record(v, n, lim)
    integer, intent(in) :: n
    real(dp), intent(inout) :: v(n)
    real(dp), intent(in) :: lim
    integer :: k
    do k = 1, n
      if (v(k) > lim) v(k) = lim
      if (v(k) < -lim) v(k) = -lim
    end do
  end subroutine clip_record

  function channel_rms(a, nrec, nchan) result(r)
    integer, intent(in) :: nrec, nchan
    real(dp), intent(in) :: a(nrec, nchan)
    real(dp) :: r(nchan)
    integer :: i, j
    r = 0.0_dp
    do i = 1, nrec
      do j = 1, nchan
        r(j) = r(j) + a(i, j)**2
      end do
    end do
    r = sqrt(r / nrec)
  end function channel_rms

end module sensor_mod

src/main.f90

program sensor_cal
  use sensor_mod
  implicit none
  integer :: nrec, nchan, i, t0, t1, rate
  real(dp), allocatable :: readings(:,:), rms(:)
  character(len=32) :: arg
  logical :: verbose

  verbose = .false.
  nrec = 200000
  nchan = 64

  if (command_argument_count() >= 1) then
    call get_command_argument(1, arg)
    read(arg, *) nrec
  end if
  if (command_argument_count() >= 2) then
    call get_command_argument(2, arg)
    read(arg, *) nchan
  end if
  if (command_argument_count() >= 3) verbose = .true.

  allocate(readings(nrec, nchan), rms(nchan))
  call random_number(readings)
  readings = 100.0_dp * readings

  call system_clock(t0, rate)
  do i = 1, nrec
    call detrend_record(readings(i, :), nchan)
    call clip_record(readings(i, :), nchan, 40.0_dp)
    if (verbose .and. mod(i, 50000) == 0) print *, 'record', i
  end do
  call system_clock(t1)
  print '(a,f8.3,a)', 'detrend+clip: ', real(t1 - t0) / rate, ' s'

  call system_clock(t0)
  rms = channel_rms(readings, nrec, nchan)
  call system_clock(t1)
  print '(a,f8.3,a)', 'rms:          ', real(t1 - t0) / rate, ' s'

  print '(a,3f10.4)', 'first rms values: ', rms(1:3)

  ! TODO: write rms to calibration file, format not agreed with the lab yet
end program sensor_cal

我没看过汇编,也没开警告。上面的耗时是凭记忆写的,不精确。

从英语翻译查看原文

后续讨论 2

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

    是一个问题,不是两个。Fortran 是列主序,所以 readings(i,:) 是带步长的,每次调用都会拷贝到临时数组。把 rms 里的循环顺序换一下。更好的做法是把数组声明成 (nchan, nrec)。用 -Warray-temporaries 编译可以看到这些拷贝。

    从英语翻译查看原文

同一个问题的其他角度

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

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

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

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

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

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

    回复 2最近活动 ocaml