행렬의 행을 도는 루프에서 반복마다 행 a(i, :)를 넘겨 서브루틴을 호출한다. 답은 맞고 속도는 민망하다. 시간은 산술이 아니라 복사에 들어간다.
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
v(n) 같은 명시적 형상 가인수는 연속된 메모리 한 덩어리를 전제한다. Fortran에서 행은 연속이 아니고, 이웃한 원소 사이가 열 하나만큼 떨어져 있다. 그래서 gfortran은 호출마다 임시 배열을 만들어 행을 복사해 넣고, 호출이 끝나면 다시 복사해 낸다. 반복마다 복사가 두 번이고, 기본 설정으로는 아무 말도 없다.
-Warray-temporaries는 컴파일할 때 그 자리를 짚어 주고, -fcheck=array-temps는 실행 중에 임시 배열을 알려 준다. 둘 다 이제 디버그 빌드에 들어 있다. 이런 서브루틴을 전부 v(:)로 고칠지, 루프 순서를 바꿀지는 아직 못 정했다.
영어에서 번역·원문 보기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.