openlang

A slow gfortran loop that copied an array section on every call

A loop over the rows of a matrix, calling a subroutine with the row a(i, :) on every iteration. Right answer, embarrassing speed. The time goes into copying, not arithmetic.

subroutine scale(v, n)
  integer, intent(in) :: n
  real, intent(inout) :: v(n)
  v = 2.0 * v
end subroutine scale

! caller, a is m by n
do i = 1, m
  call scale(a(i, :), n)
end do

An explicit-shape dummy like v(n) assumes one contiguous run of memory. A row in Fortran isn't contiguous, neighbouring elements sit a whole column apart. So gfortran makes a temporary array on every call, copies the row in, and copies it back out after the call. Two copies per iteration, and nothing says so by default.

-Warray-temporaries points at the spot at compile time, and -fcheck=array-temps reports the temporary at run time. Both are in the debug build now. Whether to rewrite every such subroutine with v(:) or change the loop order is still open.

后续讨论 2

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

    Make the dummy assumed-shape, v(:). The caller then passes a descriptor with a stride, no copy.

    module ops
    contains
      subroutine scale(v)
        real, intent(inout) :: v(:)
        v = 2.0 * v
      end subroutine scale
    end module ops

    The call becomes call scale(a(i, :)) after use ops. It needs an explicit interface and the module provides one. Without it the call is invalid, and the compiler isn't required to notice.

    The copy goes. The stride stays. The subroutine still walks memory a column apart. If the loop order is free, loop over columns with a(:, j).

  2. 固定链接

    Fun side effect here. Put contiguous on that v(:) dummy and the copy is back. The attribute promises the compiler that the dummy's elements sit next to each other, so a strided actual like a(i, :) has to be packed into a temporary before the call, same as with v(n). People add contiguous because it can help vectorization, and here it would quietly undo pgr's fix for exactly the calls that started this. I'd grep the sources for it before calling the loop fixed.

同一个问题的其他角度

  1. GOMAXPROCS in a container now follows the CPU limit

    Before Go 1.25, GOMAXPROCS defaulted to the logical CPU count of the machine. A container limited to two CPUs on a host with…

    回复 8最近活动 go
  2. try vs catch unreachable compares two different programs

    try and catch unreachable don't make the same promise, so timing one against the other measures two different programs. Both…

    回复 4最近活动 zig
  3. Column-major loop order in Fortran

    Fortran arrays are column-major. a(i,j) and a(i+1,j) are neighbours in memory. a(i,j+1) is a whole column away. So the loop n…

    回复 1最近活动 fortran