Python ์ฝ๋ ์ฑ๋ฅ์ ๋ช ๋ฐฐ๋ก ํฅ์์ํค์ธ์. ์ด ํฌ๊ด์ ์ธ ๊ฐ์ด๋๋ ์ ์ธ๊ณ ๊ฐ๋ฐ์๋ฅผ ์ํด SIMD, ๋ฒกํฐํ, NumPy ๋ฐ ๊ณ ๊ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ดํด๋ด ๋๋ค.
์ฑ๋ฅ ์ ๊ธ ํด์ : Python SIMD ๋ฐ ๋ฒกํฐํ์ ๋ํ ํฌ๊ด์ ์ธ ๊ฐ์ด๋
์ปดํจํ ์ธ๊ณ์์ ์๋๋ ๊ฐ์ฅ ์ค์ํฉ๋๋ค. ๋ฐ์ดํฐ ๊ณผํ์๊ฐ ๋จธ์ ๋ฌ๋ ๋ชจ๋ธ์ ํ๋ จํ๋ , ๊ธ์ต ๋ถ์๊ฐ๊ฐ ์๋ฎฌ๋ ์ด์ ์ ์คํํ๋ , ์ํํธ์จ์ด ์์ง๋์ด๊ฐ ๋๊ท๋ชจ ๋ฐ์ดํฐ ์ธํธ๋ฅผ ์ฒ๋ฆฌํ๋ , ์ฝ๋ ํจ์จ์ฑ์ ์์ฐ์ฑ ๋ฐ ๋ฆฌ์์ค ์๋น์ ์ง์ ์ ์ธ ์ํฅ์ ๋ฏธ์นฉ๋๋ค. ๋จ์์ฑ๊ณผ ๊ฐ๋ ์ฑ์ผ๋ก ์ ๋ช ํ Python์ ๊ณ์ฐ ์ง์ฝ์ ์ธ ์์ , ํนํ ๋ฃจํ์ ๊ด๋ จ๋ ์์ ์์ ์ ์๋ ค์ง ์ํฌ๋ ์ค๊ฑด์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ๊ทธ๋ฌ๋ ํ ๋ฒ์ ํ๋์ ์์๊ฐ ์๋ ์ ์ฒด ๋ฐ์ดํฐ ์ปฌ๋ ์ ์ ๋ํ ์์ ์ ๋์์ ์คํํ ์ ์๋ค๋ฉด ์ด๋จ๊น์? ์ด๊ฒ์ด ๋ฐ๋ก CPU ๊ธฐ๋ฅ์ธ SIMD๋ก ๊ตฌ๋๋๋ ํจ๋ฌ๋ค์์ธ ๋ฒกํฐํ๋ ๊ณ์ฐ์ ์ฝ์์ ๋๋ค.
์ด ๊ฐ์ด๋๋ Python์์ SIMD(Single Instruction, Multiple Data) ์ฐ์ฐ ๋ฐ ๋ฒกํฐํ์ ์ธ๊ณ๋ก ์ฌ๋ฌ๋ถ์ ์๋ดํฉ๋๋ค. CPU ์ํคํ ์ฒ์ ๊ธฐ๋ณธ ๊ฐ๋ ๋ถํฐ NumPy, Numba ๋ฐ Cython๊ณผ ๊ฐ์ ๊ฐ๋ ฅํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ์ค์ ์ ์ฉ๊น์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค. ์ฐ๋ฆฌ์ ๋ชฉํ๋ ์ง๋ฆฌ์ ์์น๋ ๋ฐฐ๊ฒฝ์ ๊ด๊ณ์์ด ๋๋ฆฌ๊ณ ๋ฃจํ๊ฐ ๋ง์ Python ์ฝ๋๋ฅผ ๊ณ ๋๋ก ์ต์ ํ๋ ๊ณ ์ฑ๋ฅ ์ ํ๋ฆฌ์ผ์ด์ ์ผ๋ก ๋ณํํ ์ ์๋ ์ง์์ ์ ๊ณตํ๋ ๊ฒ์ ๋๋ค.
๊ธฐ๋ฐ: CPU ์ํคํ ์ฒ ๋ฐ SIMD ์ดํด
๋ฒกํฐํ์ ํ์ ์ง์ ์ผ๋ก ์ดํดํ๋ ค๋ฉด ๋จผ์ ์ต์ ์ค์ ์ฒ๋ฆฌ ์ฅ์น(CPU)๊ฐ ์ด๋ป๊ฒ ์๋ํ๋์ง ์์ธํ ์ดํด๋ด์ผ ํฉ๋๋ค. SIMD์ ๋ง๋ฒ์ ์ํํธ์จ์ด ํธ๋ฆญ์ด ์๋๋๋ค. ์์น ๊ณ์ฐ์ ํ๋ช ์ ์ผ์ผํจ ํ๋์จ์ด ๊ธฐ๋ฅ์ ๋๋ค.
SISD์์ SIMD๋ก: ๊ณ์ฐ์ ํจ๋ฌ๋ค์ ์ ํ
์ค๋ซ๋์ ์ง๋ฐฐ์ ์ธ ๊ณ์ฐ ๋ชจ๋ธ์ SISD(Single Instruction, Single Data)์์ต๋๋ค. ์๋ฆฌ์ฌ๊ฐ ํ ๋ฒ์ ํ๋์ ์ผ์ฑ๋ฅผ ๊ผผ๊ผผํ๊ฒ ์๋ฅด๋ ๊ฒ์ ์์ํด ๋ณด์ญ์์ค. ์๋ฆฌ์ฌ๋ ํ๋์ ์ง์นจ("์๋ฅด๊ธฐ")์ ๊ฐ์ง๊ณ ์์ผ๋ฉฐ ํ๋์ ๋ฐ์ดํฐ ์กฐ๊ฐ(๋น๊ทผ ํ๋)์ ๋ํด ์๋ํฉ๋๋ค. ์ด๋ ๊ธฐ์กด CPU ์ฝ์ด๊ฐ ์ฌ์ดํด๋น ํ๋์ ๋ฐ์ดํฐ ์กฐ๊ฐ์ ๋ํด ํ๋์ ๋ช ๋ น์ ์คํํ๋ ๊ฒ๊ณผ ์ ์ฌํฉ๋๋ค. ๋ ๋ชฉ๋ก์์ ์ซ์๋ฅผ ํ๋์ฉ ๋ํ๋ ๊ฐ๋จํ Python ๋ฃจํ๋ SISD ๋ชจ๋ธ์ ์๋ฒฝํ ์์ ๋๋ค.
# ๊ฐ๋
์ SISD ์ฐ์ฐ
result = []
for i in range(len(list_a)):
# ํ ๋ฒ์ ํ๋์ ๋ฐ์ดํฐ ์กฐ๊ฐ(a[i], b[i])์ ๋ํ ํ๋์ ๋ช
๋ น(์ถ๊ฐ)
result.append(list_a[i] + list_b[i])
์ด ์ ๊ทผ ๋ฐฉ์์ ์์ฐจ์ ์ด๋ฉฐ ๊ฐ ๋ฐ๋ณต์ ๋ํด Python ์ธํฐํ๋ฆฌํฐ์์ ์๋นํ ์ค๋ฒํค๋๊ฐ ๋ฐ์ํฉ๋๋ค. ์ด์ ์๋ฆฌ์ฌ์๊ฒ ๋ ๋ฒ๋ฅผ ํ ๋ฒ ๋น๊ฒจ์ ํ ๋ฒ์ ๋น๊ทผ ๋ค ์ค ์ ์ฒด๋ฅผ ์๋ฅผ ์ ์๋ ํน์ ๊ธฐ๊ณ๋ฅผ ์ ๊ณตํ๋ค๊ณ ์์ํด ๋ณด์ญ์์ค. ์ด๊ฒ์ด ๋ฐ๋ก SIMD(Single Instruction, Multiple Data)์ ๋ณธ์ง์ ๋๋ค. CPU๋ ๋จ์ผ ๋ช ๋ น์ ์คํํ์ง๋ง ํน์ํ๊ณ ๋์ ๋ ์ง์คํฐ์ ํจ๊ป ๋ฌถ์ธ ์ฌ๋ฌ ๋ฐ์ดํฐ ํฌ์ธํธ์ ๋ํด ์๋ํฉ๋๋ค.
์ต์ CPU์์ SIMD๊ฐ ์๋ํ๋ ๋ฐฉ์
Intel ๋ฐ AMD์ ๊ฐ์ ์ ์กฐ์ ์ฒด์ ์ต์ CPU์๋ ์ด๋ฌํ ๋ณ๋ ฌ ์ฐ์ฐ์ ์ํํ๊ธฐ ์ํ ํน์ SIMD ๋ ์ง์คํฐ ๋ฐ ๋ช ๋ น ์ธํธ๊ฐ ์ฅ์ฐฉ๋์ด ์์ต๋๋ค. ์ด๋ฌํ ๋ ์ง์คํฐ๋ ๋ฒ์ฉ ๋ ์ง์คํฐ๋ณด๋ค ํจ์ฌ ๋์ผ๋ฉฐ ํ ๋ฒ์ ์ฌ๋ฌ ๋ฐ์ดํฐ ์์๋ฅผ ๋ด์ ์ ์์ต๋๋ค.
- SIMD ๋ ์ง์คํฐ: ์ด๋ CPU์ ๋ํ ํ๋์จ์ด ๋ ์ง์คํฐ์ ๋๋ค. ํฌ๊ธฐ๋ ์๊ฐ์ด ์ง๋จ์ ๋ฐ๋ผ 128๋นํธ, 256๋นํธ, ํ์ฌ๋ 512๋นํธ ๋ ์ง์คํฐ๊ฐ ์ผ๋ฐ์ ์ ๋๋ค. ์๋ฅผ ๋ค์ด 256๋นํธ ๋ ์ง์คํฐ๋ 8๊ฐ์ 32๋นํธ ๋ถ๋ ์์์ ์ซ์ ๋๋ 4๊ฐ์ 64๋นํธ ๋ถ๋ ์์์ ์ซ์๋ฅผ ๋ด์ ์ ์์ต๋๋ค.
- SIMD ๋ช
๋ น ์ธํธ: CPU์๋ ์ด๋ฌํ ๋ ์ง์คํฐ๋ก ์์
ํ๊ธฐ ์ํ ํน์ ๋ช
๋ น์ด ์์ต๋๋ค. ๋ค์๊ณผ ๊ฐ์ ์ฝ์ด๋ฅผ ๋ค์ด๋ณด์
จ์ ๊ฒ์
๋๋ค.
- SSE(Streaming SIMD Extensions): ์ด์ 128๋นํธ ๋ช ๋ น ์ธํธ์ ๋๋ค.
- AVX(Advanced Vector Extensions): 256๋นํธ ๋ช ๋ น ์ธํธ๋ก, ์๋นํ ์ฑ๋ฅ ํฅ์์ ์ ๊ณตํฉ๋๋ค.
- AVX2: ๋ ๋ง์ ๋ช ๋ น์ด ์๋ AVX์ ํ์ฅ์ ๋๋ค.
- AVX-512: ๋ง์ ์ต์ ์๋ฒ ๋ฐ ๊ณ ๊ธ ๋ฐ์คํฌํฑ CPU์์ ๋ณผ ์ ์๋ ๊ฐ๋ ฅํ 512๋นํธ ๋ช ๋ น ์ธํธ์ ๋๋ค.
์ด๋ฅผ ์๊ฐํํด ๋ณด๊ฒ ์ต๋๋ค. ๋ ๊ฐ์ ๋ฐฐ์ด `A = [1, 2, 3, 4]`์ `B = [5, 6, 7, 8]`์ ๋ํ๊ณ ๊ฐ ์ซ์๊ฐ 32๋นํธ ์ ์๋ผ๊ณ ๊ฐ์ ํฉ๋๋ค. 128๋นํธ SIMD ๋ ์ง์คํฐ๊ฐ ์๋ CPU์์:
- CPU๋ `[1, 2, 3, 4]`๋ฅผ SIMD ๋ ์ง์คํฐ 1์ ๋ก๋ํฉ๋๋ค.
- CPU๋ `[5, 6, 7, 8]`์ SIMD ๋ ์ง์คํฐ 2์ ๋ก๋ํฉ๋๋ค.
- CPU๋ ๋จ์ผ ๋ฒกํฐํ๋ "add" ๋ช ๋ น์ ์คํํฉ๋๋ค(`_mm_add_epi32`๋ ์ค์ ๋ช ๋ น์ ์์ ๋๋ค).
- ๋จ์ผ ํด๋ญ ์ฌ์ดํด์์ ํ๋์จ์ด๋ `1+5`, `2+6`, `3+7`, `4+8`์ 4๊ฐ์ ๊ฐ๋ณ ์ถ๊ฐ๋ฅผ ๋ณ๋ ฌ๋ก ์ํํฉ๋๋ค.
- ๊ฒฐ๊ณผ `[6, 8, 10, 12]`์ ๋ค๋ฅธ SIMD ๋ ์ง์คํฐ์ ์ ์ฅ๋ฉ๋๋ค.
์ด๋ ํต์ฌ ๊ณ์ฐ์ ๋ํ SISD ์ ๊ทผ ๋ฐฉ์๋ณด๋ค 4๋ฐฐ ๋น ๋ฅธ ์๋ ํฅ์์ด๋ฉฐ, ๋ช ๋ น ๋์คํจ์น ๋ฐ ๋ฃจํ ์ค๋ฒํค๋์ ๋ํญ์ ์ธ ๊ฐ์๋ ๊ณ ๋ คํ์ง ์์์ต๋๋ค.
์ฑ๋ฅ ๊ฒฉ์ฐจ: ์ค์นผ๋ผ ๋ ๋ฒกํฐ ์ฐ์ฐ
๊ธฐ์กด์ ํ ๋ฒ์ ํ๋์ ์์ ์ฐ์ฐ์ ์ค์นผ๋ผ ์ฐ์ฐ์ด๋ผ๊ณ ํฉ๋๋ค. ์ ์ฒด ๋ฐฐ์ด ๋๋ ๋ฐ์ดํฐ ๋ฒกํฐ์ ๋ํ ์ฐ์ฐ์ ๋ฒกํฐ ์ฐ์ฐ์ ๋๋ค. ์ฑ๋ฅ ์ฐจ์ด๋ ๋ฏธ๋ฌํ์ง ์์ผ๋ฉฐ ๋ช ๋ฐฐ๋ ๋ ์ ์์ต๋๋ค.
- ์ค๋ฒํค๋ ๊ฐ์: Python์์ ๋ฃจํ์ ๋ชจ๋ ๋ฐ๋ณต์๋ ๋ฃจํ ์กฐ๊ฑด ํ์ธ, ์นด์ดํฐ ์ฆ๊ฐ, ์ธํฐํ๋ฆฌํฐ๋ฅผ ํตํ ์์ ๋์คํจ์น์ ๊ฐ์ ์ค๋ฒํค๋๊ฐ ํฌํจ๋ฉ๋๋ค. ๋จ์ผ ๋ฒกํฐ ์ฐ์ฐ์ ๋ฐฐ์ด์ 1,000๊ฐ ๋๋ 100๋ง ๊ฐ์ ์์๊ฐ ์๋์ง์ ๊ด๊ณ์์ด ํ๋์ ๋์คํจ์น๋ง ๊ฐ์ง๋๋ค.
- ํ๋์จ์ด ๋ณ๋ ฌ ์ฒ๋ฆฌ: ์ด๋ฏธ ๋ณด์๋ฏ์ด SIMD๋ ๋จ์ผ CPU ์ฝ์ด ๋ด์์ ๋ณ๋ ฌ ์ฒ๋ฆฌ ์ฅ์น๋ฅผ ์ง์ ํ์ฉํฉ๋๋ค.
- ์บ์ ์ง์ญ์ฑ ํฅ์: ๋ฒกํฐํ๋ ์์ ์ ์ผ๋ฐ์ ์ผ๋ก ์ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ๋ธ๋ก์์ ๋ฐ์ดํฐ๋ฅผ ์ฝ์ต๋๋ค. ์ด๋ ์์ฐจ์ ์ฒญํฌ๋ก ๋ฐ์ดํฐ๋ฅผ ๋ฏธ๋ฆฌ ๊ฐ์ ธ์ค๋๋ก ์ค๊ณ๋ CPU์ ์บ์ฑ ์์คํ ์ ๋งค์ฐ ํจ์จ์ ์ ๋๋ค. ๋ฃจํ์ ์์ ์ก์ธ์ค ํจํด์ ๋งค์ฐ ๋๋ฆฐ ๋น๋ฒํ "์บ์ ๋ฏธ์ค"๋ก ์ด์ด์ง ์ ์์ต๋๋ค.
Python ๋ฐฉ์: NumPy๋ฅผ ์ฌ์ฉํ ๋ฒกํฐํ
ํ๋์จ์ด๋ฅผ ์ดํดํ๋ ๊ฒ์ ํฅ๋ฏธ๋กญ์ง๋ง ๊ทธ ํ์ ํ์ฉํ๊ธฐ ์ํด ์ ์์ค ์ด์ ๋ธ๋ฆฌ ์ฝ๋๋ฅผ ์์ฑํ ํ์๋ ์์ต๋๋ค. Python ์์ฝ์์คํ ์๋ ๋ฒกํฐํ๋ฅผ ์ ๊ทผ ๊ฐ๋ฅํ๊ณ ์ง๊ด์ ์ผ๋ก ๋ง๋๋ ๋๋ผ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์์ต๋๋ค. ๋ฐ๋ก NumPy์ ๋๋ค.
NumPy: Python์์ ๊ณผํ ์ปดํจํ ์ ๊ธฐ๋ฐ
NumPy๋ Python์์ ์์น ๊ณ์ฐ์ ์ํ ๊ธฐ๋ณธ ํจํค์ง์ ๋๋ค. ํต์ฌ ๊ธฐ๋ฅ์ ๊ฐ๋ ฅํ N์ฐจ์ ๋ฐฐ์ด ๊ฐ์ฒด์ธ `ndarray`์ ๋๋ค. NumPy์ ์ง์ ํ ๋ง๋ฒ์ ๊ฐ์ฅ ์ค์ํ ๋ฃจํด(์ํ ์ฐ์ฐ, ๋ฐฐ์ด ์กฐ์ ๋ฑ)์ด Python์ผ๋ก ์์ฑ๋์ง ์์๋ค๋ ๊ฒ์ ๋๋ค. ์ด๋ฌํ ๋ฃจํด์ ํธ์คํธ CPU์์ ์ฌ์ฉ ๊ฐ๋ฅํ SIMD ๋ช ๋ น ์ธํธ๋ฅผ ์ต์ ์ผ๋ก ์ฌ์ฉํ๊ธฐ ์ํด ์ข ์ข ๊ณต๊ธ์ ์ฒด์์ ํ๋ํ๋ BLAS(Basic Linear Algebra Subprograms) ๋ฐ LAPACK(Linear Algebra Package)์ ๊ฐ์ ์ ์์ค ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ํด ๋งํฌ๋๋ ๊ณ ๋๋ก ์ต์ ํ๋ ๋ฏธ๋ฆฌ ์ปดํ์ผ๋ C ๋๋ Fortran ์ฝ๋์ ๋๋ค.
NumPy์์ `C = A + B`๋ฅผ ์์ฑํ๋ฉด Python ๋ฃจํ๋ฅผ ์คํํ๋ ๊ฒ์ด ์๋๋๋ค. SIMD ๋ช ๋ น์ ์ฌ์ฉํ์ฌ ์ถ๊ฐ๋ฅผ ์ํํ๋ ๊ณ ๋๋ก ์ต์ ํ๋ C ํจ์์ ๋จ์ผ ๋ช ๋ น์ ๋์คํจ์นํ๋ ๊ฒ์ ๋๋ค.
์ค์ ์์ : Python ๋ฃจํ์์ NumPy ๋ฐฐ์ด๋ก
์ค์ ๋ก ์๋ํ๋์ง ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค. ๋จผ์ ์์ Python ๋ฃจํ๋ฅผ ์ฌ์ฉํ์ฌ ๋ ๊ฐ์ ํฐ ์ซ์ ๋ฐฐ์ด์ ์ถ๊ฐํ ๋ค์ NumPy๋ฅผ ์ฌ์ฉํ์ฌ ์ถ๊ฐํฉ๋๋ค. Jupyter Notebook ๋๋ Python ์คํฌ๋ฆฝํธ์์ ์ด ์ฝ๋๋ฅผ ์คํํ์ฌ ์์ ์ ์ปดํจํฐ์์ ๊ฒฐ๊ณผ๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.
๋จผ์ ๋ฐ์ดํฐ๋ฅผ ์ค์ ํฉ๋๋ค.
import time
import numpy as np
# ๋ง์ ์์ ์์๋ฅผ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค.
num_elements = 10_000_000
# ์์ Python ๋ชฉ๋ก
list_a = [i * 0.5 for i in range(num_elements)]
list_b = [i * 0.2 for i in range(num_elements)]
# NumPy ๋ฐฐ์ด
array_a = np.arange(num_elements) * 0.5
array_b = np.arange(num_elements) * 0.2
์ด์ ์์ Python ๋ฃจํ์ ์๊ฐ์ ์ธก์ ํด ๋ณด๊ฒ ์ต๋๋ค.
start_time = time.time()
result_list = [0] * num_elements
for i in range(num_elements):
result_list[i] = list_a[i] + list_b[i]
end_time = time.time()
python_duration = end_time - start_time
print(f"์์ Python ๋ฃจํ ์์ ์๊ฐ: {python_duration:.6f}์ด")
์ด์ ๋๋ฑํ NumPy ์ฐ์ฐ์ ์ํํฉ๋๋ค.
start_time = time.time()
result_array = array_a + array_b
end_time = time.time()
numpy_duration = end_time - start_time
print(f"NumPy ๋ฒกํฐํ๋ ์ฐ์ฐ ์์ ์๊ฐ: {numpy_duration:.6f}์ด")
# ์๋ ํฅ์ ๊ณ์ฐ
if numpy_duration > 0:
print(f"NumPy๋ ์ฝ {python_duration / numpy_duration:.2f}๋ฐฐ ๋ ๋น ๋ฆ
๋๋ค.")
์ผ๋ฐ์ ์ธ ์ต์ ์ปดํจํฐ์์ ์ถ๋ ฅ์ ์์ฒญ๋ ๊ฒ์ ๋๋ค. NumPy ๋ฒ์ ์ด 50๋ฐฐ์์ 200๋ฐฐ ๋ ๋น ๋ฅผ ๊ฒ์ผ๋ก ์์ํ ์ ์์ต๋๋ค. ์ด๋ ์ฌ์ํ ์ต์ ํ๊ฐ ์๋๋ผ ๊ณ์ฐ์ด ์ํ๋๋ ๋ฐฉ์์ ๊ทผ๋ณธ์ ์ธ ๋ณํ์ ๋๋ค.
์ ๋๋ฒ์ค ํจ์(ufunc): NumPy ์๋์ ์์ง
๋ฐฉ๊ธ ์ํํ ์ฐ์ฐ(`+`)์ NumPy ์ ๋๋ฒ์ค ํจ์ ๋๋ ufunc์ ์์ ๋๋ค. ์ด๋ฌํ ํจ์๋ `ndarray`์์ ์์๋ณ๋ก ์๋ํฉ๋๋ค. ์ด๋ฌํ ํจ์๋ NumPy ๋ฒกํฐํ๋ ์ฑ๋ฅ์ ํต์ฌ์ ๋๋ค.
ufunc์ ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
- ์ํ ์ฐ์ฐ: `np.add`, `np.subtract`, `np.multiply`, `np.divide`, `np.power`.
- ์ผ๊ฐ ํจ์: `np.sin`, `np.cos`, `np.tan`.
- ๋ ผ๋ฆฌ ์ฐ์ฐ: `np.logical_and`, `np.logical_or`, `np.greater`.
- ์ง์ ๋ฐ ๋ก๊ทธ ํจ์: `np.exp`, `np.log`.
๋ช ์์ ๋ฃจํ๋ฅผ ์์ฑํ์ง ์๊ณ ์ด๋ฌํ ์ฐ์ฐ์ ํจ๊ป ์ฐ๊ฒฐํ์ฌ ๋ณต์กํ ๊ณต์์ ํํํ ์ ์์ต๋๋ค. ๊ฐ์ฐ์ค ํจ์ ๊ณ์ฐ์ ๊ณ ๋ คํด ๋ณด์ญ์์ค.
# x๋ ๋ฐฑ๋ง ๊ฐ์ ์ ์ผ๋ก ๊ตฌ์ฑ๋ NumPy ๋ฐฐ์ด์
๋๋ค.
x = np.linspace(-5, 5, 1_000_000)
# ์ค์นผ๋ผ ์ ๊ทผ ๋ฐฉ์(๋งค์ฐ ๋๋ฆผ)
result = []
for val in x:
term = -0.5 * (val ** 2)
result.append((1 / np.sqrt(2 * np.pi)) * np.exp(term))
# ๋ฒกํฐํ๋ NumPy ์ ๊ทผ ๋ฐฉ์(๋งค์ฐ ๋น ๋ฆ)
result_vectorized = (1 / np.sqrt(2 * np.pi)) * np.exp(-0.5 * x**2)
๋ฒกํฐํ๋ ๋ฒ์ ์ ํจ์ฌ ๋ ๋น ๋ฅผ ๋ฟ๋ง ์๋๋ผ ์์น ๊ณ์ฐ์ ์ต์ํ ์ฌ๋๋ค์๊ฒ๋ ๋ ๊ฐ๊ฒฐํ๊ณ ์ฝ๊ธฐ ์ฝ์ต๋๋ค.
๊ธฐ๋ณธ ์ฌํญ ์ด์: ๋ธ๋ก๋์บ์คํ ๋ฐ ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์
NumPy์ ๋ฒกํฐํ ๊ธฐ๋ฅ์ ๋ธ๋ก๋์บ์คํ ์ด๋ผ๋ ๊ฐ๋ ์ ์ํด ๋์ฑ ํฅ์๋ฉ๋๋ค. ์ด๋ ์ฐ์ ์ฐ์ฐ ์ค์ NumPy๊ฐ ๋ชจ์์ด ๋ค๋ฅธ ๋ฐฐ์ด์ ์ฒ๋ฆฌํ๋ ๋ฐฉ๋ฒ์ ์ค๋ช ํฉ๋๋ค. ๋ธ๋ก๋์บ์คํ ์ ์ฌ์ฉํ๋ฉด ๋ ํฐ ๋ฐฐ์ด์ ๋ชจ์๊ณผ ์ผ์นํ๋๋ก ๋ ์์ ๋ฐฐ์ด์ ๋ณต์ฌ๋ณธ์ ๋ช ์์ ์ผ๋ก ๋ง๋ค์ง ์๊ณ ๋ ํฐ ๋ฐฐ์ด๊ณผ ๋ ์์ ๋ฐฐ์ด(์: ์ค์นผ๋ผ) ๊ฐ์ ์ฐ์ฐ์ ์ํํ ์ ์์ต๋๋ค. ์ด๋ ๊ฒ ํ๋ฉด ๋ฉ๋ชจ๋ฆฌ๊ฐ ์ ์ฝ๋๊ณ ์ฑ๋ฅ์ด ํฅ์๋ฉ๋๋ค.
์๋ฅผ ๋ค์ด ๋ฐฐ์ด์ ๋ชจ๋ ์์๋ฅผ 10๋ฐฐ๋ก ํ์ฅํ๋ ค๋ฉด 10์ผ๋ก ๊ฐ๋ ์ฐฌ ๋ฐฐ์ด์ ๋ง๋ค ํ์๊ฐ ์์ต๋๋ค. ๊ฐ๋จํ ๋ค์์ ์์ฑํ์ญ์์ค.
my_array = np.array([1, 2, 3, 4])
scaled_array = my_array * 10 # my_array ์ ์ฒด์ ์ค์นผ๋ผ 10์ ๋ธ๋ก๋์บ์คํ
๋ํ ๋ฐ์ดํฐ๊ฐ ๋ฉ๋ชจ๋ฆฌ์ ๋ฐฐ์น๋๋ ๋ฐฉ์์ด ์ค์ํฉ๋๋ค. NumPy ๋ฐฐ์ด์ ์ฐ์๋ ๋ฉ๋ชจ๋ฆฌ ๋ธ๋ก์ ์ ์ฅ๋ฉ๋๋ค. ์ด๋ ๋ฐ์ดํฐ๋ฅผ ์์ฐจ์ ์ผ๋ก ๋์ ๋ ์ง์คํฐ์ ๋ก๋ํด์ผ ํ๋ SIMD์ ํ์์ ์ ๋๋ค. ๋ค์ฐจ์ ๋ฐ์ดํฐ๋ก ์์ ํ ๋ ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์(์: C ์คํ์ผ ํ ์ฐ์ vs. Fortran ์คํ์ผ ์ด ์ฐ์ )์ ์ดํดํ๋ ๊ฒ์ด ํนํ ๊ณ ๊ธ ์ฑ๋ฅ ํ๋์ ์ค์ํด์ง๋๋ค.
๊ฒฝ๊ณ ํ์ฅ: ๊ณ ๊ธ SIMD ๋ผ์ด๋ธ๋ฌ๋ฆฌ
NumPy๋ Python์์ ๋ฒกํฐํ๋ฅผ ์ํ ์ฒซ ๋ฒ์งธ์ด์ ๊ฐ์ฅ ์ค์ํ ๋๊ตฌ์ ๋๋ค. ๊ทธ๋ฌ๋ ํ์ค NumPy ufunc๋ฅผ ์ฌ์ฉํ์ฌ ์๊ณ ๋ฆฌ์ฆ์ ์ฝ๊ฒ ํํํ ์ ์๋ ๊ฒฝ์ฐ์๋ ์ด๋ป๊ฒ ๋ ๊น์? ์๋ง๋ ๋ณต์กํ ์กฐ๊ฑด๋ถ ๋ ผ๋ฆฌ๊ฐ ์๋ ๋ฃจํ๊ฐ ์๊ฑฐ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ฌ์ฉํ ์ ์๋ ์ฌ์ฉ์ ์ง์ ์๊ณ ๋ฆฌ์ฆ์ด ์์ ๊ฒ์ ๋๋ค. ์ด๋ฌํ ๊ฒฝ์ฐ ๊ณ ๊ธ ๋๊ตฌ๊ฐ ์ฌ์ฉ๋ฉ๋๋ค.
Numba: ์๋๋ฅผ ์ํ JIT(Just-In-Time) ์ปดํ์ผ
Numba๋ JIT(Just-In-Time) ์ปดํ์ผ๋ฌ ์ญํ ์ ํ๋ ๋๋ผ์ด ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. Python ์ฝ๋๋ฅผ ์ฝ๊ณ ๋ฐํ์์ Python ํ๊ฒฝ์ ๋ ๋์ง ์๊ณ ๋ ๊ณ ๋๋ก ์ต์ ํ๋ ๊ธฐ๊ณ ์ฝ๋๋ก ๋ณํํฉ๋๋ค. ํ์ค Python์ ์ฃผ์ ์ฝ์ ์ธ ๋ฃจํ๋ฅผ ์ต์ ํํ๋ ๋ฐ ํนํ ๋ฐ์ด๋ฉ๋๋ค.
Numba๋ฅผ ์ฌ์ฉํ๋ ๊ฐ์ฅ ์ผ๋ฐ์ ์ธ ๋ฐฉ๋ฒ์ ๋ฐ์ฝ๋ ์ดํฐ `@jit`์ ์ฌ์ฉํ๋ ๊ฒ์ ๋๋ค. NumPy์์ ๋ฒกํฐํํ๊ธฐ ์ด๋ ค์ด ์์ ๋ฅผ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ๋ฐ๋ก ์ฌ์ฉ์ ์ง์ ์๋ฎฌ๋ ์ด์ ๋ฃจํ์ ๋๋ค.
import numpy as np
from numba import jit
# NumPy์์ ๋ฒกํฐํํ๊ธฐ ์ด๋ ค์ด ๊ฐ์ ํจ์
def simulate_particles_python(positions, velocities, steps):
for _ in range(steps):
for i in range(len(positions)):
# ์ผ๋ถ ๋ณต์กํ ๋ฐ์ดํฐ ์ข
์ ๋
ผ๋ฆฌ
if positions[i] > 0:
velocities[i] -= 9.8 * 0.01
else:
velocities[i] = -velocities[i] * 0.9 # ๋นํ์ฑ ์ถฉ๋
positions[i] += velocities[i] * 0.01
return positions
# Numba JIT ๋ฐ์ฝ๋ ์ดํฐ๊ฐ ์๋ ์ ํํ ๋์ผํ ํจ์
@jit(nopython=True, fastmath=True)
def simulate_particles_numba(positions, velocities, steps):
for _ in range(steps):
for i in range(len(positions)):
if positions[i] > 0:
velocities[i] -= 9.8 * 0.01
else:
velocities[i] = -velocities[i] * 0.9
positions[i] += velocities[i] * 0.01
return positions
`@jit(nopython=True)` ๋ฐ์ฝ๋ ์ดํฐ๋ฅผ ์ถ๊ฐํ๊ธฐ๋ง ํ๋ฉด Numba์๊ฒ ์ด ํจ์๋ฅผ ๊ธฐ๊ณ ์ฝ๋๋ก ์ปดํ์ผํ๋๋ก ์ง์ํ๋ ๊ฒ์ ๋๋ค. `nopython=True` ์ธ์๋ ๋งค์ฐ ์ค์ํฉ๋๋ค. Numba๊ฐ ๋๋ฆฐ Python ์ธํฐํ๋ฆฌํฐ๋ก ๋๋์๊ฐ์ง ์๋ ์ฝ๋๋ฅผ ์์ฑํ๋๋ก ๋ณด์ฅํ๊ธฐ ๋๋ฌธ์ ๋๋ค. `fastmath=True` ํ๋๊ทธ๋ฅผ ์ฌ์ฉํ๋ฉด Numba๊ฐ ๋ ์ ํํ์ง๋ง ๋ ๋น ๋ฅธ ์ํ ์ฐ์ฐ์ ์ฌ์ฉํ ์ ์์ผ๋ฉฐ ์๋ ๋ฒกํฐํ๋ฅผ ํ์ฑํํ ์ ์์ต๋๋ค. Numba์ ์ปดํ์ผ๋ฌ๊ฐ ๋ด๋ถ ๋ฃจํ๋ฅผ ๋ถ์ํ ๋ ์กฐ๊ฑด๋ถ ๋ ผ๋ฆฌ๊ฐ ์๋ ๊ฒฝ์ฐ์๋ ์ฌ๋ฌ ์ ์๋ฅผ ํ ๋ฒ์ ์ฒ๋ฆฌํ๊ธฐ ์ํด SIMD ๋ช ๋ น์ ์๋์ผ๋ก ์์ฑํ ์ ์๋ ๊ฒฝ์ฐ๊ฐ ๋ง์ผ๋ฏ๋ก ์์ผ๋ก ์์ฑํ C ์ฝ๋์ ๊ฒฝ์ํ๊ฑฐ๋ ๋ฅ๊ฐํ๋ ์ฑ๋ฅ์ ์ป์ ์ ์์ต๋๋ค.
Cython: Python๊ณผ C/C++ ์ตํฉ
Numba๊ฐ ์ธ๊ธฐ๋ฅผ ์ป๊ธฐ ์ ์๋ Cython์ด Python ์ฝ๋ ์๋๋ฅผ ๋์ด๋ ์ฃผ์ ๋๊ตฌ์์ต๋๋ค. Cython์ C/C++ ํจ์ ํธ์ถ๊ณผ ๋ณ์ ๋ฐ ํด๋์ค ์์ฑ์ C ์ ํ ์ ์ธ์ ์ง์ํ๋ Python ์ธ์ด์ ์์ ์งํฉ์ ๋๋ค. AOT(Ahead-Of-Time) ์ปดํ์ผ๋ฌ ์ญํ ์ ํฉ๋๋ค. ์ฝ๋๋ฅผ `.pyx` ํ์ผ์ ์์ฑํ๋ฉด Cython์ด ์ด๋ฅผ C/C++ ์์ค ํ์ผ๋ก ์ปดํ์ผํ ๋ค์ ํ์ค Python ํ์ฅ ๋ชจ๋๋ก ์ปดํ์ผํฉ๋๋ค.
Cython์ ์ฃผ์ ์ฅ์ ์ ์ ๊ณตํ๋ ์ธ๋ถํ๋ ์ ์ด์ ๋๋ค. ์ ์ ์ ํ ์ ์ธ์ ์ถ๊ฐํ๋ฉด Python์ ๋์ ์ค๋ฒํค๋๋ฅผ ๋ง์ด ์ ๊ฑฐํ ์ ์์ต๋๋ค.
๊ฐ๋จํ Cython ํจ์๋ ๋ค์๊ณผ ๊ฐ์ต๋๋ค.
# 'sum_module.pyx'๋ผ๋ ํ์ผ์์
def sum_typed(long[:] arr):
cdef long total = 0
cdef int i
for i in range(arr.shape[0]):
total += arr[i]
return total
์ฌ๊ธฐ์ `cdef`๋ C ์์ค ๋ณ์(`total`, `i`)๋ฅผ ์ ์ธํ๋ ๋ฐ ์ฌ์ฉ๋๊ณ `long[:]`๋ ์ ๋ ฅ ๋ฐฐ์ด์ ์ ํ์ด ์ง์ ๋ ๋ฉ๋ชจ๋ฆฌ ๋ณด๊ธฐ๋ฅผ ์ ๊ณตํฉ๋๋ค. ์ด๋ฅผ ํตํด Cython์ ๋งค์ฐ ํจ์จ์ ์ธ C ๋ฃจํ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค. ์ ๋ฌธ๊ฐ๋ฅผ ์ํด Cython์ SIMD ๋ด์ฅ ํจ์๋ฅผ ์ง์ ํธ์ถํ๋ ๋ฉ์ปค๋์ฆ๋ ์ ๊ณตํ์ฌ ์ฑ๋ฅ์ด ์ค์ํ ์ ํ๋ฆฌ์ผ์ด์ ์ ๋ํ ์ต๊ณ ์ ์์ค์ ์ ์ด๋ฅผ ์ ๊ณตํฉ๋๋ค.
ํน์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ: ์์ฝ์์คํ ์ฟ๋ณด๊ธฐ
๊ณ ์ฑ๋ฅ Python ์์ฝ์์คํ ์ ๊ด๋ํฉ๋๋ค. NumPy, Numba ๋ฐ Cython ์ธ์๋ ๋ค์๊ณผ ๊ฐ์ ๋ค๋ฅธ ํน์ ๋๊ตฌ๊ฐ ์์ต๋๋ค.
- NumExpr: ๋ฉ๋ชจ๋ฆฌ ์ฌ์ฉ๋์ ์ต์ ํํ๊ณ ์ฌ๋ฌ ์ฝ์ด๋ฅผ ์ฌ์ฉํ์ฌ `2*a + 3*b`์ ๊ฐ์ ์์ ํ๊ฐํ์ฌ ๋๋ก๋ NumPy๋ณด๋ค ์ฑ๋ฅ์ด ๋ฐ์ด๋ ๋น ๋ฅธ ์์น ์ ํ๊ฐ๊ธฐ์ ๋๋ค.
- Pythran: NumPy๋ฅผ ์ฌ์ฉํ๋ ์ฝ๋์ ๊ฐ์ Python ์ฝ๋์ ํ์ ์งํฉ์ ๊ณ ๋๋ก ์ต์ ํ๋ C++11๋ก ๋ณํํ์ฌ ์ข ์ข ๊ณต๊ฒฉ์ ์ธ SIMD ๋ฒกํฐํ๋ฅผ ๊ฐ๋ฅํ๊ฒ ํ๋ AOT(Ahead-Of-Time) ์ปดํ์ผ๋ฌ์ ๋๋ค.
- Taichi: ํนํ ์ปดํจํฐ ๊ทธ๋ํฝ ๋ฐ ๋ฌผ๋ฆฌ ์๋ฎฌ๋ ์ด์ ์์ ๋๋ฆฌ ์ฌ์ฉ๋๋ ๊ณ ์ฑ๋ฅ ๋ณ๋ ฌ ์ปดํจํ ์ ์ํด Python์ ํฌํจ๋ ๋๋ฉ์ธ๋ณ ์ธ์ด(DSL)์ ๋๋ค.
์ ์ธ๊ณ ์ฌ์ฉ์๋ฅผ ์ํ ์ค์ ๊ณ ๋ ค ์ฌํญ ๋ฐ ๋ชจ๋ฒ ์ฌ๋ก
๊ณ ์ฑ๋ฅ ์ฝ๋๋ฅผ ์์ฑํ๋ ค๋ฉด ์ฌ๋ฐ๋ฅธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ๊ฒ ์ด์์ด ํ์ํฉ๋๋ค. ๋ค์์ ๋ณดํธ์ ์ผ๋ก ์ ์ฉ ๊ฐ๋ฅํ ๋ช ๊ฐ์ง ๋ชจ๋ฒ ์ฌ๋ก์ ๋๋ค.
SIMD ์ง์ ํ์ธ ๋ฐฉ๋ฒ
์ป๋ ์ฑ๋ฅ์ ์ฝ๋๊ฐ ์คํ๋๋ ํ๋์จ์ด์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋๋ค. ํน์ CPU์์ ์ง์ํ๋ SIMD ๋ช ๋ น ์ธํธ๋ฅผ ์๋ ๊ฒ์ด ์ ์ฉํ ๊ฒฝ์ฐ๊ฐ ๋ง์ต๋๋ค. `py-cpuinfo`์ ๊ฐ์ ํฌ๋ก์ค ํ๋ซํผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์์ต๋๋ค.
# ๋ค์์ ์ฌ์ฉํ์ฌ ์ค์น: pip install py-cpuinfo
import cpuinfo
info = cpuinfo.get_cpu_info()
supported_flags = info.get('flags', [])
print("SIMD ์ง์:")
if 'avx512f' in supported_flags:
print("- AVX-512 ์ง์๋จ")
elif 'avx2' in supported_flags:
print("- AVX2 ์ง์๋จ")
elif 'avx' in supported_flags:
print("- AVX ์ง์๋จ")
elif 'sse4_2' in supported_flags:
print("- SSE4.2 ์ง์๋จ")
else:
print("- ๊ธฐ๋ณธ SSE ์ง์ ๋๋ ์ด์ ๋ฒ์ .")
ํด๋ผ์ฐ๋ ์ปดํจํ ์ธ์คํด์ค ๋ฐ ์ฌ์ฉ์ ํ๋์จ์ด๋ ์ง์ญ์ ๋ฐ๋ผ ํฌ๊ฒ ๋ค๋ฅผ ์ ์์ผ๋ฏ๋ก ์ด๋ ์ ์ญ ์ปจํ ์คํธ์์ ๋งค์ฐ ์ค์ํฉ๋๋ค. ํ๋์จ์ด ๊ธฐ๋ฅ์ ์๋ฉด ์ฑ๋ฅ ํน์ฑ์ ์ดํดํ๊ฑฐ๋ ํน์ ์ต์ ํ๋ฅผ ํตํด ์ฝ๋๋ฅผ ์ปดํ์ผํ๋ ๋ฐ ๋์์ด ๋ ์ ์์ต๋๋ค.
๋ฐ์ดํฐ ์ ํ์ ์ค์์ฑ
SIMD ์ฐ์ฐ์ ๋ฐ์ดํฐ ์ ํ(`dtype` in NumPy)์ ๋งค์ฐ ๊ตฌ์ฒด์ ์ ๋๋ค. SIMD ๋ ์ง์คํฐ์ ๋๋น๋ ๊ณ ์ ๋์ด ์์ต๋๋ค. ์ฆ, ๋ ์์ ๋ฐ์ดํฐ ์ ํ์ ์ฌ์ฉํ๋ฉด ๋จ์ผ ๋ ์ง์คํฐ์ ๋ ๋ง์ ์์๋ฅผ ๋ง์ถ๊ณ ๋ช ๋ น์ด๋น ๋ ๋ง์ ๋ฐ์ดํฐ๋ฅผ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค.
์๋ฅผ ๋ค์ด 256๋นํธ AVX ๋ ์ง์คํฐ๋ ๋ค์์ ๋ด์ ์ ์์ต๋๋ค.
- 4๊ฐ์ 64๋นํธ ๋ถ๋ ์์์ ์ซ์(`float64` ๋๋ `double`).
- 8๊ฐ์ 32๋นํธ ๋ถ๋ ์์์ ์ซ์(`float32` ๋๋ `float`).
์ ํ๋ฆฌ์ผ์ด์ ์ ์ ๋ฐ๋ ์๊ตฌ ์ฌํญ์ด 32๋นํธ ๋ถ๋ ์์์ ์ผ๋ก ์ถฉ์กฑ๋ ์ ์๋ ๊ฒฝ์ฐ NumPy ๋ฐฐ์ด์ `dtype`์ `np.float64`(๋ง์ ์์คํ ์์ ๊ธฐ๋ณธ๊ฐ)์์ `np.float32`๋ก ๋ณ๊ฒฝํ๊ธฐ๋ง ํ๋ฉด AVX ์ง์ ํ๋์จ์ด์์ ๊ณ์ฐ ์ฒ๋ฆฌ๋์ ๋ ๋ฐฐ๋ก ๋๋ฆด ์ ์์ต๋๋ค. ๋ฌธ์ ์ ์ถฉ๋ถํ ์ ๋ฐ๋๋ฅผ ์ ๊ณตํ๋ ๊ฐ์ฅ ์์ ๋ฐ์ดํฐ ์ ํ์ ํญ์ ์ ํํ์ญ์์ค.
๋ฒกํฐํํ์ง ์๋ ๊ฒฝ์ฐ
๋ฒกํฐํ๋ ๋ง๋ณํต์น์ฝ์ด ์๋๋๋ค. ํจ๊ณผ๊ฐ ์๊ฑฐ๋ ์ญํจ๊ณผ๊ฐ ์๋ ์๋๋ฆฌ์ค๊ฐ ์์ต๋๋ค.
- ๋ฐ์ดํฐ ์ข ์ ์ ์ด ํ๋ฆ: ์์ธกํ ์ ์๊ณ ๋ฐ์ฐ ์คํ ๊ฒฝ๋ก๋ก ์ด์ด์ง๋ ๋ณต์กํ `if-elif-else` ๋ถ๊ธฐ๊ฐ ์๋ ๋ฃจํ๋ ์ปดํ์ผ๋ฌ๊ฐ ์๋์ผ๋ก ๋ฒกํฐํํ๊ธฐ๊ฐ ๋งค์ฐ ์ด๋ ต์ต๋๋ค.
- ์์ฐจ์ ์ข ์์ฑ: ํ ์์์ ๋ํ ๊ณ์ฐ์ด ์ด์ ์์์ ๊ฒฐ๊ณผ์ ๋ฐ๋ผ ๋ฌ๋ผ์ง๋ ๊ฒฝ์ฐ(์: ์ผ๋ถ ์ฌ๊ท ๊ณต์์์) ๋ฌธ์ ๋ ๋ณธ์ง์ ์ผ๋ก ์์ฐจ์ ์ด๋ฉฐ SIMD๋ก ๋ณ๋ ฌํํ ์ ์์ต๋๋ค.
- ์์ ๋ฐ์ดํฐ ์ธํธ: ๋งค์ฐ ์์ ๋ฐฐ์ด(์: 12๊ฐ ๋ฏธ๋ง์ ์์)์ ๊ฒฝ์ฐ NumPy์์ ๋ฒกํฐํ๋ ํจ์ ํธ์ถ์ ์ค์ ํ๋ ์ค๋ฒํค๋๊ฐ ๊ฐ๋จํ ์ง์ Python ๋ฃจํ์ ๋น์ฉ๋ณด๋ค ํด ์ ์์ต๋๋ค.
- ๋ถ๊ท์นํ ๋ฉ๋ชจ๋ฆฌ ์ก์ธ์ค: ์๊ณ ๋ฆฌ์ฆ์ด ์์ธกํ ์ ์๋ ํจํด์ผ๋ก ๋ฉ๋ชจ๋ฆฌ์์ ์ ํํด์ผ ํ๋ ๊ฒฝ์ฐ CPU์ ์บ์ ๋ฐ ํ๋ฆฌํ์นญ ๋ฉ์ปค๋์ฆ์ ๋ฌดํจํํ์ฌ SIMD์ ์ฃผ์ ์ด์ ์ ๋ฌดํจํํฉ๋๋ค.
์ฌ๋ก ์ฐ๊ตฌ: SIMD๋ฅผ ์ฌ์ฉํ ์ด๋ฏธ์ง ์ฒ๋ฆฌ
์ค์ฉ์ ์ธ ์์ (์ปฌ๋ฌ ์ด๋ฏธ์ง๋ฅผ ํ๋ฐฑ์ผ๋ก ๋ณํ)๋ก ์ด๋ฌํ ๊ฐ๋ ์ ๊ตฌ์ฒดํํด ๋ณด๊ฒ ์ต๋๋ค. ์ด๋ฏธ์ง๋ ์ซ์(๋์ด x ๋๋น x ์์ ์ฑ๋)์ 3D ๋ฐฐ์ด์ผ ๋ฟ์ด๋ฏ๋ก ๋ฒกํฐํ์ ์๋ฒฝํ ํ๋ณด์ ๋๋ค.
ํ๋์ ๋ํ ํ์ค ๊ณต์์ ๋ค์๊ณผ ๊ฐ์ต๋๋ค. `ํ๋ฐฑ = 0.299 * R + 0.587 * G + 0.114 * B`.
`uint8` ๋ฐ์ดํฐ ์ ํ์ผ๋ก `(1920, 1080, 3)` ๋ชจ์์ NumPy ๋ฐฐ์ด๋ก ๋ก๋๋ ์ด๋ฏธ์ง๊ฐ ์๋ค๊ณ ๊ฐ์ ํด ๋ณด๊ฒ ์ต๋๋ค.
๋ฐฉ๋ฒ 1: ์์ Python ๋ฃจํ(๋๋ฆฐ ๋ฐฉ๋ฒ)
def to_grayscale_python(image):
h, w, _ = image.shape
grayscale_image = np.zeros((h, w), dtype=np.uint8)
for r in range(h):
for c in range(w):
pixel = image[r, c]
gray_value = 0.299 * pixel[0] + 0.587 * pixel[1] + 0.114 * pixel[2]
grayscale_image[r, c] = int(gray_value)
return grayscale_image
์ฌ๊ธฐ์๋ ์ธ ๊ฐ์ ์ค์ฒฉ๋ ๋ฃจํ๊ฐ ํฌํจ๋๋ฉฐ ๊ณ ํด์๋ ์ด๋ฏธ์ง์ ๊ฒฝ์ฐ ์์ฒญ๋๊ฒ ๋๋ฆฝ๋๋ค.
๋ฐฉ๋ฒ 2: NumPy ๋ฒกํฐํ(๋น ๋ฅธ ๋ฐฉ๋ฒ)
def to_grayscale_numpy(image):
# R, G, B ์ฑ๋์ ๋ํ ๊ฐ์ค์น ์ ์
weights = np.array([0.299, 0.587, 0.114])
# ๋ง์ง๋ง ์ถ(์์ ์ฑ๋)์ ๋ฐ๋ผ ๋ด์ ์ฌ์ฉ
grayscale_image = np.dot(image[...,:3], weights).astype(np.uint8)
return grayscale_image
์ด ๋ฒ์ ์์๋ ๋ด์ ์ ์ํํฉ๋๋ค. NumPy์ `np.dot`์ ๊ณ ๋๋ก ์ต์ ํ๋์ด ์์ผ๋ฉฐ SIMD๋ฅผ ์ฌ์ฉํ์ฌ ๋ง์ ํฝ์ ์ ๋ํด R, G, B ๊ฐ์ ๋์์ ๊ณฑํ๊ณ ํฉ์ฐํฉ๋๋ค. ์ฑ๋ฅ ์ฐจ์ด๋ ๋ฐค๋ฎ์ฒ๋ผ ํด ๊ฒ์ด๋ฉฐ ์ฝ๊ฒ 100๋ฐฐ ์ด์ ํฅ์๋ ๊ฒ์ ๋๋ค.
๋ฏธ๋: SIMD์ Python์ ์งํํ๋ ํ๊ฒฝ
๊ณ ์ฑ๋ฅ Python์ ์ธ๊ณ๋ ๋์์์ด ์งํํ๊ณ ์์ต๋๋ค. ์ฌ๋ฌ ์ค๋ ๋๊ฐ Python ๋ฐ์ดํธ์ฝ๋๋ฅผ ๋ณ๋ ฌ๋ก ์คํํ๋ ๊ฒ์ ๋ฐฉ์งํ๋ ์ ๋ช ๋์ GIL(Global Interpreter Lock)์ด ๋์ ์ ๋ฐ๊ณ ์์ต๋๋ค. GIL์ ์ ํ ์ฌํญ์ผ๋ก ๋ง๋๋ ๊ฒ์ ๋ชฉํ๋ก ํ๋ ํ๋ก์ ํธ๋ ๋ณ๋ ฌ ์ฒ๋ฆฌ๋ฅผ ์ํ ์๋ก์ด ๊ธธ์ ์ด ์ ์์ต๋๋ค. ๊ทธ๋ฌ๋ SIMD๋ ์ฝ์ด ์์ค์์ ์๋ํ๋ฉฐ GIL์ ์ํฅ์ ๋ฐ์ง ์์ผ๋ฏ๋ก ์์ ์ ์ด๊ณ ๋ฏธ๋ ์งํฅ์ ์ธ ์ต์ ํ ์ ๋ต์ ๋๋ค.
ํ๋์จ์ด๊ฐ ํน์ ๊ฐ์๊ธฐ ๋ฐ ๋ ๊ฐ๋ ฅํ ๋ฒกํฐ ์ฅ์น๋ก ๋์ฑ ๋ค์ํด์ง์ ๋ฐ๋ผ ์ฑ๋ฅ์ ์ ๊ณตํ๋ฉด์ ํ๋์จ์ด ์ธ๋ถ ์ ๋ณด๋ฅผ ์ถ์ํํ๋ ๋๊ตฌ(์: NumPy ๋ฐ Numba)๊ฐ ๋์ฑ ์ค์ํด์ง ๊ฒ์ ๋๋ค. CPU ๋ด์์ SIMD์์ ํ ๋จ๊ณ ๋ ๋์๊ฐ๋ฉด ์ข ์ข GPU์ SIMT(Single Instruction, Multiple Threads)๊ฐ ๋๋ฉฐ CuPy(NVIDIA GPU์์ NumPy๋ฅผ ๋์ฒดํ๋ ๋๋กญ์ธ)์ ๊ฐ์ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ ํจ์ฌ ๋ ํฐ ๊ท๋ชจ๋ก ์ด๋ฌํ ๋์ผํ ๋ฒกํฐํ ์์น์ ์ ์ฉํฉ๋๋ค.
๊ฒฐ๋ก : ๋ฒกํฐ๋ฅผ ์์ฉํ์ญ์์ค.
์ฐ๋ฆฌ๋ CPU์ ํต์ฌ์์ Python์ ๊ณ ๊ธ ์ถ์ํ๋ก ์ด๋ํ์ต๋๋ค. ์ค์ํ ์ ์ Python์์ ๋น ๋ฅธ ์์น ์ฝ๋๋ฅผ ์์ฑํ๋ ค๋ฉด ๋ฃจํ๊ฐ ์๋ ๋ฐฐ์ด๋ก ์๊ฐํด์ผ ํ๋ค๋ ๊ฒ์ ๋๋ค. ์ด๊ฒ์ด ๋ฐ๋ก ๋ฒกํฐํ์ ๋ณธ์ง์ ๋๋ค.
์ฌ์ ์ ์์ฝํด ๋ณด๊ฒ ์ต๋๋ค.
- ๋ฌธ์ : ์์ Python ๋ฃจํ๋ ์ธํฐํ๋ฆฌํฐ ์ค๋ฒํค๋๋ก ์ธํด ์์น ์์ ์ ๋๋ฆฝ๋๋ค.
- ํ๋์จ์ด ์๋ฃจ์ : SIMD๋ฅผ ์ฌ์ฉํ๋ฉด ๋จ์ผ CPU ์ฝ์ด๊ฐ ์ฌ๋ฌ ๋ฐ์ดํฐ ํฌ์ธํธ์์ ๋์์ ๋์ผํ ์์ ์ ์ํํ ์ ์์ต๋๋ค.
- ๊ธฐ๋ณธ Python ๋๊ตฌ: NumPy๋ ๋ฒกํฐํ์ ์ด์์ผ๋ก, ์ง๊ด์ ์ธ ๋ฐฐ์ด ๊ฐ์ฒด์ ์ต์ ํ๋ SIMD ์ง์ C/Fortran ์ฝ๋๋ก ์คํ๋๋ ํ๋ถํ ufunc ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ๊ณ ๊ธ ๋๊ตฌ: NumPy์์ ์ฝ๊ฒ ํํํ ์ ์๋ ์ฌ์ฉ์ ์ง์ ์๊ณ ๋ฆฌ์ฆ์ ๊ฒฝ์ฐ Numba๋ JIT ์ปดํ์ผ์ ์ ๊ณตํ์ฌ ๋ฃจํ๋ฅผ ์๋์ผ๋ก ์ต์ ํํ๋ ๋ฐ๋ฉด Cython์ Python๊ณผ C๋ฅผ ํผํฉํ์ฌ ์ธ๋ถํ๋ ์ ์ด๋ฅผ ์ ๊ณตํฉ๋๋ค.
- ์ฌ๊ณ ๋ฐฉ์: ํจ๊ณผ์ ์ธ ์ต์ ํ์๋ ๋ฐ์ดํฐ ์ ํ, ๋ฉ๋ชจ๋ฆฌ ํจํด ๋ฐ ์์ ์ ์ ํฉํ ๋๊ตฌ๋ฅผ ์ ํํ๋ ๊ฒ์ ์ดํดํด์ผ ํฉ๋๋ค.
๋ค์์ ๋๊ท๋ชจ ์ซ์ ๋ชฉ๋ก์ ์ฒ๋ฆฌํ๊ธฐ ์ํด `for` ๋ฃจํ๋ฅผ ์์ฑํ๊ณ ์๋ค๋ฉด ์ ์ ๋ฉ์ถ๊ณ "์ด๊ฒ์ ๋ฒกํฐ ์ฐ์ฐ์ผ๋ก ํํํ ์ ์์ต๋๊น?"๋ผ๊ณ ์ง๋ฌธํ์ญ์์ค. ์ด๋ฌํ ๋ฒกํฐํ๋ ์ฌ๊ณ ๋ฐฉ์์ ์์ฉํ๋ฉด ์ต์ ํ๋์จ์ด์ ์ง์ ํ ์ฑ๋ฅ์ ์ ๊ธ ํด์ ํ๊ณ ์ฝ๋ฉํ๋ ์ ์ธ๊ณ ์ด๋์์๋ Python ์ ํ๋ฆฌ์ผ์ด์ ์ ์๋ก์ด ์์ค์ ์๋์ ํจ์จ์ฑ์ผ๋ก ๋์ด์ฌ๋ฆด ์ ์์ต๋๋ค.