줄리아에서 부분배열 빠르게 참조하는 법

줄리아에서 부분배열 빠르게 참조하는 법

How to View Subarray in julia

개요

줄리아에서 view는 배열Array의 부분배열Subarray를 빠르게 참조하게끔 해주는 데이터 구조다. 1 실제로 쓰는 입장에서는 번거롭기만하고 차이가 없어보이지만 게으르게Lazily 참조되면서 더 가볍운 배열을 리턴한다. 따라서 아주 베이직한 수준에서까지 최적화된 줄리아 코드에서는 @views라는 매크로를 쉽게 찾아볼 수 있다. 2

코드

julia> M = rand(0:1,10,10)
10×10 Matrix{Int64}:
 1  0  1  1  1  0  0  1  1  0
 1  0  1  1  0  0  0  1  1  1
 0  0  0  1  0  0  1  1  1  0
 1  0  1  1  1  1  1  1  0  1
 1  1  0  1  1  1  1  0  0  1
 1  0  0  0  0  1  1  0  1  0
 1  1  1  0  0  0  0  0  1  0
 1  1  0  1  1  1  0  0  1  1
 1  1  1  1  1  1  0  0  1  1
 0  0  0  0  0  1  0  0  0  1

위 행렬 M의 부분행렬을 참조해보자.

함수형: view()

julia> A = view(M, 3:4, :)
2×10 view(::Matrix{Int64}, 3:4, :) with eltype Int64:
 0  0  0  1  0  0  1  1  1  0
 1  0  1  1  1  1  1  1  0  1

view(A, inds...)

  • Ainds...에 따른 view를 리턴한다.

그러나 이 형태는 일반적으로 코드를 보기 어렵게 만들기 때문에 선호되지 않는다. 다음의 매크로를 사용하면 view를 사용하면서도 기본적인 줄리아 문법과 큰 차이가 없다.

매크로: @view

julia> B = @view M[3:4,:]
2×10 view(::Matrix{Int64}, 3:4, :) with eltype Int64:
 0  0  0  1  0  0  1  1  1  0
 1  0  1  1  1  1  1  1  0  1

@view 매크로는 일반적으로 부분배열을 참조하는 맥락의 코드에 view가 적용된 것처럼 바꿔주는 매크로다.

블럭 전체에 적용: @views

julia> @views begin
           C = M[5:6,:];
           D = M[7:8,:];
       end
2×10 view(::Matrix{Int64}, 7:8, :) with eltype Int64:
 1  1  1  0  0  0  0  0  1  0
 1  1  0  1  1  1  0  0  1  1

julia> C
2×10 view(::Matrix{Int64}, 5:6, :) with eltype Int64:
 1  1  0  1  1  1  1  0  0  1
 1  0  0  0  0  1  1  0  1  0

julia> D
2×10 view(::Matrix{Int64}, 7:8, :) with eltype Int64:
 1  1  1  0  0  0  0  0  1  0
 1  1  0  1  1  1  0  0  1  1

@views 매크로는 @view를 이어지는 블럭 전체에 적용시켜준다. 이 덕에 view 없이 편하게 작성한 함수 앞에 @views f(x) ... end처럼 @views만 붙여주면 알아서 view를 적용시켜준다.

전체 코드

M = rand(0:1,10,10)
A = view(M, 3:4, :)
B = @view M[3:4,:]
@views begin
    C = M[5:6,:];
    D = M[7:8,:];
end
C
D

fcopy(x) = sum(x[2:end-1]);
@views fview(x) = sum(x[2:end-1]);
x = rand(10^6);

@time fcopy(x);
@time fview(x);

속도 비교

julia> fcopy(x) = sum(x[2:end-1]);

julia> @views fview(x) = sum(x[2:end-1]);

julia> x = rand(10^6);

julia> @time fcopy(x);
  0.015874 seconds (11.08 k allocations: 8.201 MiB, 79.77% compilation time)

julia> @time fview(x);
  0.016209 seconds (77.07 k allocations: 4.149 MiB, 96.49% compilation time)

fcopy()fview()는 정확히 같은 기능을 하는 함수지만 속도에서 차이가 난다. 위 결과만 보면 비슷비슷한 속도처럼 보이지만 대부분은 컴파일 시간Compilation Time이다. 이를 제외하고 당순 실행시간만 비교해보면 다음과 같이 약 4배 정도의 차이가 난다.

julia> @time fcopy(x);
  0.002933 seconds (3 allocations: 7.629 MiB)

julia> @time fview(x);
  0.000660 seconds (1 allocation: 16 bytes)

환경

  • OS: Windows
  • julia: v1.7.0

  1. https://docs.julialang.org/en/v1/base/arrays/#Views-(SubArrays-and-other-view-types) ↩︎

  2. https://docs.julialang.org/en/v1/manual/performance-tips/#man-performance-views ↩︎

댓글