와 좋은 질문이네요. 덕분에 저도 원론적인 부분에서 다시 이해를 다잡는 좋은 기회가 되었습니다.
- 기본적으로 Julia의 Argument Passing 은 "pass-by-sharing"입니다. 이 점을 염두에 두셔야 합니다.
그럼 BenchmarkTools.jl
의 @btime
과 InteractiveUtils
의 @code_lowered
를 이용해서 각자의 코드가 내부적으로 어떻게 동작하는 지 알아봅시다.
-
test1
test1(C, A, B) 37.206 ns (1 allocation: 128 bytes) CodeInfo( 1 ─ C@_5 = C@_2 │ %2 = Base.broadcasted(Main.:+, A, B) │ C@_5 = Base.materialize(%2) └── return Main.nothing )
이는
A .+ B
를 계산하고 이를C
와 다른 변수에 대입했다는 것입니다. 코드를 설명하자면 broadcast(.+
) 할때broadcasted
를 통해 처리하지만 실제로는 계산이 매번 일어나지 않고 마지막에materialize
에서 한번에 일어납니다. (lazy evaluation)본론으로 들어가서
=
는 이름을 바꾼다고 생각하시면 됩니다. 위에서 언급했듯이 argument passing인 경우에Function arguments themselves act as new variable bindings (new locations that can refer to values), but the values they refer to are identical to the passed values."
라고 되어있죠? new variable "binding"즉 function argument의 변수 이름이 원래 외부의 변수 이름과 다르더라도 같은 object를 가르키지만, 이름은 다르게 불리울 수 있다는 것을 뜻합니다. 이걸 이해하시고 엄청 오래된 글이지만 이 글을 보시면 이해가 잘되실 겁니다. 여기서 설명하긴 너무 길어서요 ㅜㅜ
-
test2
test2(C, A, B) 15.809 ns (0 allocations: 0 bytes) CodeInfo( 1 ─ %1 = Base.broadcasted(Main.:+, A, B) │ Base.materialize!(C, %1) └── return Main.nothing )
이는
A .+ B
를 계산하고 이를C
에 대입했다는 것입니다. .= 은 실제로 내용을 바꾸는 겁니다.materialize!
라고 되어있죠.!
는 argument를 바꾼다고 함수에 표시할 때 씁니다. 이 함수 정의는 실제론 잘못되었죠.test!(C, A, B)
라고 정의했어야 합니다. -
test3
test3(C, A, B) 40.831 ns (1 allocation: 128 bytes) CodeInfo( 1 ─ %1 = Base.broadcasted(Main.:+, A, B) │ %2 = Base.materialize(%1) │ Base.setindex!(C, %2, Main.:(:)) └── return Main.nothing )
이는
A .+ B
를 계산하고 이를C[:]
에 대입했다는 것입니다. Julia는 Functional Programming도 들어가있습니다. FP는 모든 것은 함수로 이루어졌다고 봅니다. 그 중 Array Indexing은 불러올때는getindex
를 대입할 때는setindex!
라는 함수를 호출한다고 생각하시면 됩니다. 즉 여기선,test1
과 비슷한 동작을 했지만, 대신 이건[:]
를 통해 다시 C에 대입합니다. 이러면 당연히test2
보다 allocation이 한 번 더 일어나므로 비효율적이죠. 그리고 이 함수 또한test3!(C, A, B)
라고 정의했어야합니다.
질문하신건 설명을 다 드린 것 같고 다른걸 테스트해보다가 재밌는걸 발견했습니다. @btime
으로 테스트해보니 제일 효율적인건
function test4(C, A, B)
for i=1:length(C)
C[i] = A[i] + B[i]
end
end
7.357 ns (0 allocations: 0 bytes)
더군요. test4
가 빠르다는건 예상했는데 broadcast
보다 두배나 더 빠를 줄은 몰랐네요. 아마 Type Inference과정때문인거 같은데 이건 한번 타입 지정해보시고 알아보시죠 ㅋㅋ (실은 귀찮아서..)
재미있네요. 자세한 설명 감사합니다. 따라 해보다가 더 재미있는걸 발견했네요. Matrix 크기에 따라
test2!
와test4!
의 속도 차이가 달라지네요.n이 아주 작을땐
test4!
가 빠르다가, 나중엔test2!
가 더 빠른데, 점점 크기가 더 커지니까 둘 다 비슷해지다가 다시test4!
가 역전을. 왜 그러는지는 잘 모르겠습니다.코드는