주리아를 빠르게 해주세요!

주리아는 특별히 노력하지 않아도 빠른 코드를 쓸 수 있다코드를 더 빨리 쓸 수 있는 몇 가지 비결이 있어요..이번에는 캐시 조정에 대해 설명해 드리겠습니다.BenchmarkTools.jl로 줄리아도 포란과 같은 방법으로 캐시 조정을 하는 것이 효과가 있는지 연구했다.

작업 환경


versioninfo()
Julia Version 1.7.1
Commit ac5cc99908 (2021-12-22 19:35 UTC)
Platform Info:
OS: Windows (x86_64-w64-mingw32)
CPU: Intel(R) Core(TM) i7-4650U CPU @ 1.70GHz
WORD_SIZE: 64
LIBM: libopenlibm
LLVM: libLLVM-12.0.1 (ORCJIT, haswell)

큰 가방


# using Pkg
# Pkg.add("BenchmarkTools")
using BenchmarkTools

캐시 튜닝


간단하게 설명하자면 컴퓨터는 메모리에서 CPU(레지스터)로 데이터를 공유한다.메모리-캐시 간의 통신 속도가 느리기 때문에 가능한 한 캐시-CPU(레지스터) 간의 통신을 하고, 쓰기 프로그램을 캐시 튜닝이라고 한다.
청산행도 《조화기법호지권》p.4-4
[중요] 다차원 진열(다순환) 상황에서 고속 캐시 오류를 줄이기 위해
Fortran에서 그림4-2-3(1)에서 보듯이 내부 순환은 진열 왼쪽의 색인으로 중복된다.
그림4-2-3 (1) 좋은 예
DIMENSION A(4,9)
DO J=1,9
  DO I=1,4
    A(I,J) = A(I,J) + 1.0
  ENDDO
ENDDO
그림4-2-4(1) 불량 예시
DIMENSION A(4,9)
DO I=1,4
  DO J=1,9
    A(I,J) = A(I,J) + 1.0
  ENDDO
ENDDO
기억 속의 다차원 그룹의 저장 방향은 주리아와Fortran에서 같기 때문에 주리아도 상기 Fortran의 캐시 조정이 똑같이 효과적일 것으로 예상할 수 있다.

기준


2차원수조의 총계를 계산하다.비교 함수는 다음과 같은 bad()good() 이외에 표준 라이브러리sum() 3개가 있다.
늦다
function bad(arr)
    sum = 0
    for i in 1:size(arr)[1]
        for j in 1:size(arr)[2]
           sum += arr[i,j]
        end
    end
    return sum
end
빨리
function good(arr)
    sum = 0
    for j in 1:size(arr)[2]
        for i in 1:size(arr)[1]
           sum += arr[i,j]
        end
    end
    return sum
end
다음 진열의 총계를 계산하다.배열A은 고정밀 부동점수(Flat64)의 1000입니다.×1000개의 배열이 있다.
A = rand(1000,1000)
각 요소는 [0,1)에 따라 고르게 분포된 위임수이기 때문에 총합은 500000이어야 한다.
println("bad(A)  = ", bad(A))
println("good(A) = ", good(A))
println("sum(A)  = ", sum(A))
bad(A) = 500432.86382117023
good(A) = 500432.8638211867
sum(A) = 500432.86382120027
부동점 계산이기 때문에 와 순서에 따라 결과는 조금 다르지만 대략 50000이다.

결실


기준 테스트의 결과는 다음과 같다.
@benchmark bad(A)
BenchmarkTools.Trial: 1479 samples with 1 evaluation.
Range (min … max): 3.153 ms … 11.431 ms ┊ GC (min … max): 0.00% … 0.00%
Time (median): 3.327 ms ┊ GC (median): 0.00%
Time (mean ± σ): 3.363 ms ± 419.905 μs ┊ GC (mean ± σ): 0.00% ± 0.00%
Memory estimate: 16 bytes, allocs estimate: 1.
@benchmark good(A)
BenchmarkTools.Trial: 4180 samples with 1 evaluation.
Range (min … max): 979.800 μs … 11.564 ms ┊ GC (min … max): 0.00% … 0.00%
Time (median): 1.151 ms ┊ GC (median): 0.00%
Time (mean ± σ): 1.179 ms ± 307.241 μs ┊ GC (mean ± σ): 0.00% ± 0.00%
Memory estimate: 16 bytes, allocs estimate: 1.
@benchmark sum(A)
BenchmarkTools.Trial: 9209 samples with 1 evaluation.
Range (min … max): 478.700 μs … 5.138 ms ┊ GC (min … max): 0.00% … 0.00%
Time (median): 509.500 μs ┊ GC (median): 0.00%
Time (mean ± σ): 525.046 μs ± 104.547 μs ┊ GC (mean ± σ): 0.00% ± 0.00%
Memory estimate: 16 bytes, allocs estimate: 1.
귀납은 표에서 다음과 같은 계산 시간이다.
함수.
Time(mean)A3327µsbad()1151µsgood()509µs

총결산


줄리아도 포란과 같은 방법으로 캐시 튜닝을 할 수 있는 것으로 알려졌다.그러나 sum()가 압도적으로 빠르기 때문에 기존 라이브러리의 함수를 최대한 활용하는 것을 권장합니다.

참고 문헌


  • 청산행도 《조화기법호지권》 p.4-4
  • https://julialang.org/blog/2013/09/fast-numeric/#write_cache-friendly_codes
  • 이 기사의 출처인 Jupyter Notebook의 데이터는 다음 링크에 있습니다.
    https://gist.github.com/ohno/86f1ba4c9a237b17a10cb88904e283e6

    좋은 웹페이지 즐겨찾기