openlang

f2py가 C 순서 배열을 호출마다 복사한다

f2py로 NumPy 배열을 Fortran 서브루틴에 넘긴다. 배열은 평소처럼 np.zeros((n, m))로 만들었으니 C 순서다. Fortran은 열이 연속으로 놓여 있길 원한다. f2py도 그걸 알고, 일반 입력 인자라면 맞는 순서로 원소 단위 복사를 아무 말 없이 해 준다. 경고는 없다. 돌아가고 숫자도 맞다. 다만 루프에서 호출할 때마다 행렬을 복사한다.

intent(inout)은 더 귀찮지만 더 정직하다. 거기서 복사본을 넘기면 바뀐 값이 복사본과 같이 사라질 테니, f2py는 제대로 배치된 배열을 요구하고 아니면 예외를 던진다. 처방은 만들 때 order='F'를 주거나, 루프 전에 np.asfortranarray를 한 번 부르는 것이다.

열 우선 순서라는 오래된 얘기다. 이번엔 두 언어가 만나는 자리에 있을 뿐이고, 값은 호출마다 복사로 치른다.

귀찮다. 차라리 경고라도 받았으면 좋겠다.

이어지는 대화 3

서로의 근거를 따라
  1. 고유 링크

    1차원 배열은 예외다. 연속된 벡터는 C 연속이면서 F 연속이다. 복사 없다. 문제는 2차원부터다.

  2. 고유 링크

    Fortran은 잘 모르지만, 루프가 실제로 복사를 몇 번 하는지 궁금해요. f2py 문서 예제는 -DF2PY_REPORT_ON_ARRAY_COPY=1을 주고 빌드하는데, 그러면 래퍼가 배열을 복사할 때마다 메시지를 찍어요. 순서만 뒤집고 나머지는 그대로 둔 채 메시지를 다시 세어 봐요.

    확인해 볼 경우가 하나 더 있어요. np.asfortranarray 다음에 a[:, ::2] 같은 슬라이스를 잡으면 더는 연속이 아니니까, 복사가 다시 생길 것 같아요. 그래도 추측보다는 메시지를 직접 보는 게 낫겠죠.

  3. 고유 링크

    mtsuda의 플래그가 이럴 때 딱 맞는 도구다. -DF2PY_REPORT_ON_ARRAY_COPY=1로 빌드하면 C 순서 배열은 호출마다 복사 메시지를 띄우고, 루프 전에 np.asfortranarray 한 번이면 조용해지며, a[:, ::2] 슬라이스는 위에서 예상한 대로 다시 복사를 찍는다.

    함정이 하나 더 있다. 그 슬라이스를 intent(inout)에 넘기면 원래 배열이 Fortran 순서여도 예외가 난다. intent(inout)은 연속된 배열을 요구하는데 건너뛰는 슬라이스는 연속이 아니기 때문이다.

같은 질문의 다른 갈래

  1. 행이 하나인 행렬의 sum(X)

    관측 행렬을 받아 각 열을 X ./ sum(X)로 정규화하는 함수가 있다. 행은 관측, 열은 특성이다. 오래 문제없이 돌았다. 그러다 행이 하나뿐인 입력이 들어왔다. sum은 크기가 1이 아닌 첫 번째 차원을 따라 더한다.…

    답글 6matlab
  2. 전부 NA인 그룹에 na.rm = TRUE

    그룹별 합계를 tapply(x, g, sum, na.rm = TRUE)로 냈더니 한 그룹이 0으로 나왔다. 그 그룹의 값은 전부 NA다. 측정값이 하나도 없는데 표에는 합계가 0이라고 적혀 있다. 하나씩 보면 다 변명할 수…

    답글 4r
  3. 열마다 새로 할당하던 루프

    Julia에서 슬라이싱은 복사를 해요. 알고 있었는데 루프 안에서는 깜빡했어요. 행렬의 열을 돌면서 j마다 s += sum(A[:, j])를 하는 함수를 예로 들어 볼게요. A[:, j]가 매번 새 벡터를 만들고, 더하고,…

    답글 4julia