Rust ๐ฆ and WebAssembly ๐ธ
์ด ๊ฐ๊ฒฐํ ์ฑ ์ ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ํจ๊ป ์ฌ์ฉํ๋ ๋ฐฉ๋ฒ์ ๋ค๋ฃน๋๋ค.
๋ฒ์ญ๋ ๋ฒ์ ์ ์ด์๋ ํ ๋ฆฌํ์คํธ๋ฅผ ์์ฑํ๊ณ ์ ํ์๋์? ๋ฒ์ญ ๋ฒ์ ์ ๋ ํฌ์งํ ๋ฆฌ๋ฅผ ํ์ธํด ์ฃผ์ธ์.
๋๊ตฌ๋ฅผ ์ํ ์ฑ ์ธ๊ฐ์?
์ด ์ฑ ์ ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ํจ๊ป ์ฌ์ฉํ์ฌ ์น์์ ๋์ํ๋ ๋น ๋ฅด๊ณ ์์ ๋ ์ฝ๋๋ฅผ ์์ฑํ๋ ๋ฐฉ๋ฒ์ ๋ํด ๊ด์ฌ์ด ์๋ ๋ถ๋ค์ ์ํด ์์ฑ๋์์ต๋๋ค. ๊ผญ ์ ๋ฌธ๊ฐ๊ฐ ๋ผ์ผํ ํ์๋ ์์ง๋ง, Rust๋ฅผ ์กฐ๊ธ์ด๋ผ๋ ์์์ผ ํ๊ณ ์๋ฐ์คํฌ๋ฆฝํธ์ HTML, CSS์ ์ต์ํ๋ฉด ๋ ์ข์ต๋๋ค.
๋ฌ์คํธ๋ฅผ ์์ง ๋ชจ๋ฅด์๋์? The Rust Programming Language ์ฑ ์ผ๋ก ์์ํด ๋ณด์ธ์.
์๋ฐ์คํฌ๋ฆฝํธ๋ HTML, CSS๋ฅผ ๋ชจ๋ฅด์๋์? MDN์์ ๋ ์์๋ณด์ธ์.
์ด ์ฑ ์ ์ฝ๋ ๋ฐฉ๋ฒ
์ ๋ฌ์คํธ๋ก ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ๋ฐ์ ํด์ผ ํ๋์? ์น์ ์ ์ฝ์ด๋ณด๋ฉด ์ข๊ณ , ๋ฐฐ๊ฒฝ์ง์๊ณผ ๋จผ์ ์น์ํด์ ธ ๋ณด๋ ๊ฒ๋ ์ข์ต๋๋ค.
์ด ํํ ๋ฆฌ์ผ์ ์ฒ์๋ถํฐ ๋๊น์ง ์ฝ๋๋ก ์์ฑ๋์ต๋๋ค. ํํ ๋ฆฌ์ผ์ ์๋ ์ฝ๋๋ฅผ ์์ฑ, ์ปดํ์ผํ๊ณ ์ง์ ์คํํด ๋ณด์ธ์. Rust์ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ๊ฐ์ด ์ฌ์ฉํด ๋ณธ ์ ์ด ์๋ค๋ฉด, ํํ ๋ฆฌ์ผ์ ํ๋ฒ ํ์ฉํด ๋ณด์ธ์!
์ฐธ์กฐ ์น์ ์ ์๋ฌด ์์๋ก ์ ๋ ํด๋ ๊ด์ฐฎ์ต๋๋ค.
๐ก ํ: ํ์ด์ง ์ต์๋จ์ ์๋ ๐ ์์ด์ฝ์ ๋๋ฅด๊ฑฐ๋
s
ํค๋ฅผ ๋๋ฌ์ ์ฑ ์ ์ฒด๋ฅผ ๊ฒ์ํด ๋ณผ ์๋ ์์ต๋๋ค.
๋ฒ์ญ๋ณธ
์ปค๋ฎค๋ํฐ ๋ฒ์ญ๋ณธ๋ ์์ผ๋ ํ๋ฒ ํ์ธํด ๋ณด์ธ์.
์ด ์ฑ ์ ๊ธฐ์ฌํ๊ธฐ
์ด ์ฑ ์ ์คํ์์ค์ ๋๋ค! ์คํ๋ฅผ ์ฐพ์ผ์ จ๋์? ๋๋ฝ๋ ๋ถ๋ถ์ด ์๋์? ํ ๋ฆฌํ์คํธ๋ฅผ ์์ฑํด ๋ณด์ธ์!
์ ๋ฌ์คํธ๋ก ์น์ด์ ๋ธ๋ฆฌ ๊ฐ๋ฐ์ ํด์ผ ํ๋์?
์ ๋ ๋ฒจ ์ปจํธ๋กค๊ณผ ๊ณ ๋ ๋ฒจ ๊ฐ๋ฐ์ ๊ฒฝํ
์๋ฐ์คํฌ๋ฆฝํธ ์น ์ฑ๋ค์ ์์ ๋ ์ฑ๋ฅ์ ํ๋ณดํ๊ณ ์ ์งํ๋ ๋ฐ ์ด๋ ค์์ ๊ฒช์ต๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ๋์ ํ์ ์์คํ ๊ณผ ๊ฐ๋น์ง ์ฝ๋ ์ ์ ์ ์ ์ค๋จํ๋ ๊ฒ๋ง์ผ๋ก๋ ํฌ๊ฒ ๋์์ด ๋์ง ์์ต๋๋ค. ๋ณด๊ธฐ์๋ ์์ ๋ด์ฉ์ ์ฝ๋๋ฅผ ์์ ํ๋๋ผ๋ JIT์ ์น๋ช ์ ์ธ ์ฝ๋๋ฅผ ์์ฑํ๋ค๋ฉด ๋๋ผ๋งํฑํ ์ฑ๋ฅ ์ ํ๋ฅผ ์ผ์ผํฌ ์๋ ์์ต๋๋ค.
๋ฌ์คํธ๋ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ๋ณ๋ค๊ฒ ๋ง๋๋ ๋น๊ฒฐ์ ์ ์ธ ๊ฐ๋น์ง ์ฝ๋ ์ ์ค๋จ์ผ๋ก๋ถํฐ ์์ ๋ก์ธ ๋ฟ ์๋๋ผ, ํ๋ก๊ทธ๋๋จธ๋ค์ด ๊ฐ์ ์ง์ (indirection)๊ณผ ๋จ์ผํ(monomorphization), ๋ฉ๋ชจ๋ฆฌ ๋ ์ด์์์ ์ปจํธ๋กคํ ์ ์๋๋ก ํจ์ผ๋ก ์ ๋ ๋ฒจ ์ปจํธ๋กค๊ณผ ์์ ๋ ์ฑ๋ฅ์ ์ ๊ณตํฉ๋๋ค.
์์ .wasm
์ฌ์ด์ฆ
.wasm
ํ์ผ์ด ๋คํธ์ํฌ๋ก ์ ์ก๋ผ์ผ ํ๋ ์ ์ ๊ณ ๋ คํ๋ฉด ์ฝ๋์ ํ์ผ ์ฌ์ด์ฆ๋ ๋งค์ฐ ์ค์ํฉ๋๋ค. ๋ฌ์คํธ๋ ๋ฐํ์์ด ์๊ณ ๊ฐ๋น์ง ์ฝ๋ ํฐ์ ๊ฐ์ ๋ถํ์ํ ์์๊ฐ ์์ด .wasm
์ฌ์ด์ฆ๋ฅผ ํจ๊ณผ์ ์ผ๋ก ์ค์ผ ์ ์๋๋ก ํด์ฃผ๊ณ , ์ค์ ๋ก ์ฌ์ฉํ์ง ์๋ ํจ์๋ค์ (๋น๋ํ๋ ์ฝ๋ ํ์ผ์์) ์ ์ธ์์ผ์ค๋๋ค.
์ด๋ ๊ฒ ์ฝ๋ ํ์ผ ์ฌ์ด์ฆ์ ๊ด์ ์์ ์ค์ ๋ก ์ฌ์ฉํ๋ ๊ธฐ๋ฅ๋ง ์ถ๋ ฅ๋๋ ์ฝ๋์ ํฌํจํ ์ ์๊ฒ ๋ฉ๋๋ค.
๋ชจ๋ ๋ด์ฉ์ ๋ค์ ์์ฑํ์ง ๋ง์ ์ฃผ์ธ์
๊ธฐ์กด์ ์กด์ฌํ๋ ์ฝ๋ ๋ฒ ์ด์ค๋ฅผ ์ง์ธ ํ์๋ ์์ต๋๋ค. ๋ฐ๋ก ํจ๊ณผ๋ฅผ ๋ณผ ์ ์๋๋ก ์ฑ๋ฅ ๊ฐ์ ์ด ์ค์ํ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์๋ค์ ๋จผ์ ๋ฌ์คํธ๋ก ์ฎ๊ธฐ๋ ๊ฒ์ผ๋ก ์์ํด ๋ณผ ์๋ ์์ต๋๋ค. ์ํ๋ค๋ฉด ๊ฑฐ๊ธฐ์ ๋ฉ์ถฐ๋ ๊ด์ฐฎ์ต๋๋ค.
๋ค๋ฅธ ํด๊ณผ ์ ์๋ํฉ๋๋ค
๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ๋ ๊ธฐ์กด์ ์กด์ฌํ๋ ์๋ฐ์คํฌ๋ฆฝํธ ํด๋ค๊ณผ ํจ๊ป ๊ฐ์ฅ ์ ์๋ํฉ๋๋ค. ์น์ด์ ๋ธ๋ฆฌ๋ ECMAScript ๋ชจ๋์ ์ง์ํ๊ณ , ๊ฐ๋ฐ ํ๊ฒฝ์์ npm๊ณผ webpack์ฒ๋ผ ๊ธฐ์กด์ ์ฌ๋๋ฐ๋ ํด๋ค์ ๊ณ์ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
๊ธฐ๋ํ ์ ์๋ ๊ฐ์ ๋ค
๋ฌ์คํธ๋ ๋ค์๊ณผ ๊ฐ์ด ๊ฐ๋ฐ์๋ค์ด ์ผ๋ฐ์ ์ผ๋ก ๊ธฐ๋ํ๊ฒ ๋๋ ํ๋์ ์ธ ํธ์ ๊ธฐ๋ฅ์ ๊ฐ์ถ๊ณ ์์ต๋๋ค.
-
cargo
๋ก ๊ฐ๋ ฅํ ํจํค์ง ๊ด๋ฆฌํ๊ธฐ -
(์ถ๊ฐ ๋น์ฉ ์์ด) ์ดํดํ๊ธฐ ์ฌ์ด ์ฝ๋๋ฅผ ์ธ ์ ์๊ฒ ํด์ฃผ๋ ์ถ์ํ
-
ํ์ํ๋ ์ปค๋ฎค๋ํฐ! ๐
๋ฐฐ๊ฒฝ์ง์
์ด ์น์ ์ ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ ๊ฐ๋ฐ์ ์์ํ ๋ ํ์ํ ๋ฐฐ๊ฒฝ์ง์์ ์ค๋ช ํด์ค๋๋ค.
์น์ด์ ๋ธ๋ฆฌ๊ฐ ๋ญ๊ฐ์?
์น์ด์ ๋ธ๋ฆฌ๋ ํฌ๊ด์ ์ธ ์ฌ์์ ๊ฐ์ง๊ณ ์๋ ๊ฐ๋จํ ๊ธฐ๊ณ ๋ชจ๋ธ์ด์ ์คํ ๊ฐ๋ฅํ ํฌ๋งท์ ๋๋ค. ํด๋ ๊ฐ๋ฅํ๊ณ , ๊ฐ๋ฒผ์ฐ๋ฉฐ ๊ฑฐ์ ๋ค์ดํฐ๋ธ ํ๋ก๊ทธ๋จ๊ณผ ๊ฐ์ ์๋๋ก ์คํ๋ ์ ์๋๋ก ์ค๊ณ๋์ต๋๋ค.
ํ๋ก๊ทธ๋๋ฐ ์ธ์ด๋ก์จ, ์น์ด์ ๋ธ๋ฆฌ๋ ๊ฐ์ ๊ตฌ์กฐ๋ฅผ ๋ํ๋ด๋๋ฐ, ๋ ๊ฐ์ง ๋ค๋ฅธ ํฌ๋งท์ผ๋ก ๊ตฌ์ฑ๋ผ ์์ต๋๋ค.
-
("WebAssembly Text" ์์ ์ด๋ฆ์ด ์ ๋๋)
.wat
ํ ์คํธ ํฌ๋งท์ S-expressions ๊ตฌ์กฐ๋ฅผ ์ฌ์ฉํ๊ณ Scheme์ด๋ Clojure์ ๊ฐ์ Lisp ๊ณ์ด ์ธ์ด์ ์ ์ฌ์ ๋ค์ ๊ณต์ ํฉ๋๋ค. -
.wasm
๋ฐ์ด๋๋ฆฌ ํฌ๋งท์ ๋ ์ ๋ ๋ฒจ์ด๋ฉด์ ์น์ด์ ๋ธ๋ฆฌ ๊ฐ์ ๋จธ์ ์์ ๋ฐ๋ก ์ฌ์ฉ๋๋๋ก ์๋๋์ต๋๋ค. ๊ฐ๋ ์ ์ผ๋ก ELF ์ Mach-0์ ๋น์ทํฉ๋๋ค.
์ฐธ๊ณ ์๋ฃ๋ก, wat
์ธ์ด๋ก ์์ฑ๋ ํฉํ ๋ฆฌ์ผ ํจ์๋ฅผ ํ์ธํด ๋ณด์ธ์.
(module
(func $fac (param f64) (result f64)
local.get 0
f64.const 1
f64.lt
if (result f64)
f64.const 1
else
local.get 0
local.get 0
f64.const 1
f64.sub
call $fac
f64.mul
end)
(export "fac" (func $fac)))
์ ์์ ๊ฐ .wasm
ํ์ผ๋ก๋ ์ด๋ป๊ฒ ๋ณด์ผ์ง ๊ถ๊ธํ๋ค๋ฉด, wat2wasm ๋ฐ๋ชจ ์น์ฌ์ดํธ๋ฅผ ์ด์ฉํด ๋ณด์ธ์.
์ ํ ๋ฉ๋ชจ๋ฆฌ
์น์ด์ ๋ธ๋ฆฌ๋ ๋งค์ฐ ๊ฐ๋จํ ๋ฉ๋ชจ๋ฆฌ ๋ชจ๋ธ์ ๊ฐ์ง๊ณ ์๊ณ , ํ ์น์ด์ ๋ธ๋ฆฌ ๋ชจ๋์ ํ๋์ "์ ํ ๋ฉ๋ชจ๋ฆฌ" ์ ์ ๊ทผํ ์ ์์ต๋๋ค.
์ด ๋ฉ๋ชจ๋ฆฌ๋ ํ์ด์ง ์ฌ์ด์ฆ (64K)์ ๊ณฑ๋งํผ ์ปค์ง ์ ์์ผ๋ฉฐ ์ด ์ฌ์ด์ฆ๋ ์ค์ด๋ค ์ ์์ต๋๋ค.
์น์์๋ง ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์๋์?
ํ์ฌ๋ก๋ ์๋ฐ์คํฌ๋ฆฝํธ ์น ์ปค๋ฎค๋ํฐ์์ ์ฃผ๋ก ์ฃผ๋ชฉ์ ๋ฐ๊ณ ์์ง๋ง, ์น์ด์ ๋ธ๋ฆฌ๋ ํน์ ์คํ ํ๊ฒฝ์ ํ์๋ก ํ์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก, ์น์ด์ ๋ธ๋ฆฌ๊ฐ ๋ฏธ๋์ ๋ค์ํ ๋งฅ๋ฝ์์ ์ฌ์ฉํ ์ ์๋ "ํด๋ ๊ฐ๋ฅํ ์คํํ ์ ์๋" ํฌ๋งท์ด๋ผ๊ณ ์ฌ๊ฒจ์ง์๋ ์์ต๋๋ค. ํ์ง๋ง ์ค๋๋ ํ์ฌ ์์ ์์๋ ์น์ด์ ๋ธ๋ฆฌ๊ฐ (์น๊ณผ Node.js์ ํฌํจํ) ๋ค์ํ ํํ๋ก ์กด์ฌํ๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ํจ๊ป ์ฃผ๋ก ์ธ๊ธ๋ฉ๋๋ค.
ํํ ๋ฆฌ์ผ: Conway's Game of Life
์ด ํํ ๋ฆฌ์ผ์ Conway's Game of Life๋ฅผ ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ๋ก ๊ตฌํํ๋ ๋ด์ฉ์ ๋ค๋ฃน๋๋ค.
๋๊ตฌ๋ฅผ ์ํ ํํ ๋ฆฌ์ผ์ธ๊ฐ์?
์ด ํํ ๋ฆฌ์ผ์ ์ด๋ฏธ ๊ธฐ์ด์ ์ธ ๋ฌ์คํธ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ๋ฐฐ์ ๊ณ , ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ, ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ๊ฐ์ด ์ฌ์ฉํ๊ณ ์ถ์ด ํ๋ ์ฌ๋๋ค์ ์ํด ์์ฑ๋์ต๋๋ค.
์ํํ ์งํ์ ์ํด ๊ธฐ์ด์ ์ธ ๋ฌ์คํธ, ์๋ฐ์คํฌ๋ฆฝํธ, HTML ์ฝ๋๋ฅผ ๋ฌธ์ ์์ด ์์ฑํ ์ ์์ด์ผ ํฉ๋๋ค. ํ์ง๋ง ์ ๋ฌธ๊ฐ๊ฐ ๋ผ์ผ ํ ํ์๋ ์ ํ ์์ต๋๋ค.
๋ฌด์์ ๋ฐฐ์ฐ๊ฒ ๋๋์?
-
์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ปดํ์ผํ ์ ์๋๋ก ๋ฌ์คํธ ํด์ฒด์ธ์ ์ค์ ํ๋ ๋ฒ.
-
๋ฌ์คํธ, ์น์ด์ ๋ธ๋ฆฌ, ์๋ฐ์คํฌ๋ฆฝํธ, HTML, CSS์ผ๋ก ๋ค์ธ์ด ํ๋ก๊ทธ๋จ์ ๊ฐ๋ฐํ ์ ์๋ ์ํฌํ๋ก์ฐ.
-
๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ, ๊ทธ๋ฆฌ๊ณ ์๋ฐ์คํฌ๋ฆฝํธ์ ๊ฐ์ ์ ๋ชจ๋ ์ด๋ฆฌ๋๋ก API๋ฅผ ์ค๊ณํ๋ ๋ฐฉ๋ฒ.
-
๋ฌ์คํธ ์ฝ๋์์ ์ปดํ์ผ๋ ์น์ด์ ๋ธ๋ฆฌ ๋ชจ๋์ ๋๋ฒ๊น ํ๋ ๋ฐฉ๋ฒ.
-
๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ ํ๋ก๊ทธ๋จ์ ๋ ๋น ๋ฅด๊ฒ ๋ง๋ค๊ธฐ ์ํด ํ์ ํ๋กํ์ผ๋งํ๋ ๋ฐฉ๋ฒ.
-
.wasm
๋ฐ์ด๋๋ฆฌ๋ฅผ ๋ ์๊ณ ๋น ๋ฅด๊ฒ ๋ง๋ค์ด ๋คํธ์ํฌ๋ฅผ ํตํ ๋ค์ด๋ก๋๊ฐ ๋ ์ํํ ์ ์๋๋ก ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ ํ๋ก๊ทธ๋จ์ ์ฌ์ด์ฆ ํ๋กํ์ผ๋งํ๋ ๋ฐฉ๋ฒ.
์ ์
์ด ์น์ ์์๋ Rust ํ๋ก๊ทธ๋จ๋ค์ ์น์ด์ ๋ธ๋ฆฌ๋ก ์ปดํ์ผํ๊ณ ์๋ฐ์คํฌ๋ฆฝํธ ํ๊ฒฝ๊ณผ ํตํฉ์ํค๋ ๋ฐฉ๋ฒ์ ๋ํด ๋ค๋ฃจ์ด ๋ณด๊ฒ ์ต๋๋ค.
๋ฌ์คํธ ํด์ฒด์ธ
์งํ์ ์ํด rustup
, rustc
, cargo
๋ฅผ ํฌํจํ ์คํ ๋ค๋ Rust ํด์ฒด์ธ์ด ํ์ํฉ๋๋ค.
๋ฌ์คํธ ํด์ฒด์ธ์ ์ค์นํ๋ ค๋ฉด ์ด ์ง์นจ์ ๋ฐ๋ผ์ฃผ์ธ์.
๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ ๊ฐ๋ฐ ๊ฒฝํ์ด stable ๋ฒ์ ์ ๋ฌ์คํธ์ ํฌํจ๋ ๋งํผ ์์ ํ๋๊ณ ์๊ธฐ ๋๋ฌธ์ ์ด๋ค ์คํ์ ๊ธฐ๋ฅ flag๋ ์๊ตฌ๋์ง ์์ต๋๋ค. ํ์ง๋ง Rust 1.30์ด๋ ๊ทธ ์ดํ ๋ฒ์ ์ด ์๊ตฌ๋ฉ๋๋ค.
wasm-pack
wasm-pack
์ ๋ฌ์คํธ๋ก ์์ฑํ ์น์ด์
๋ธ๋ฆฌ๋ฅผ ๊ฐ๋ฐ, ํ
์คํ
, ๋ฐฐํฌํ๋๋ก ๋์์ฃผ๋ ๋ง๋ฅ ํด์
๋๋ค.
์ฌ๊ธฐ์ wasm-pack
๋ค์ด๋ก๋ ํด๋ณด์ธ์!
cargo-generate
์ด ๋ช
๋ น์ด๋ก cargo-generate
๋ฅผ ์ค์นํด ๋ณด์ธ์:
cargo install cargo-generate
npm
npm
์ ์๋ฐ์คํฌ๋ฆฝํธ์ ํจ๊ป ์ฌ์ฉ๋๋ ํจํค์ง ๋งค๋์ ์
๋๋ค. ์ด ์ฑ
์ ์งํํ๋ฉด์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฒ๋ค๋ฌ์ ๊ฐ๋ฐ ์๋ฒ๋ฅผ ์ค์นํ๊ณ ๊ตฌ๋ํ๋ ๋ฐ ์ฌ์ฉ๋ ์์ ์
๋๋ค. ์ด ํํ ๋ฆฌ์ผ ๋์์๋ ์ปดํ์ผ๋ .wasm
์ npm
๋ ์ง์คํธ๋ฆฌ์ ๋ฐฐํฌํด ๋ด
๋๋ค.
npm
์ ์ค์นํ๋ ค๋ฉด ์ด ์ง์นจ์ ๋ฐ๋ผ์ฃผ์ธ์.
์ด๋ฏธ npm
์ด ์ค์น๋ผ ์๋ค๋ฉด, ์ด ๋ช
๋ น์ด๋ฅผ ํตํด ์ต์ ๋ฒ์ ์ผ๋ก ์
๋ฐ์ดํธ๋ผ ์๋์ง ํ์ธํด ์ฃผ์ธ์:
npm install npm@latest -g
Hello, World!
์ด ์น์ ์์๋ ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ ํ๋ก๊ทธ๋จ์ ์ฒ์ ์คํํ๋ ๊ณผ์ ์ ๋ค๋ฃน๋๋ค. "Hello, World!" ๋ฉ์ธ์ง๋ฅผ ๋ณด์ฌ์ฃผ๋ ์นํ์ด์ง๋ฅผ ๋ง๋ค์ด๋ด ์๋ค.
์์ํ๊ธฐ ์ ์ ์ ์ ๊ฐ์ด๋๋ฅผ ์ฝ๊ณ ์ ๋ฐ๋ผ์๋์ง ํ ๋ฒ ๋ ํ์ธํด ์ฃผ์ธ์.
ํ๋ก์ ํธ ํ ํ๋ฆฟ ํด๋ก ํ๊ธฐ
์ด ํ๋ก์ ํธ ํ ํ๋ฆฟ์ ํฉ๋ฆฌ์ ์ธ ๊ธฐ๋ณธ ์ค์ ์ผ๋ก ๊ตฌ์ฑ๋ผ ์์ต๋๋ค. ์ด ํ ํ๋ฆฟ์ ํ์ฉํด์ ์น์ผ๋ก ์ฝ๋๋ฅผ ๋น ๋ฅด๊ฒ ๊ฐ๋ฐ, ํตํฉ ๋ฐ ํจํค์งํ ์ ์์ต๋๋ค.
์ด ๋ช ๋ น์ด๋ก ํ๋ก์ ํธ๋ฅผ ํด๋ก ํด๋ณด์ธ์:
cargo generate --git https://github.com/rustwasm/wasm-pack-template
์ ํ๋ก์ ํธ ์ด๋ฆ์ ์ด๋ป๊ฒ ์ง์์ง ์ ๋ ฅํ๊ฒ ๋ฉ๋๋ค. "wasm-game-of-life" ์ด๋ผ๋ ์ด๋ฆ์ ์ฌ์ฉํฉ์๋ค.
wasm-game-of-life
์ด๋ป๊ฒ ๊ตฌ์ฑ๋ผ ์๋์?
์๋ก ์์ฑํ wasm-game-of-life
ํ๋ก์ ํธ๋ฅผ ์ด์ด๋ณด๋๋ก ํฉ์๋ค.
cd wasm-game-of-life
๊ทธ๋ค์ ์์ ์ด๋ค ํ์ผ์ด ๋ด๊ฒจ์๋์ง ํ์ธํด ๋ด ์๋ค:
wasm-game-of-life/
โโโ Cargo.toml
โโโ LICENSE_APACHE
โโโ LICENSE_MIT
โโโ README.md
โโโ src
โโโ lib.rs
โโโ utils.rs
๋ช ๊ฐ์ง ํ์ผ์ ๋ ์์ธํ ์ดํด๋ณผ๊น์?
wasm-game-of-life/Cargo.toml
Cargo.toml
ํ์ผ์ ๋ฌ์คํธ์ ํจํค์ง ๋งค๋์ ์ด์ ๋น๋ ํด์ธ cargo
์ ํจ๊ป ์ฌ์ฉ๋๋๋ฐ, ์์กด์ฑ๊ณผ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ์ง์ ํ๋ ์ญํ ์ ํฉ๋๋ค. ์ด ํ์ผ์ wasm-bindgen
์์กด์ฑ๊ณผ ํจ๊ป wasm
๋ผ์ด๋ธ๋ฌ๋ฆฌ ์์ฑ์ ์ฌ์ฉ๋ ์ ์๋๋ก ์ฌ๋ฐ๋ฅด๊ฒ ์ค์ ๋ crate-type
์ด ํจ๊ป ์ฌ์ ์ ๋ฏธ๋ฆฌ ํฌํจ๋ผ ์์ต๋๋ค. ๋ช ๊ฐ์ง ํ์๊ฐ ์๋ ์์กด์ฑ์ ๋์ค์ ์ดํด๋ณด๊ฒ ์ต๋๋ค.
wasm-game-of-life/src/lib.rs
src/lib.rs
ํ์ผ์ ์น์ด์
๋ธ๋ฆฌ๋ก ์ปดํ์ผํ๋ ๋ฌ์คํธ ํฌ๋ ์ดํธ์ ํต์ฌ ์ฝ๋์
๋๋ค. wasm-bindgen
์ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ์กฐ์ํ๊ธฐ ์ํด ์ฌ์ฉํ๊ณ , window.alert
์๋ฐ์คํฌ๋ฆฝํธ ํจ์๋ฅผ ๋ถ๋ฌ์จ ๋ค์์ greet
๋ฌ์คํธ ํจ์๋ฅผ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋ณด๋
๋๋ค. ์ด๋ ๊ฒ "Hello World" alert ๋ฉ์ธ์ง๋ฅผ ํ์์ํฌ ์ ์๊ฒ ๋ฉ๋๋ค.
#![allow(unused)] fn main() { mod utils; use wasm_bindgen::prelude::*; // `wee_alloc` ๊ธฐ๋ฅ์ด ํ์ฑํ๋ผ ์์ผ๋ฉด, `wee-alloc`๋ฅผ ์ ์ญ ํ ๋น์๋ก ์ฌ์ฉํฉ๋๋ค. #[cfg(feature = "wee_alloc")] #[global_allocator] static ALLOC: wee_alloc::WeeAlloc = wee_alloc::WeeAlloc::INIT; #[wasm_bindgen] extern { fn alert(s: &str); } #[wasm_bindgen] pub fn greet() { alert("Hello, wasm-game-of-life!"); } }
wasm-game-of-life/src/utils.rs
src/utils.rs
๋ชจ๋์ ์์ฃผ ์ฌ์ฉ๋๋ ์ ํธ๋ฆฌํฐ๋ฅผ ์ ๊ณตํ๋๋ฐ, ์ด ์ ํธ๋ฆฌํฐ๊ฐ ์น์ด์
๋ธ๋ฆฌ๋ก ์ปดํ์ผ๋ ๋ฌ์คํธ ์ฝ๋ ์์
์ ๋ ์ฝ๊ฒ ํ ์ ์๋๋ก ๋์์ค๋๋ค. wasm ์ฝ๋ ๋๋ฒ๊น
ํ๊ธฐ์ ๊ฐ์ ํํ ๋ฆฌ์ผ ํ๋ฐ ์น์
์์ ์ด๋ฌํ ์ ํธ๋ฆฌํฐ๋ค์ ๋ ์์ธํ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ํ์ฌ ์์ ์์๋ ์ด ํ์ผ์ ๋ฌด์ํด๋ ๊ด์ฐฎ์ต๋๋ค.
ํ๋ก์ ํธ ๋น๋ํ๊ธฐ
wasm-pack
์ ์ฌ์ฉํ์ฌ ๋ค์ ๋น๋ ๊ณผ์ ์ ์๋ํํ๊ฒ ๋ฉ๋๋ค:
- ๋ฌ์คํธ 1.30๋ ๊ทธ ์ดํ ๋ฒ์ ์ ์ฌ์ฉํ๊ณ ์๋์ง,
wasm32-unknown-unknown
ํ๊ฒ์ดrustup
์ ํตํด ์ค์น๋ผ ์๋์ง ํ์ธํฉ๋๋ค. cargo
๋ฅผ ์ฌ์ฉํ์ฌ ๋ฌ์คํธ ์์ค ์ฝ๋๋ฅผ ์น์ด์ ๋ธ๋ฆฌ.wasm
๋ฐ์ด๋๋ฆฌ๋ก ์ปดํ์ผํฉ๋๋ค.wasm-bindgen
์ ์ฌ์ฉํ์ฌ ๋ฌ์คํธ๋ก ์์ฑํ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ฌ์ฉํ ์ ์๋๋ก ์๋ฐ์คํฌ๋ฆฝํธ API๋ฅผ ์์ฑํฉ๋๋ค.
์ ์์ ์ ์์ํ๋ ค๋ฉด, ํ๋ก์ ํธ ๊ฒฝ๋ก์์ ๋ค์ ๋ช ๋ น์ด๋ฅผ ์คํํด ์ฃผ์ธ์:
wasm-pack build
๋น๋๊ฐ ์๋ฃ๋๋ฉด ๊ฒฐ๊ณผ๋ฌผ์ pkg
๊ฒฝ๋ก์์ ํ์ธํด ๋ณด์ธ์. ๋ค์ ๋ด์ฉ๋ฌผ์ ํ์ธํ ์ ์์ต๋๋ค:
pkg/
โโโ package.json
โโโ README.md
โโโ wasm_game_of_life_bg.wasm
โโโ wasm_game_of_life.d.ts
โโโ wasm_game_of_life.js
README.md
ํ์ผ์ด ๋ฉ์ธ ํ๋ก์ ํธ์์ ๋ณต์ฌ๋์ง๋ง ๋๋จธ์ง ํ์ผ๋ค์ ์์ ํ ์๋ก ์์ฑ๋ ๋ถ๋ถ์ ํ์ธํ ์ ์์ต๋๋ค.
wasm-game-of-life/pkg/wasm_game_of_life_bg.wasm
์ด๋ฌํ .wasm
ํ์ผ์ ๋ฌ์คํธ ์ปดํ์ผ๋ฌ๊ฐ ๋ฌ์คํธ ์์ค ์ฝ๋๋ก ์์ฑํ ๋ฐ์ด๋๋ฆฌ ํ์ผ์
๋๋ค. ์ด ํ์ผ์ wasm ํ์์ผ๋ก ์ปดํ์ผ๋ ๋ชจ๋ ๋ฌ์คํธ ํจ์๋ค๊ณผ ๋ฐ์ดํฐ๋ก ๊ตฌ์ฑ๋ผ ์์ต๋๋ค. ๋ณํ๋ "greet" ํจ์๊ฐ ์๊ฐ ๋ ์ ์์ต๋๋ค.
wasm-game-of-life/pkg/wasm_game_of_life.js
์ด๋ฌํ .js
ํ์ผ์ wasm-bindgen
์ ์ํด ์์ฑ๋ฉ๋๋ค. DOM๊ณผ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์๋ฅผ ๋ฌ์คํธ์์ ํธ์ถํ ์ ์๊ฒ ํด์ฃผ๊ณ , ์๋ฐ์คํฌ๋ฆฝํธ ํ๊ฒฝ์์๋ ์น์ด์
๋ธ๋ฆฌ ํจ์๋ฅผ ํธ์ถํ ์ ์๋๋ก ๋์์ฃผ๋ ์ ์ฉํ API๋ฅผ ๋
ธ์ถ์์ผ์ค๋๋ค. ์๋ฅผ ๋ค์ด์, greet
์ด๋ผ๋ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์๋ฅผ ํตํด ์น์ด์
๋ธ๋ฆฌ์ ํด๋นํ๋ ํจ์๋ฅผ ํธ์ถํ ์ ์์ต๋๋ค. ํ์ฌ ์์ ์์๋ ์ด๋ฌํ ๋ฐ์ธ๋ฉ(bindings glue)๋ค์ด ํฐ ์ญํ ์ ํ์ง๋ ์์ง๋ง, ๋ ๋ณต์กํ ๊ฐ๋ค์ ์น์ด์
๋ธ๋ฆฌ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฌ์ด์์ ์ฃผ๊ณ ๋ฐ์ ๋ ์ ๋ง ๋์์ด ๋ง์ด ๋ฉ๋๋ค.
import * as wasm from './wasm_game_of_life_bg';
// ...
export function greet() {
return wasm.greet();
}
wasm-game-of-life/pkg/wasm_game_of_life.d.ts
.d.ts
ํ์ผ์ ์์ฑํ ์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ๊ณผ ํจ๊ป ์ฌ์ฉํ ์ ์๋ TypeScript ํ์
์ ์๋ฅผ ํฌํจํฉ๋๋ค. TypeScript๋ฅผ ์ฌ์ฉํ๋ค๋ฉด ์ ์ ํ์
์ ์๋ฅผ ํตํด IDE๊ฐ ์ ๊ณตํ๋ ์๋ ์์ฑ๊ณผ ๊ฐ์ ๊ธฐ๋ฅ์ ์ฌ์ฉํด์ ๋ ์ฝ๊ฒ ์น์ด์
๋ธ๋ฆฌ ํจ์๋ฅผ ํธ์ถํ ์ ์๊ฒ ๋ฉ๋๋ค. TypeScript๋ฅผ ์ฌ์ฉํ์ง ์๋๋ค๋ฉด, ์ด ํ์ผ์ ๋ฌด์ํด๋ ๊ด์ฐฎ์ต๋๋ค.
export function greet(): void;
wasm-game-of-life/pkg/package.json
package.json
ํ์ผ์ ์์ฑ๋ ์๋ฐ์คํฌ๋ฆฝํธ์ ์น์ด์
๋ธ๋ฆฌ ํจํค์ง์ ๋ํ ๋ฉํ๋ฐ์ดํฐ๋ฅผ ํฌํจํฉ๋๋ค. ์ด๋ฐ ๋ฉํ๋ฐ์ดํฐ๋ npm๊ณผ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฒ๋ค๋ฌ๊ฐ ์ฌ๋ฌ ํจํค์ง๋ค์ ์ข
์์ฑ, ํจํค์ง ์ด๋ฆ, ๋ฒ์ ๋ฑ์ ๊ฒฐ์ ํ ๋ ์ฌ์ฉ๋ฉ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ํด๋ง๊ณผ ํจ๊ป ์ฌ์ฉํ๊ณ npm์ ์น์ด์
๋ธ๋ฆฌ ํจํค์ง๋ฅผ ๋ฐฐํฌํ ๋ ์ ์ฉํฉ๋๋ค.
{
"name": "wasm-game-of-life",
"collaborators": [
"Your Name <your.email@example.com>"
],
"description": null,
"version": "0.1.0",
"license": null,
"repository": null,
"files": [
"wasm_game_of_life_bg.wasm",
"wasm_game_of_life.d.ts"
],
"main": "wasm_game_of_life.js",
"types": "wasm_game_of_life.d.ts"
}
์น ํ์ด์ง์ ํฌํจ์ํค๊ธฐ
wasm-game-of-life
ํจํค์ง๋ฅผ ์นํ์ด์ง์ ํฌํจ์ํค๊ณ ์น ํ๊ฒฝ์์ ์๋์ํค๊ธฐ ์ํด,
create-wasm-app
์๋ฐ์คํฌ๋ฆฝํธ ํ๋ก์ ํธ ํ
ํ๋ฆฟ ์ ์ฌ์ฉํด ๋ด
์๋ค.
๋ค์ ๋ช
๋ น์ด๋ฅผ wasm-game-of-life
๊ฒฝ๋ก ๋ด๋ถ์์ ์คํํด ์ฃผ์ธ์:
npm init wasm-app www
์๋กญ๊ฒ ์์ฑ๋ ๋ด๋ถ ๊ฒฝ๋ก์ธ wasm-game-of-life/www
๋ ๋ค์ ํ์ผ๋ค์ ํฌํจํฉ๋๋ค:
wasm-game-of-life/www/
โโโ bootstrap.js
โโโ index.html
โโโ index.js
โโโ LICENSE-APACHE
โโโ LICENSE-MIT
โโโ package.json
โโโ README.md
โโโ webpack.config.js
ํ ๋ฒ ๋ ์์ฑ๋ ํ์ผ๋ค์ ์์ธํ ์ดํด๋ด ์๋ค.
wasm-game-of-life/www/package.json
์ด package.json
ํ์ผ์ webpack
๊ณผ webpack-dev-server
์ข
์์ฑ๋ค๋ก ๋ฏธ๋ฆฌ ์
์
์ด ๋์ด ์๊ณ , npm ์ ๋ฐฐํฌ๋ผ ์๋ wasm-pack-template
์ ์ด๊ธฐ ๋ฒ์ ์ธ hello-wasm-pack
ํจํค์ง ๋ํ ์ข
์์ฑ์ผ๋ก ํฌํจํ๊ณ ์์ต๋๋ค.
wasm-game-of-life/www/webpack.config.js
์ด ํ์ผ์ webpack๊ณผ ๋ก์ปฌ ๊ฐ๋ฐ ์๋ฒ๋ฅผ ์ค์ ํฉ๋๋ค. ๋ฏธ๋ฆฌ ์ ์ ์์ ์ด ๋ผ ์๋๋ฐ, webpack๊ณผ ๋ก์ปฌ ๊ฐ๋ฐ ์๋ฒ๋ฅผ ์ฌ์ฉํ ๋์๋ ์ ํ ๋ฐ๋ก ์์ ํ ํ์๊ฐ ์์ต๋๋ค.
wasm-game-of-life/www/index.html
์น์ฌ์ดํธ์ ์ฌ์ฉ๋๋ ์ต์๋จ HTML ํ์ผ์
๋๋ค. index.js
๋ฅผ ๊ฐ์ธ์ฃผ๋ bootstrap.js
๋ฅผ ๋ถ๋ฅด๋ ๊ฒ ์ธ์๋ ํน๋ณํ ๋์์ ํ์ง ์์ต๋๋ค.
<!DOCTYPE html>
<html>
<head>
<meta charset="utf-8">
<title>Hello wasm-pack!</title>
</head>
<body>
<script src="./bootstrap.js"></script>
</body>
</html>
wasm-game-of-life/www/index.js
์ด index.js
๋ ์์
์ ํ๊ฒ ๋ ์น์ฌ์ดํธ ์๋ฐ์คํฌ๋ฆฝํธ์ ์ง์
์ (entry point) ์
๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋์ wasm-pack-template
์ ์ปดํ์ผ ๊ฒฐ๊ณผ๋ฌผ์ธ ์น์ด์
๋ธ๋ฆฌ๊ฐ ํฌํจ๋ผ ์๋๋ฐ, hello-wasm-pack
npm ํจํค์ง๋ฅผ ๋ก๋ํ๊ณ ํจํค์ง ๋ด๋ถ์ greet
ํจ์๋ฅผ ํธ์ถํด ์ค๋๋ค.
import * as wasm from "hello-wasm-pack";
wasm.greet();
์ข ์์ฑ ์ค์นํ๊ธฐ
์ฐ์ , wasm-game-of-life/www
๋ด๋ถ ๊ฒฝ๋ก์์ npm install
๋ช
๋ น์ด๋ฅผ ์คํํ์ฌ ๋ก์ปฌ ๊ฐ๋ฐ ์๋ฒ์ ์ข
์์ฑ๋ค์ด ์ค์น๋ผ ์๋์ง ํ์ธํด ์ฃผ์ธ์:
npm install
์ด ์ปค๋งจ๋๋ ํ ๋ฒ๋ง ์คํ๋ผ์ผ ํฉ๋๋ค. ์คํํ๋ฉด webpack
์๋ฐ์คํฌ๋ฆฝํธ ๋ฒ๋ค๋ฌ์ ๊ฐ๋ฐ ์๋ฒ๋ฅผ ์ค์นํ ์ ์์ต๋๋ค.
๋จ์ํ ๊ฐํธํ๊ฒ ์ฑ ์ ์งํํ๊ธฐ ์ํด ์ด ๋ฒ๋ค๋ฌ์ ๊ฐ๋ฐ ์๋ฒ๋ฅผ ์ฌ์ฉํ ์์ ์ด์ง๋ง
webpack
์ด ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ ์์ ์ ํ์๊ฐ ์๋๋ผ๋ ์ ์ ๊ธฐ์ตํด ์ฃผ์ธ์. Parcel๊ณผ Rollup๋ ์น์ด์ ๋ธ๋ฆฌ์ ECMAScript ๋ชจ๋์ ๋ถ๋ฅด๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค. ์ํ๋ค๋ฉด ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ๋ฒ๋ค๋ฌ ์์ด ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
www
ํจํค์ง ๋ด๋ถ์์ ๋ก์ปฌ wasm-game-of-life
ํจํค์ง ์ฌ์ฉํ๊ธฐ
npm์์ ๋ค์ด๋ก๋ํ hello-wasm-pack
๋์ ์ ๋ก์ปฌ ํ๊ฒฝ์ ์๋ wasm-game-of-life
๋ฅผ ์ฌ์ฉํด ๋ด
์๋ค. ์ด๋ ๊ฒ ํจ์ผ๋ก์จ Game of Life ํ๋ก๊ทธ๋จ์ ๋ ์ ์ง์ ์ผ๋ก ๊ฐ๋ฐํ ์ ์๊ฒ ๋ฉ๋๋ค.
wasm-game-of-life/www/package.json
ํ์ผ์ ์ด๊ณ devDependencies
๋ค์์ dependencies
ํ๋๋ฅผ ์์ฑํด ์ฃผ์ธ์. ๊ทธ๋ค์์, "wasm-game-of-life": "file:../pkg"
์ํธ๋ฆฌ๋ฅผ ํฌํจ์์ผ์ฃผ์ธ์.
{
// ...
"dependencies": { // ์ด 3์ค ๊ธธ์ด์ ๋ธ๋ญ์ ์ถ๊ฐํด์ฃผ์ธ์!
"wasm-game-of-life": "file:../pkg"
},
"devDependencies": {
//...
}
}
๋ค์์ผ๋ก, hello-wasm-pack
๋์ ์ wasm-game-of-life
๋ฅผ ๋ถ๋ฅผ ์ ์๋๋ก wasm-game-of-life/www/index.js
ํ์ผ์ ์์ ํด ์ฃผ์ธ์:
import * as wasm from "wasm-game-of-life";
wasm.greet();
์ ์ข ์์ฑ์ ๋ง๋ค์๋ค๋ฉด, ๋ค์ ๋ช ๋ น์ด๋ก ์ค์นํด์ผ ํฉ๋๋ค:
npm install
์ด์ ์น์ฌ์ดํธ๋ฅผ ๋ก์ปฌ ํ๊ฒฝ์์ ๊ตฌ๋ํ ์ ์๊ฒ ๋์ต๋๋ค!
๋ก์ปฌ ํ๊ฒฝ์์ ๊ตฌ๋ํ๊ธฐ
์ด์ ๊ฐ๋ฐ ์๋ฒ๋ฅผ ๊ตฌ๋ํ ์ ํฐ๋ฏธ๋์ ์ด์ด์ฃผ์ธ์. ์๋ก ์ฐ ํฐ๋ฏธ๋์์ ์๋ฒ๋ฅผ ๊ตฌ๋ํ๋ฉด ๋ฐฑ๊ทธ๋ผ์ด๋์์ ๊ณ์ ์๋ฒ๋ฅผ ๊ตฌ๋ํ๋ฉด์ ๋ค๋ฅธ ๋ช
๋ น์ด๋ฅผ ๊ณ์ ์
๋ ฅํ ์ ์๊ฒ ๋ฉ๋๋ค. ์ ํฐ๋ฏธ๋์์ wasm-game-of-life/www
๊ฒฝ๋ก๋ก ๋ค์ด๊ฐ ๋ค์ ์ด ๋ช
๋ น์ด๋ฅผ ์คํํด ์ฃผ์ธ์:
npm run start
์น ๋ธ๋ผ์ฐ์ ๋ฅผ ์ด๊ณ http://localhost:8080/ ๋ฅผ ์ด๋ฉด ํ์๋๋ "Hello World" alert ๋ฉ์ธ์ง๋ฅผ ํ์ธํ ์ ์์ต๋๋ค:
ํ์ผ์ ์ ์ฅํ ๋๋ง๋ค http://localhost:8080/ ํ์ด์ง์ ๋ฐ์๋๋๋ก ํ๊ณ ์ถ๋ค๋ฉด, wasm-pack build
๋ช
๋ น์ด๋ฅผ wasm-game-of-life
๊ฒฝ๋ก์์ ๋ค์ ์คํํด ์ฃผ์ธ์.
์ฐ์ตํด ๋ณด๊ธฐ
-
wasm-game-of-life/src/lib.rs
๊ฒฝ๋ก์ ์๋greet
ํจ์๋ฅผ ์์ ํด์ ํ์๋๋ ๋ฉ์ธ์ง๋ฅผ ์ปค์คํฐ๋ง์ด์งํ ์ ์๋๋กname: &str
๋งค๊ฐ๋ณ์๋ฅผ ์ถ๊ฐํด ๋ณด๊ณ ,wasm-game-of-life/www/index.js
ํ์ผ์์greet
ํจ์๋ฅผ ์ด๋ฆ๊ณผ ํจ๊ป ํธ์ถํด ๋ณด์ธ์.wasm-pack build
๋ช ๋ น์ด๋ก.wasm
๋ฐ์ด๋๋ฆฌ๋ฅผ ๋ค์ ๋น๋ํ๊ณ , http://localhost:8080/ ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ๋ฉด ๋ธ๋ผ์ฐ์ ์์ ์์ ๋ ์๋ฆผ ๋ฉ์ธ์ง๋ฅผ ํ์ธํ ์ ์์ต๋๋ค.์ ๋ต
wasm-game-of-life/src/lib.rs
ํ์ผ์์ ์๋กญ๊ฒ ์์ ๋greet
ํจ์:#![allow(unused)] fn main() { #[wasm_bindgen] pub fn greet(name: &str) { alert(&format!("Hello, {}!", name)); } }
wasm-game-of-life/www/index.js
ํ์ผ์์ ์์ ๋greet
ํจ์ ํธ์ถํ๊ธฐ:wasm.greet("Your Name");
Conway's Game of Life์ ๊ท์น
Note: ์ด๋ฏธ Conway's Game of Life ์ ์ด ๊ฒ์์ ๊ท์น์ ์ ์๊ณ ์๋ค๋ฉด ๋ค์ ์น์ ์ผ๋ก ๋์ด๊ฐ๋ ๊ด์ฐฎ์ต๋๋ค.
Wikipedia์ Conway's Game of Life์ ๊ท์น์ด ์์ฃผ ์ ์ค๋ช ๋ผ ์์ต๋๋ค.
Game of Life์ ์ธ์(universe)์ ์ฌ๊ฐํ ์ธํฌ๋ก ์ด๋ฃจ์ด์ง ๋ฌดํํ ์ฌ์ด์ฆ์ 2์ฐจ์ ์ ์ฌ๊ฐํ_ํ ์ ๋ ์ด์ ์ธ๋ฐ, ๊ฐ๊ฐ์ ์ธํฌ๋ ์ด์์๊ฑฐ๋ ์ฃฝ์ด์๊ฑฐ๋, ํน์ "์ฃผ๊ฑฐ"๋ "๋ฌด์ฃผ๊ฑฐ" ์ค ํ ์ํ์ผ ์ ์์ต๋๋ค. ๊ฐ ์ธํฌ์ ์ํ, ์์ง, ํน์ ๋๊ฐ์ ์ผ๋ก ์ด์ํ๋ ์ฌ๋๊ฐ์ ์ด์ ์ธํฌ๊ณผ ์ํธ์์ฉํฉ๋๋ค. ๋งค ๋จ๊ณ๋ง๋ค, ๋ค์ ์ ์ด๊ฐ ๋ฐ์ํฉ๋๋ค.
- ์ธ๊ตฌ ๋ถ์กฑ์ผ๋ก 2๊ฐ ๋ฏธ๋ง์ ์ด์์ ๊ฐ์ง ์ธํฌ๋ ์ฃฝ๊ฒ ๋ฉ๋๋ค.
- 2๊ฐ ํน์ 3๊ฐ์ ์ด์์ ๊ฐ์ง ์ธํฌ๋ ๋ค์ ์ธ๋์์ ๊ณ์ ์ด์์์ต๋๋ค.
- ๊ณผ์ ์ธ๊ตฌ๋ก 3๊ฐ ์ด๊ณผ์ ์ด์์ ๊ฐ์ง ๋ชจ๋ ์ธํฌ๋ ์ฃฝ๊ฒ ๋ฉ๋๋ค.
- ์ธํฌ ์ฆ์์ผ๋ก ์ ํํ 3๊ฐ์ ์ด์์ ๊ฐ์ง ์ธํฌ๋ ์ด์๋๊ฒ ๋ฉ๋๋ค.
์ด ์ด๊ธฐ ํจํด์ ๊ฒ์ ์์คํ ์ ์์์ (seed)์ ๋ง๋ค๊ฒ ๋ฉ๋๋ค. ์ ๊ท์น๋ค์ ์์์ (seed)์ ๋ชจ๋ ์ธํฌ์ ์ ์ฉํ๋ฉด์ ์ฒซ ๋ฒ์งธ ์ธ๋๊ฐ ์์ฑ๋๊ฒ ๋ฉ๋๋ค. ์ถ์๊ณผ ์ฌ๋ง์ ๋์์ ์ผ์ด๋๊ณ , ์ด๊ฐ ๋ฐ์ํ๋ ๊ฐ๊ฐ์ ์๊ฐ์ ํฑ(tick) ์ด๋ผ๊ณ ๋ถ๋ฆ ๋๋ค. (๋ค์ ๋งํด, ๊ฐ ์ธ๋๋ ์ง์ ์ธ๋์ ์์ ํจ์์ ๋๋ค.) ์ด ๊ท์น์ ๊ณ์ ์ ์ฉ๋์ด ๋ฐ๋ณต์ ์ผ๋ก ์ถ๊ฐ ์ธ๋๋ฅผ ๋ง๋ญ๋๋ค.
๋ค์ ์ด๋ฏธ์ง๋ฅผ ์ด๊ธฐ ์ธ์์ด๋ผ๊ณ ์๊ฐํด ๋ด ์๋ค:

๋ค์ ์ธ๋๋ฅผ ๊ณ์ฐํ ๋ ํ๋์ฉ ๊ฐ ์ธํฌ๋ฅผ ๊ณ ๋ คํด ๋ณผ ์ ์๋๋ฐ, ํ๋ฒ ์ดํด๋ณด๋๋ก ํฉ์๋ค. ์ด ์ด๊ธฐ ์ธ์์์ ์ผ์ชฝ ์ต์๋จ ์ธํฌ๋ ์ฃฝ์ด์์ต๋๋ค. ๊ท์น (4)๋ ์ฃฝ์ด์๋ ์ธํฌ์๋ง ์ ์ฉ๋๋ ์ ์ผํ ์ ์ด ์ธํฌ์ด์ง๋ง, ์ต์๋จ ์ผ์ชฝ ์ธํฌ๋ ์ ํํ 3๊ฐ์ ์ด์์๋ ์ด์์ ๊ฐ์ง๊ณ ์์ง ์๊ธฐ ๋๋ฌธ์ ์ ์ด ๊ท์น์ด ์ ์ฉ๋์ง ์๊ณ ๋ค์ ์ธ๋์์๋ ์ฃฝ์ด ์๋ ์ํ๋ก ์ ์ง๋ฉ๋๋ค. ๋์ผํ ์ด์ ๋ก ์ฒซ ๋ฒ์งธ ํ์ ๋ค๋ฅธ ์ธํฌ๋ค๋ ๊ทธ๋๋ก ์ฃฝ์ด์๊ฒ ๋ฉ๋๋ค.
๋ ๋ฒ์งธ ํ, ์ธ๋ฒ์งธ ์ด์ ์ด์์๋ ์ธํฌ๋ฅผ ๋ณด๋ฉด ๋งค์ฐ ํฅ๋ฏธ๋ก์ด ๋ด์ฉ์ ํ์ธํ ์ ์๋๋ฐ, ์ธํฌ๊ฐ ์ด์์๋ค๋ฉด ์ฒซ๋ฒ์งธ ์ธ ๊ท์น์ด ์ ์ฉ๋ ์๋ ์์ต๋๋ค. ์ด ์ธํฌ๋ ๋จ ํ ๊ฐ์ ์ด์์๋ ์ด์์ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์ ๊ท์น (1)์ด ์ ์ฉ๋๊ฒ ๋์ด ๋ค์ ์ธ๋์์ ์ฃฝ๊ฒ ๋ฉ๋๋ค. ์ตํ๋จ์ ์ด์์๋ ์ธํฌ๋ ๋์ผํ๊ฒ ์ฃฝ์ต๋๋ค.
๊ฐ์ด๋ฐ์ ์์นํ ์ด์์๋ ์ธํฌ๋ ์์๋๋ก ๋ ๊ฐ์ ์ด์์๋ ์ด์์ ๊ฐ์ง๊ณ ์์ต๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก ๊ท์น (2)๊ฐ ์ ์ฉ์ด ๋์ด ๋ค์ ์ธ๋์์๋ ์ด์์๊ฒ ๋ฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก ๊ฐ์ด๋ฐ์ ์ด์์๋ ์ธํฌ๋ค์ ์ผ์ชฝ๊ณผ ์ค๋ฅธ์ชฝ ์ด์๋ค์ ๋ณด๋ฉด ์ ๋ง ํฅ๋ฏธ๋ก์ด ๋ถ๋ถ์ ํ์ธํ ์ ์๋๋ฐ, ์ด 3๊ฐ์ ์ด์์๋ ์ธํฌ๋ค์ ์์ชฝ ๋ฐฉํฅ์ผ๋ก ๋ ์ธํฌ๋ค๊ณผ ์ด์ํด ์๊ธฐ ๋๋ฌธ์ ๊ท์น (4)๊ฐ ์ ์ฉ์ด ๋ฉ๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก ์ด ์ธํฌ๋ค์ ๋ค์ ์ธ๋์์ ์ด์์๊ฒ ๋ฉ๋๋ค.
์์์ ๋ค๋ฃฌ ๋ด์ฉ์ ํ ๋๋ก, ๋ค์ ํฑ์ ์ธ์์ ์ด๋ ๊ฒ ๋ณด์ด๊ฒ ๋ฉ๋๋ค:

์ด๋ฌํ ๊ฐ๋จํ๊ณ ๊ฒฐ์ ์ ์ธ ๊ท์น์ ์ ์ฉํ๋ ๊ฒ์ผ๋ก ๋ค์๊ณผ ๊ฐ์ ์ด์ํ์ง๋ง ํฅ๋ฏธ๋ก์ด ๋์์ ํ์ธํ ์ ์๊ฒ ๋ฉ๋๋ค:
Gosper's glider gun | Pulsar | Space ship |
---|---|---|
![]() | ![]() | ![]() |
์ฐ์ตํด ๋ณด๊ธฐ
-
๋ฐฉ๊ธ ๋ณด์ฌ๋๋ฆฐ ๋ค์ ํฑ ์์ ์ดํ, ๊ทธ ๋ค์ ํฑ์ ์ค์ค๋ก ๊ณ์ฐํด ๋ณด์ธ์. ์ด๋ป๊ฒ ๊ฐ์ด ์ค์๋ ๊ฒ ๊ฐ๋์?
์ ๋ต
์ด๊ธฐ ์ํ์ ์ฐ์ฃผ๋ก ๋ค์ ๋์๊ฐ๊ฒ ๋ฉ๋๋ค.
์ด ํจํด์ ์ฃผ๊ธฐ์ ์ ๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก ๋ ํฑ๋ง๋ค ์ฒ์ ์ํ๋ก ๋์๊ฐ๊ฒ ๋ฉ๋๋ค.
-
"์์ ๋" ์ด๊ธฐ ์ธ์์ ์ฐพ์๋ณด์ค ์ ์์ผ์ ๊ฐ์? ์ธ๋๊ฐ ๋ฐ๋์ด๋ ๋ด์ฉ์ด ๋ฐ๋์ง ์๋ ์ธ์์ ์ฐพ์๋ณด์ธ์.
์ ๋ต
์์ ๋ ์ธ์์ ๋ฌดํํ๊ฒ ๋ง์ต๋๋ค. ์ง๋ฃจํ๊ฒ๋ ํ ๋น์ด์๋ ์ธ์๋ ์์ ๋ ์ธ์์ด๊ณ , ์ด์์๋ ์ธํฌ๋ค์ด 2 x 2 ์ฌ์ด์ฆ์ ์ฌ๊ฐํ ๋ชจ์์ ํ์ฑํ ๋๋ ์์ ๋ ์ธ์์ ํ์ธํ ์ ์์ต๋๋ค.
Conway's Game of Life ๊ตฌํํ๊ธฐ
์ค๊ณ
์์ํ๊ธฐ ์ ์, ์ด๋ค ๋ฐฉ์์ผ๋ก Game of Life๋ฅผ ์ค๊ณํ ์ง ์ดํด๋ด ์๋ค.
๋ฌดํํ ์ธ์
Game of Life๋ ๋ฌดํํ ์ธ์์์ ์์๋ฉ๋๋ค. ํ์ง๋ง ๋ณดํต์ ์ฐ๋ฆฌ๊ฐ ๋ฌดํํ ๋ฉ๋ชจ๋ฆฌ์ ์ปดํจํฐ ํ์๋ฅผ ๊ฐ์ง๊ณ ์์ง ์๊ธฐ ๋๋ฌธ์ ๋ค์ ์ธ ๊ฐ์ง ๋ฐฉ๋ฒ ์ค ํ ๋ฐฉ๋ฒ์ ํตํด ์ด ๊ท์ฐฎ์ ์ ํ์ ์ฐํํ๊ฒ ๋ฉ๋๋ค:
-
์ธ์์ ์ด๋ค ๋ถ๋ถ์ด ๋ง์ ์ปดํจํฐ ์์์ ํ์๋ก ํ๋์ง ์ถ์ ํ๊ณ ์ด๋ฌํ ๋ถ๋ถ์ ํ์ํ ๋ ํ์ฅํฉ๋๋ค. ์ต์ ์ ๊ฒฝ์ฐ์๋, ์ด ํ์ฅ์ด ์ ํ ์์ด ์งํ๋๊ณ ์ฝ๋๊ฐ ๊ณ์ํด์ ๋๋ ค์ง๋ฉด์ ๊ฒฐ๊ตญ์๋ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๋ค ์ฐจ์งํ๊ฒ ๋ฉ๋๋ค.
-
๋ชจ์๋ฆฌ์ ์์นํ ์ธํฌ๋ค์ด ๊ฐ์ด๋ฐ์ ์์นํ ์ธํฌ๋ค๊ณผ ๋น๊ตํด์ ๋ ์ ์ ์ด์์ ๊ฐ์ง๊ฒ ๋๋ ์ฌ์ด์ฆ๊ฐ ์ ํด์ ธ ์๋ ์ธ์์ ๋ง๋ญ๋๋ค. ์ด ๋ฐฉ๋ฒ์๋ gliders์ ๊ฐ์ ๋ฌดํํ ํจํด์ด ๋ชจ์๋ฆฌ์์ ๋๋๋ฒ๋ฆฌ๊ฒ ๋๋ค๋ ๋จ์ ์ด ์์ต๋๋ค.
-
์ฌ์ด์ฆ๊ฐ ์ ํด์ก์ง๋ง ๊ณ์ํด์ ์ฐ๊ฒฐ๋๋ ์ฐ์ฃผ๋ฅผ ๋ง๋ญ๋๋ค. ์ธ์์ ๋์ ๋ฐ๋์ชฝ ์ธ์์ ๋์ผ๋ก ์ฐ๊ฒฐ์์ผ ์ธํฌ๋ค์ด ๊ณ์ํด์ ์ด์์ ๊ฐ์ง ์ ์๊ฒ ํฉ๋๋ค. ์ด๋ ๊ฒ gliders ํจํด์ด ๊ณ์ ์์ง์ผ ์ ์๊ฒ ๋ฉ๋๋ค.
๊ทธ๋ฌ๋ฉด ์ธ ๋ฒ์งธ ๋ฐฉ๋ฒ์ผ๋ก ๊ตฌํ์ ํด๋ณด๊ฒ ์ต๋๋ค.
๋ฌ์คํธ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ผ๋ฆฌ ์ฐ๊ฒฐํ๊ธฐ
โก ๋ค์ ๋ด์ฉ์ ์ด ํํ ๋ฆฌ์ผ์์ ๋ค๋ฃจ๋ ๋ด์ฉ ์ค์์๋ ์์ฃผ ์ค์ํ ๋ด์ฉ์ ๋๋ค. ์ด ๋ด์ฉ์ ์ดํดํ๋ฉด์ ์ป์ด๊ฐ ์ ์๋ ๋ถ๋ถ์ด ์์ฃผ ๋ง์ต๋๋ค!
์๋ฐ์คํฌ๋ฆฝํธ๋ Object
, Array
๊ทธ๋ฆฌ๊ณ DOM ๋
ธ๋ (node) ๋ค์ด ํ ๋น๋๋ ๊ฐ๋น์ง ์ฝ๋ ํฐ๊ฐ ๊ด๋ฆฌํ๋ ํ์ ์ฌ์ฉํ์ง๋ง, ์์ฑํ๊ฒ ๋ ๋ฌ์คํธ ์ฝ๋์ ์ ํ ๋ฉ๋ชจ๋ฆฌ๋ ๋ณ๊ฐ์ ๊ณต๊ฐ์ ์ฌ์ฉํ๊ฒ ๋ฉ๋๋ค. ์น์ด์
๋ธ๋ฆฌ๋ ํ์ฌ๋ก์จ๋ ๊ฐ๋น์ง ์ฝ๋ ํฐ๊ฐ ๊ด๋ฆฌํ๋ ํ์ ์ง์ ์ ๊ทผํ ์ ์์ต๋๋ค. (2018๋
4์ ๊ธฐ์ค์ผ๋ก, "์ธํฐํ์ด์ค ํ์
" ์ ์ ๊ณผ ํจ๊ป ๋ณ๊ฒฝ๋ ์ ๋ง์ด๊ธด ํฉ๋๋ค.) ๋ฐ๋ฉด์ ์๋ฐ์คํฌ๋ฆฝํธ๋ ArrayBuffer
๋ ์ค์นผ๋ผ ๊ฐ (scalar values / u8
, i32
, f64
, ๋ฑ...) ๋ง์ผ๋ก๋ผ๋ ์ด ์ ํ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฝ๊ณ ์ธ ์ ์์ต๋๋ค. ์ด๋ฐ ๋ด์ฉ์ ๊ธฐ๋ฐ์ผ๋ก ๋ชจ๋ ์น์ด์
๋ธ๋ฆฌ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฌ์ด์ ์ปค๋ฎค๋์ผ์ด์
์ด ๊ตฌ์ฑ๋๊ฒ ๋ฉ๋๋ค.
wasm_bindgen
๋ ์ด ๊ฒฝ๊ณ๋ฅผ ์ฌ์ด๋ก ์ด๋ป๊ฒ ๊ตฌ์กฐ์ฒด (compound structure) ๋ค์ ์ฃผ๊ณ ๋ฐ์์ผ ํ๋์ง ์ ํด์ฃผ๋ ์ญํ ์ ํฉ๋๋ค. ์ด๋ฌํ ์์
์ ๋ฌ์คํธ ๊ตฌ์กฐ์ฒด๋ฅผ ๋ฐ์ฑ(boxing)ํ๊ณ , ์ฝ๊ฒ ์ฌ์ฉํ๊ธฐ ์ํด ์๋ฐ์คํฌ๋ฆฝํธ ํด๋์ค์ ํฌ์ธํฐ๋ฅผ ๋ฉํ(wrapping)ํ๊ณ , ๋ฌ์คํธ ์ฝ๋์์ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด ํ
์ด๋ธ์ ์ธ๋ฑ์ฑ(indexing)ํ๋ ๊ณผ์ ์ ํฌํจํฉ๋๋ค. wasm_bindgen
์ ๋งค์ฐ ๊ฐํธํ์ง๋ง, ๋ฐ์ดํฐ ํํ ์ค๊ณ๋ฅผ ๋ชจ๋ ๋์ ํด์ฃผ์ง ์์ต๋๋ค. ์ํ๋ ๋ฐฉ์์ผ๋ก ์ธํฐํ์ด์ค ์ค๊ณ๋ฅผ ๊ตฌํํ ์ ์๋๋ก ๋์์ฃผ๋ ๋๊ตฌ ์ ๋๋ก ์๊ฐํ๋ฉด ์ข์ต๋๋ค.
WebAssembly์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฌ์ด์ ์ธํฐํ์ด์ค๋ฅผ ์ค๊ณํ ๋, ๋ค์ ๋ด์ฉ๋ค์ ์ต์ ํ ์์ ์ ๊ณ ๋ คํด์ผ ํฉ๋๋ค:
-
์๋ฐ์คํฌ๋ฆฝํธ์ ์น์ด์ ๋ธ๋ฆฌ ์ ํ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ด๋ฅผ ์ค๊ฐ๋ ๋ณต์ฌ(copy) ์ต์ํํ๊ธฐ. ๋ถํ์ํ ๋ณต์ฌ๋ ๋ถํ์ํ ์ค๋ฒํค๋๋ฅผ ๋ฐ์์ํต๋๋ค.
-
์ง๋ ฌํ(serializing)์ ์ญ์ง๋ ฌํ(deserializing) ์ต์ํํ๊ธฐ. ๋ณต์ฌ์ ๋ง์ฐฌ๊ฐ์ง๋ก, ์ง๋ ฌํ์ ์ญ์ง๋ ฌํ๋ ์ค๋ฒํค๋๋ฅผ ๋ฐ์์ํฌ ์ ์๊ณ , ์ด๋ฌํ ์์ ์ด ๋ณต์ฌ๋ ์์ฃผ ๋ฐ์์ํค๊ฒ ๋ฉ๋๋ค. ํ ๊ณณ์์ ๋ชจ๋ ์ง๋ ฌํ ์์ ์ ํ๋ ๋์ ์ผ๋ฐ์ ์ผ๋ก ์น์ด์ ๋ธ๋ฆฌ ์ ํ ๋ฉ๋ชจ๋ฆฌ์ ์๋ ค์ง ์์น๋ก opaque handle๋ค์ ๋๊ธฐ๋ ๋ฐฉ์์ผ๋ก ๋ง์ ์ค๋ฒํค๋๋ฅผ ์ค์ผ์ ์๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ
wasm_bindgen
์ ํตํด ์๋ฐ์คํฌ๋ฆฝํธ์Object
๋ ๋ฐ์ฑ๋ ๋ฌ์คํธstruct
๋ฅผ ๊ฐ๋ฆฌํค๋ opaque handle๋ค์ ๋ ์ฝ๊ฒ ์ ์ํ๊ณ ์ฌ์ฉํ ์ ์์ต๋๋ค.
๋๋ถ๋ถ์ ๊ฒฝ์ฐ์๋, ์๋ฐ์คํฌ๋ฆฝํธ์ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ค๊ฐ ๋ ์ฌ์ด์ฆ๊ฐ ํฌ๊ณ ์ค๋ ์ด์์์ด์ผ ํ๋ ์๋ฃ ๊ตฌ์กฐ๋ฅผ ์น์ด์ ๋ธ๋ฆฌ ์ ํ ๋ฉ๋ชจ๋ฆฌ์ ๋๊ณ , ์ด๋ฌํ ๊ฐ๋ค์ ์๋ฐ์คํฌ๋ฆฝํธ์์ opaque handle๋ก์จ ๋ ธ์ถ์ํค๋ ๊ฒ์ด ์ข์ ์ธํฐํ์ด์ค ์ค๊ณ์ ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ์ด๋ฌํ opaque handle๋ฅผ ํตํด ์น์ด์ ๋ธ๋ฆฌ ํจ์๋ฅผ ํธ์ถํ๊ณ , ๋ฐ์ดํฐ๋ฅผ ๋ณํ์ํค๊ณ , ๋ฌด๊ฑฐ์ด ์ปดํจํ ์์ ์ ํ๊ณ , ๊ฐ์ ๊ฒ์ํ๊ณ , ์ต์ข ์ ์ผ๋ก ์์ ์ฌ์ด์ฆ์ ๋ณต์ฌํ ์ ์๋ ๊ฐ์ ๋ฐํํ๊ฒ ๋ฉ๋๋ค. ์์ ๊ฐ๋ง ๋ฐํํ๊ฒ ๋๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ๋น์ง ์ฝ๋ ํฐ๊ฐ ๊ด๋ฆฌํ๋ ํ๊ณผ ์น์ด์ ๋ธ๋ฆฌ ์ ํ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ด์ ๋ชจ๋ ๊ฐ๋ค์ ์๋ค๋ก ๋ณต์ฌํ๊ณ ์ง๋ ฌํํ ํ์๊ฐ ์์ด์ง๊ฒ ๋ฉ๋๋ค.
๋ฌ์คํธ์ ์๋ฐ์คํฌ๋ฆฝํธ๋ฅผ ๊ตฌํํ๋ ํ๋ก๊ทธ๋จ์์ ์กฐ์ํ๊ธฐ
์ํํ ์ฌ๋ก๋ฅผ ์ดํด๋ณด๋ ๊ฒ์ผ๋ก ์์ํด๋ด ์๋ค. ๋งค ํฑ๋ง๋ค ์ธ์์ ์น์ด์ ๋ธ๋ฆฌ์์ ๋ถ๋ฌ์ค๊ฑฐ๋ ๊ฐ์ ธ์ค๊ธฐ ์ํด ๋ณต์ฌํ์ง ์์์ผ ํ๊ณ , ์ธํฌ ํ๋์ฉ ๊ฐ์ฒด๋ฅผ ๋ชจ๋ ํ ๋นํ๊ฑฐ๋ ๊ฒฝ๊ณ๋ฅผ ์ค๊ฐ๋ฉด์ ์ฝ๊ณ ์ฐ๋ ๊ฒ๋ ์ข์ง ์์ต๋๋ค.
๊ทธ๋ ๋ค๋ฉด ์ด๋ป๊ฒ ๊ตฌํํ๋ ๊ฒ ์ข์๊น์? ์ฃฝ์ ์ธํฌ๋ฅผ 0
๋ก ๋ํ๋ด๊ณ ์ด์์๋ ์ธํฌ๋ฅผ 1
๋ก ๋ํ๋ด๋ ์์ผ๋ก ์ธํฌ๋ค์ ๊ฐ๊ฐ 1 byte ๊ฐ์ผ๋ก ๋ํ๋ด๋ณผ ์๋ ์๋๋ฐ, ์น์ด์
๋ธ๋ฆฌ ์ ํ ๋ฉ๋ชจ๋ฆฌ์ 1์ฐจ์ ๋ฐฐ์ด๋ก ๋ํ๋ด๋ด
์๋ค.
4 x 4 ์ฌ์ด์ฆ์ ์ฐ์ฃผ๋ฅผ ๋ฉ๋ชจ๋ฆฌ ์ด๋ฏธ์ง๋ก ํํํด ๋ณด๊ฒ ์ต๋๋ค:
์ด ๊ณต์์ ์ฌ์ฉํด์ ์ฃผ์ด์ง ์ด๊ณผ ํ์ ํด๋นํ๋ ๋ฐฐ์ด ์ธ๋ฑ์ค๋ฅผ ์ฐพ์ ์ ์์ต๋๋ค:
index(row, column, universe) = row * width(universe) + column
์ธํฌ๋ค์ ์๋ฐ์คํฌ๋ฆฝํธ์ ๋
ธ์ถ์ํฌ๋ ์ฌ๋ฌ ๊ฐ์ง ๋ฐฉ๋ฒ์ ์ฌ์ฉํด๋ณผ์ ์๋๋ฐ, ์ฐ์ ์ ๋ฌ์คํธ String
ํ์
์ ๊ฐ์ผ๋ก ์ธํฌ๋ค์ ๋ฌธ์๋ก ํ์ํ ์ ์๋๋ก Universe
ํ์
์ std::fmt::Display
ํธ๋ ์ดํธ๋ฅผ ๊ตฌํํด ์ฃผ๋๋ก ํฉ์๋ค. ์ด ํธ๋ ์ดํธ๋ฅผ ํตํด ๋ฌ์คํธ String
ํ์
์ ๊ฐ์ ์น์ด์
๋ธ๋ฆฌ ์ ํ ๋ฉ๋ชจ๋ฆฌ์์ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ๋น์ง ์ฝ๋ ํฐ๊ฐ ๊ด๋ฆฌํ๋ ํ์ผ๋ก ๋ณต์ฌํ ์ ์๊ฒ ๋ฉ๋๋ค. ๊ทธ ๋ค์, ๋ณต์ฌ๋ ๊ฐ์ HTML textConent
์ ํ์ํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ์ด ์ฑํฐ ํ๋ฐ์์๋ ์ด ๊ตฌํ์ ๋ง๋ถ์ฌ์ ์ธํฌ๋ค์ ํ์ ๋ณต์ฌํ์ง ์๋๋ก ํด๋ณด๊ณ ์ธํฌ๋ค์ <canvas>
์ ํ์ํด๋ณผ ์์ ์
๋๋ค.
ํ๋ ๋ ๋์ ํด๋ณผ ๋ฒํ ์ค๊ณ๊ฐ ์๋๋ฐ, ์ธ์ ์ ์ฒด๋ฅผ ๋ ธ์ถ์ํค์ง ์๊ณ ๋งค ํฑ๋ง๋ค ์ํ๊ฐ ๋ฐ๋๊ฒ ๋๋ ์ธํฌ๋ค์ ๋ชฉ๋ก์ผ๋ก ๋ง๋ค์ด์ ๋ฌ์คํธ ์ฝ๋์์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋ฐํํด ๋ณผ ์๋ ์์ต๋๋ค. ์ด ๋ฐฉ๋ฒ์ผ๋ก, ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋์์ ์ธ์ ์ ์ฒด๋ฅผ ์ํํ ํ์ ์์ด ์ผ๋ถ๋ง ์ํํ ์ ์๊ฒ ๋ฉ๋๋ค. ๋จ์ ์ผ๋ก๋, ์ด ๋ธํ ๊ธฐ๋ฐ (delta-based)์ ์ค๊ณ๋ ๊ตฌํํ๊ธฐ๊ฐ ์กฐ๊ธ ๋ ์ด๋ ต์ต๋๋ค.
๋ฌ์คํธ ์ฝ๋ ๊ตฌํํ๊ธฐ
์ง์ ์ฑํฐ์์ ์ด๊ธฐ ํ๋ก์ ํธ ํ ํ๋ฆฟ์ ํด๋ก ํ๋๋ฐ, ์ด ํ ํ๋ฆฟ์ ํ๋ฒ ์์ ํด ๋ณด๋๋ก ํฉ์๋ค.
alert
๋ฅผ ์ํฌํธํ๋ ์ค๊ณผ greet
ํจ์๋ฅผ wasm-game-of-life/src/lib.rs
ํ์ผ์์ ์ง์๋ณด๊ณ , ์ธํฌ์ ํ์
์ ์๋ฅผ ๋์ ์ถ๊ฐํด ์ฃผ๋ ๊ฒ์ผ๋ก ์์ํด ๋ณด๊ฒ ์ต๋๋ค:
#![allow(unused)] fn main() { #[wasm_bindgen] #[repr(u8)] #[derive(Clone, Copy, Debug, PartialEq, Eq)] pub enum Cell { Dead = 0, Alive = 1, } }
๊ฐ ์ธํฌ๊ฐ 1 byte ์ฌ์ด์ฆ๋ก ํํ๋ผ์ผ ํ๋ฏ๋ก #[repr(u8)]
์ ์์ง ์๊ณ ๋ถ์ฌ์ฃผ๋๋ก ํ๊ณ , ์ธํฌ ์ฃผ๋ณ์ ์ด์์๋ ์ด์๋ค์ ์ฝ๊ฒ ์
์ ์๋๋ก, 0
๋ฅผ Dead
๋ก, 1
์ Alive
๋ก ์ ํด์ฃผ๋ ๊ฒ๋ ์ค์ํฉ๋๋ค.
๊ทธ๋ค์, ์ธ์์ ์ ์ํด๋ด
์๋ค. ์ธ์์ ๋ํ๋ด๋ ๊ตฌ์กฐ์ฒด๋ ๋๋น์ ๋์ด, ์ธํฌ๋ค์ ๋ํ๋ด๋ width * height
ํฌ๊ธฐ์ ๋ฒกํฐ(vector)๋ฅผ ํ๋๋ก ๊ฐ์ง๊ฒ ๋ฉ๋๋ค.
#![allow(unused)] fn main() { #[wasm_bindgen] pub struct Universe { width: u32, height: u32, cells: Vec<Cell>, } }
์ด์ ์ ์ค๋ช ํ ๋ด์ฉ๋๋ก ์ฃผ์ด์ง ํ๊ณผ ์ด์ ์ธํฌ ๋ฒกํฐ์ ์ธ๋ฑ์ค๋ก ๋ณํํ์ฌ ์ฌ์ฉํ ์ ์์ต๋๋ค:
#![allow(unused)] fn main() { impl Universe { fn get_index(&self, row: u32, column: u32) -> usize { (row * self.width + column) as usize } // ... } }
์ธํฌ์ ๋ค์ ์ํ๋ฅผ ๊ณ์ฐํ๋ ค๋ฉด ๋ช ๊ฐ์ ์ด์์ด ์ด์์๋์ง ํ์ธํด์ผ ํฉ๋๋ค. ์ด ๋ด์ฉ์ ํ ๋๋ก live_neighbor_count
๋ฉ์๋๋ฅผ ์์ฑํด ๋ด
์๋ค!
#![allow(unused)] fn main() { impl Universe { // ... fn live_neighbor_count(&self, row: u32, column: u32) -> u8 { let mut count = 0; for delta_row in [self.height - 1, 0, 1].iter().cloned() { for delta_col in [self.width - 1, 0, 1].iter().cloned() { if delta_row == 0 && delta_col == 0 { continue; } let neighbor_row = (row + delta_row) % self.height; let neighbor_col = (column + delta_col) % self.width; let idx = self.get_index(neighbor_row, neighbor_col); count += self.cells[idx] as u8; } } count } } }
live_neighbor_count
๋ฉ์๋๋ ๋ธํ๊ฐ๊ณผ ๋๋จธ์ง ๊ฐ์ ๊ฐ๊ฐ ํ์ธํด์ ์ธ์์ ๊ฐ์ฅ์๋ฆฌ์์ ๋ฐ์ํ ์ ์๋ ์์ธ๋ฅผ if
๋ฌธ์ผ๋ก ์ฒ๋ฆฌํด ์ค๋๋ค. -1
์ ๋ธํ๋ฅผ ์ ์ฉํ ๋, self.height - 1
์ ์ถ๊ฐํด์ 1
์ ๋นผ๋ ๋์ ๋๋จธ์ง ๊ฐ์ ๊ณ์ ์ฒ๋ฆฌํ๊ณ , row
์ column
์ ๊ฐ๊ฐ 0
์ด ๋ ์ ์์ต๋๋ค. ์ด row
์ column
๊ฐ์์ 1
์ ๋นผ๋ ค๊ณ ์๋ํ ๋, unsigned integer underflow ๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
์ด์ ํ์ฌ ์ธ๋๋ฅผ ๊ธฐ๋ฐ์ผ๋ก ๋ค์ ์ธ๋๋ฅผ ์ฒ๋ฆฌํ๋ ๋ฐ ํ์ํ ์ค๋น๊ฐ ์๋ฃ๋์ต๋๋ค! match
๋ฌธ์ ์ฌ์ฉํด์ ๊ฒ์์ ๊ท์น์ ๋ณด๊ธฐ ๋ช
ํํ๊ฒ ๋ํ๋ด๋ด
์๋ค. ์ถ๊ฐ๋ก ํฑ์ด ์ผ์ด๋ ๋ ์๋ฐ์คํฌ๋ฆฝํธ๊ฐ ์ปจํธ๋กคํ๋๋ก ํ ์์ ์ด๊ธฐ ๋๋ฌธ์, #[wasm_bindgen]
๋ธ๋ญ์ ์ถ๊ฐํด์ ์ด ๋ฉ์๋๋ฅผ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋์ ๋
ธ์ถ์์ผ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
#![allow(unused)] fn main() { /// Public ๋ฉ์๋, ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ต์คํฌํธ ํ ์ ์๋๋ก ํจ. #[wasm_bindgen] impl Universe { pub fn tick(&mut self) { let mut next = self.cells.clone(); for row in 0..self.height { for col in 0..self.width { let idx = self.get_index(row, col); let cell = self.cells[idx]; let live_neighbors = self.live_neighbor_count(row, col); let next_cell = match (cell, live_neighbors) { // ๊ท์น 1: ์ธ๊ตฌ ๋ถ์กฑ์ผ๋ก 2๊ฐ ๋ฏธ๋ง์ ์ด์์ ๊ฐ์ง ์ธํฌ๋ ์ฃฝ๊ฒ ๋ฉ๋๋ค. (Cell::Alive, x) if x < 2 => Cell::Dead, // ๊ท์น 2: 2๊ฐ ํน์ 3๊ฐ์ ์ด์์ ๊ฐ์ง ์ธํฌ๋ ๋ค์ ์ธ๋์์ ๊ณ์ ์ด์์์ต๋๋ค. (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive, // Rule 3: ๊ณผ์ ์ธ๊ตฌ๋ก 3๊ฐ ์ด๊ณผ์ ์ด์์ ๊ฐ์ง ๋ชจ๋ ์ธํฌ๋ ์ฃฝ๊ฒ ๋ฉ๋๋ค. (Cell::Alive, x) if x > 3 => Cell::Dead, // ๊ท์น 4: ์ธํฌ ์ฆ์์ผ๋ก ์ ํํ 3๊ฐ์ ์ด์์ ๊ฐ์ง ์ธํฌ๋ ์ด์๋๊ฒ ๋ฉ๋๋ค. (Cell::Dead, 3) => Cell::Alive, // ๊ท์น์ด ์ ์ฉ๋์ง ์๋ ์ธํฌ๋ค์ ์ํ๋ ๊ทธ๋๋ก ์ ์ง๋๊ฒ ๋ฉ๋๋ค. (otherwise, _) => otherwise, }; next[idx] = next_cell; } } self.cells = next; } // ... } }
์ง๊ธ๊น์ง๋ ์ธ์์ ์ํ๋ฅผ ์ธํฌ๋ค์ ๋ฒกํฐ๋ก ๋ํ๋์ต๋๋ค. ์กฐ๊ธ ๋ ์ฌ๋์ด ์ฝ๊ธฐ ์ฝ๋๋ก ํ
์คํธ ๋ ๋๋ฌ (text renderer) ๋ฅผ ๊ตฌํํด ๋ณด๋๋ก ํฉ์๋ค. ์ธ์์ ํ์ค ํ์ค์ฉ ํ
์คํธ๋ก ํํ์ ํด๋ณด๋๋ก ํ๋๋ฐ, ์ด์์๋ ์ธํฌ๋ค์ ์ ๋์ฝ๋ ๋ฌธ์ โผ
("black medium square") ๋ก ๋ํ๋ด๊ณ ์ฃฝ์ ์ธํฌ๋ค์ โป
("white medium square") ๋ก ํํํด ๋ณด๊ฒ ์ต๋๋ค.
๋ํ, ๋ฌ์คํธ ์คํ ๋ค๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ Display
ํธ๋ ์ดํธ๋ฅผ ๊ตฌํํด์ ์ฌ๋์ด ์ฝ๊ธฐ ์ฌ์ด ๋ฐฉ์์ผ๋ก ํฌ๋งทํ ์ ์๋๋ก ๋ฉ์๋๋ฅผ ์ถ๊ฐํด ๋ณด๊ฒ ์ต๋๋ค. ์ด ํธ๋ ์ดํธ๋ฅผ ๊ตฌํํ๋ฉด ์๋์ ์ผ๋ก Universe์ ์ธ์คํด์ค(instance)๋ค์ด to_string
๋ฉ์๋๋ฅผ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค.
#![allow(unused)] fn main() { use std::fmt; impl fmt::Display for Universe { fn fmt(&self, f: &mut fmt::Formatter) -> fmt::Result { for line in self.cells.as_slice().chunks(self.width as usize) { for &cell in line { let symbol = if cell == Cell::Dead { 'โป' } else { 'โผ' }; write!(f, "{}", symbol)?; } write!(f, "\n")?; } Ok(()) } } }
๋ง์ง๋ง์ผ๋ก render
๋ฉ์๋์ ํจ๊ป ์์ฑ์๋ฅผ ์ ์ํด์ ์ธํฌ๋ค์ด ์ด์๋๊ณ ์ฃฝ์ด๊ฐ๋ ์ ๊ธฐํ ์ธ์์ ์์ฑํ ์ ์๋๋ก ํด๋ณด๊ฒ ์ต๋๋ค.
#![allow(unused)] fn main() { /// Public ๋ฉ์๋, ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ต์คํฌํธ ํ ์ ์๋๋ก ํจ. #[wasm_bindgen] impl Universe { // ... pub fn new() -> Universe { let width = 64; let height = 64; let cells = (0..width * height) .map(|i| { if i % 2 == 0 || i % 7 == 0 { Cell::Alive } else { Cell::Dead } }) .collect(); Universe { width, height, cells, } } pub fn render(&self) -> String { self.to_string() } } }
๋๋์ด Game of Life์ ๋ฌ์คํธ ์ฝ๋ ๊ตฌํ์ด ๋๋ฌ์ต๋๋ค!
์ด์ wasm-game-of-life
๊ฒฝ๋ก์์ wasm-pack build
๋ฅผ ์คํํด์ ์น์ด์
๋ธ๋ฆฌ ํ์ผ์ ๋ค์ ์ปดํ์ผํด ์ฃผ์ธ์.
์๋ฐ์คํฌ๋ฆฝํธ๋ก ํ์ด์ง ๋ ๋๋งํ๊ธฐ
wasm-game-of-life/www/index.html
์ <pre>
์์๋ฅผ ์ถ๊ฐํด์ ์ธ์์ ๋ ๋๋งํด ๋ด
์๋ค. <pre>
์์๋ฅผ <script>
ํ๊ทธ ๋ฐ๋ก ์์ ์ถ๊ฐํด ์ฃผ์ธ์:
<body>
<pre id="game-of-life-canvas"></pre>
<script src="./bootstrap.js"></script>
</body>
์ถ๊ฐ๋ก, <pre>
๊ฐ ์น์ฌ์ดํธ ์ค๊ฐ์ ํ์๋ ์ ์๋๋ก CSS flex box๋ฅผ ์ฌ์ฉํด ๋ด
์๋ค. wasm-game-of-life/www/index.html
ํ์ผ์ ์ด๊ณ <head>
๋ด์ <style>
ํ๊ทธ๋ฅผ ์ถ๊ฐํด ์ฃผ์ธ์:
<style>
body {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
display: flex;
flex-direction: column;
align-items: center;
justify-content: center;
}
</style>
์ด์ wasm-game-of-life/www/index.js
ํ์ผ ์ต์๋จ ์์นํ ๊ธฐ์กด greet
ํจ์๋ฅผ ์ง์ฐ๊ณ Universe
๋ฅผ ์ํฌํธํ๋ ์ค์ ์ถ๊ฐํด ์ฃผ์ธ์.
import { Universe } from "wasm-game-of-life";
<pre>
์์๋ฅผ pre
์์์ ๋ด์ ๋ค์ ์ ์ฐ์ฃผ๋ฅผ ์์ํด ๋ด
์๋ค.
const pre = document.getElementById("game-of-life-canvas");
const universe = Universe.new();
์ด ์๋ฐ์คํฌ๋ฆฝํธ ํจ์๋ requestAnimationFrame
๋ฃจํ๋ก ์คํํด์ ๋งค ๋ฐ๋ณต๋ง๋ค ์
๋ฐ์ดํธ๋ ์ธ์์ <pre>
์ ๋ฐ์ํ๊ณ Universe::tick
์ ํธ์ถํฉ๋๋ค.
const renderLoop = () => {
pre.textContent = universe.render();
universe.tick();
requestAnimationFrame(renderLoop);
};
๋ ๋๋ง ์ฒ๋ฆฌ๋ฅผ ์์ํ๋ ค๋ฉด ๋ค์ ์ฝ๋๋ฅผ renderLoop
ํจ์ ๋ฐ์ ์ถ๊ฐํด์ ๋ ๋๋ง ๋ฃจํ๋ฅผ ์์ํด ์ฃผ์ธ์:
requestAnimationFrame(renderLoop);
๋ค์ ํ๋ฒ (npm run
์ wasm-game-of-life/www
๊ฒฝ๋ก์์ ์คํํ) ๊ฐ๋ฐ ์๋ฒ๊ฐ ์์ง ๊ตฌ๋ ์ค์ธ์ง ํ์ธํด ์ฃผ์ธ์. http://localhost:8080/ ํ์ด์ง๋ฅผ ์ด๋ฉด ๋ค์ ๋ด์ฉ์ ํ์ธํ ์ ์๊ฒ ๋ฉ๋๋ค:
๋ฉ๋ชจ๋ฆฌ์์ ๋ฐ๋ก ์บ๋ฒ์ค๋ก ๋ ๋๋งํ๊ธฐ
๋ฌ์คํธ ์ฝ๋์์ String
์ ์์ฑ (๋ฐ ํ ๋น) ํ๊ณ wasm-bindgen
๋ก ์ด ์์ฑํ ๊ฐ์ ์ ํจํ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฌธ์์ด๋ก ๋ณํํ๋ฉด ์ธํฌ๋ค์ ๋ถํ์ํ๊ฒ ๋ณต์ฌํ๊ฒ ๋ฉ๋๋ค. ์ฐ๋ฆฌ๊ฐ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋์์ ์ธ์์ ๋๋น์ ๋์ด๋ฅผ ์ด๋ฏธ ์๊ณ ์๊ณ , ์ธํฌ๋ฅผ ๋ง๋๋ ์ฒ๋ฆฌ๊ฐ ์ด๋ฃจ์ด์ง๋ ์น์ด์
๋ธ๋ฆฌ ์ ํ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฝ์ ์ ์๊ธฐ ๋๋ฌธ์, render
๋ฉ์๋๋ฅผ ์์ ํ์ฌ cells
๋ฐฐ์ด์ ์์์ ๊ฐ๋ฆฌํค๋ ํฌ์ธํฐ๋ฅผ ๋์ ๋ฐํํด ๋ณด๋๋ก ํฉ์๋ค.
๊ทธ๋ฆฌ๊ณ ์ ๋์ฝ๋ ๋ฌธ์๋ฅผ ๋ ๋๋งํ์ง ์๊ณ Canvas API ๋ฅผ ๋์ ์ฌ์ฉํด ๋ด ์๋ค. ์ด API๋ฅผ ์ด ๋ถ๋ถ ์ดํ๋ถํฐ ๊ณ์ ์ฌ์ฉํ๊ฒ ์ต๋๋ค.
wasm-game-of-life/www/index.html
ํ์ผ์์ ์ด์ ์ ์ถ๊ฐํ <pre>
๋ฅผ ์ง์ฐ๊ณ ๋ ๋๋ง์ ์ฌ์ฉํ <canvas>
๋ฅผ ์ถ๊ฐํด์ฃผ์ธ์. (<body>
ํ๊ทธ ๋ด์์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ์์์ํค๋ <script>
ํ๊ทธ ์์ ์ถ๊ฐ๋ผ์ผ ํฉ๋๋ค.)
<body>
<canvas id="game-of-life-canvas"></canvas>
<script src='./bootstrap.js'></script>
</body>
๋ฌ์คํธ๋ก ๊ตฌํํ ์ฝ๋์์ ํ์ํ ์ ๋ณด๋ฅผ ์ป์ด์ฌ ์ ์๋๋ก ์ธ์์ ๋์ด, ๋๋น, ์ธํฌ ๋ฐฐ์ด์ ๊ฐ๋ฆฌํค๋ ํฌ์ธํฐ๋ฅผ ๋ฐํํ๋ getter ํจ์๋ค์ ์กฐ๊ธ ๋ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค. ์ด ํจ์๋ค๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ก ๋
ธ์ถ์์ผ์ผ ํ๋ ์ ํ์ธํด ์ฃผ๊ณ , wasm-game-of-life/src/lib.rs
ํ์ผ์ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํด ์ฃผ์ธ์:
#![allow(unused)] fn main() { /// Public ๋ฉ์๋, ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ต์คํฌํธ ํ ์ ์๋๋ก ํจ. #[wasm_bindgen] impl Universe { // ... pub fn width(&self) -> u32 { self.width } pub fn height(&self) -> u32 { self.height } pub fn cells(&self) -> *const Cell { self.cells.as_ptr() } } }
๊ทธ ๋ค์, wasm-game-of-life/www/index.js
ํ์ผ์ wasm-game-of-life
๋ชจ๋์์ Cell
์ ์ํฌํธํ๋ ์ค์ ์ถ๊ฐํด ์ค ๋ค์ ์บ๋ฒ์ค๋ฅผ ๋ ๋๋งํ ๋ ์ฌ์ฉํ ์์ ๋ช ๊ฐ์ง๋ฅผ ์ ์ํด์ฃผ์ธ์:
import { Universe, Cell } from "wasm-game-of-life";
const CELL_SIZE = 5; // px
const GRID_COLOR = "#CCCCCC";
const DEAD_COLOR = "#FFFFFF";
const ALIVE_COLOR = "#000000";
์ด์ <pre>
ํ๊ทธ์ textContent
๋์ ์ <canvas>
๋ฅผ ์
๋ฐ์ดํธ ํ ์ ์๋๋ก ์๋ฐ์คํฌ๋ฆฝํธ์ ๋๋จธ์ง ์ฝ๋๋ฅผ ๋ค์ ์์ฑํด์ฃผ์ธ์:
// Universe๋ฅผ ์์ฑํ๊ณ ๋๋น์ ๋์ด๋ฅผ ๋ฐํ๋ฐ์ต๋๋ค.
const universe = Universe.new();
const width = universe.width();
const height = universe.height();
// ์บ๋ฒ์ค์ ์ธํฌ๋ค์ ํ์ํ ๊ณต๊ฐ์ ๋ง๋ค์ด์ฃผ๊ณ , ๊ฐ ์ธํฌ๋ค์ด 1px ๋๊ป์ ํ
๋๋ฆฌ๋ฅผ ๊ฐ์ง ์ ์๋๋ก ํด์ค๋๋ค.
const canvas = document.getElementById("game-of-life-canvas");
canvas.height = (CELL_SIZE + 1) * height + 1;
canvas.width = (CELL_SIZE + 1) * width + 1;
const ctx = canvas.getContext('2d');
const renderLoop = () => {
universe.tick();
drawGrid();
drawCells();
requestAnimationFrame(renderLoop);
};
์ธํฌ๋ค ์ฌ์ด์ ๊ฒฉ์๋ฅผ ๊ทธ๋ฆฌ๋ ค๋ฉด ์ผ์ ํ ๊ฐ๊ฒฉ์ผ๋ก ๋๋ํ ๋์ธ ์ํ์ ๊ณผ ์์ง์ ์ ๊ทธ๋ ค์ค๋๋ค. ์ด๋ฌํ ์ ๋ค์ ๊ต์ฐจํ์ฌ ๊ฒฉ์๋ฅผ ํ์ฑํ๊ฒ ๋ฉ๋๋ค.
const drawGrid = () => {
ctx.beginPath();
ctx.strokeStyle = GRID_COLOR;
// ์์ง์ค
for (let i = 0; i <= width; i++) {
ctx.moveTo(i * (CELL_SIZE + 1) + 1, 0);
ctx.lineTo(i * (CELL_SIZE + 1) + 1, (CELL_SIZE + 1) * height + 1);
}
// ์ํ์ค
for (let j = 0; j <= height; j++) {
ctx.moveTo(0, j * (CELL_SIZE + 1) + 1);
ctx.lineTo((CELL_SIZE + 1) * width + 1, j * (CELL_SIZE + 1) + 1);
}
ctx.stroke();
};
raw wasm ๋ชจ๋์ธ wasm_game_of_life_bg
์ ์ ์๋ memory
๋ฅผ ์ํฌํธํด์ ์น์ด์
๋ธ๋ฆฌ์ ์ ํ ๋ฉ๋ชจ๋ฆฌ์ ์ง์ ์ ๊ทผํ ์ ์์ต๋๋ค. ์ธํฌ๋ฅผ ๊ทธ๋ฆฌ๋ ค๋ฉด ์ฐ์ ์ธ์์ ์๋ ์ธํฌ๋ค์ ๊ฐ๋ฆฌํค๋ ํฌ์ธํฐ๋ฅผ ๋ฐํ๋ฐ๊ณ ์ธํฌ ๋ฒํผ(buffer)๋ฅผ ์ค๋ฒ๋ ์ด(overlay)ํ๋ Uint8Array
๊ฐ์ฒด๋ฅผ ์์ฑํด์ผ ํฉ๋๋ค. ๊ทธ ๋ค์ ๊ฐ ์ธํฌ๋ฅผ ์ํํ์ฌ ์์กด ์ฌ๋ถ์ ๋ฐ๋ผ ํฐ์ ๋๋ ๊ฒ์์ ์ฌ๊ฐํ์ ๊ทธ๋ฆฝ๋๋ค. ํฌ์ธํฐ๋ฅผ ์ค๋ฒ๋ ์ด ํ๊ฒ ๋๋ฉด์, ๋ชจ๋ ํฑ๋ง๋ค ์ธํฌ๋ค์ ๋ณต์ฌํ์ง ์๋๋ก ์ต์ ํ ์์
์ ํด์ค ์ ์์ต๋๋ค.
// ์น์ด์
๋ธ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ํ์ผ ์ต์๋จ์ ์ํฌํธ ํด์ค๋๋ค.
import { memory } from "wasm-game-of-life/wasm_game_of_life_bg";
// ...
const getIndex = (row, column) => {
return row * width + column;
};
const drawCells = () => {
const cellsPtr = universe.cells();
const cells = new Uint8Array(memory.buffer, cellsPtr, width * height);
ctx.beginPath();
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const idx = getIndex(row, col);
ctx.fillStyle = cells[idx] === Cell.Dead
? DEAD_COLOR
: ALIVE_COLOR;
ctx.fillRect(
col * (CELL_SIZE + 1) + 1,
row * (CELL_SIZE + 1) + 1,
CELL_SIZE,
CELL_SIZE
);
}
}
ctx.stroke();
};
์ด์ ์ ๋ณด์ฌ๋๋ฆฐ ์ฝ๋๋ฅผ ์ฌ์ฉํด์ ๋ ๋๋ง ์ฒ๋ฆฌ๋ฅผ ์์ํด ๋ณด๊ฒ ์ต๋๋ค:
drawGrid();
drawCells();
requestAnimationFrame(renderLoop);
drawGrid()
์ drawCells()
๋ ํจ์๋ฅผ requestAnimationFrame()
ํจ์ ํธ์ถ ์ด์ ์ ํธ์ถํด์ผ ํ๋ค๋ ์ ์ ๊ผญ ๊ธฐ์ตํด ์ฃผ์ธ์. ์ด๊ธฐ ์ํ์ ์ธ์์ด ๊ทธ๋ ค์ง ์ดํ์ ์์ ์ฌํญ์ ์ ์ฉํด์ผ ํฉ๋๋ค. requestAnimationFrame(renderLoop)
ํจ์๋ง ํธ์ถํ๊ฒ ๋๋ค๋ฉด universe.tick()
์ด ํธ์ถ๋ ์ดํ ์์ ์ ๋ ๋ฒ์งธ ํฑ์ด ๋์ ๊ทธ๋ ค์ง๊ฒ ๋ฉ๋๋ค.
๋ค ๋์ด์!
์ต์๋จ wasm-game-of-life
๊ฒฝ๋ก์์ ๋ค์ ๋ช
๋ น์ด๋ฅผ ์คํํ์ฌ ์น์ด์
๋ธ๋ฆฌ์ ๋ฐ์ธ๋ฉ ํ์ผ (bindings glue) ๋ค์ ๋ค์ ๋น๋ํด์ค์๋ค:
wasm-pack build
๋ค์ ํ๋ฒ ๊ฐ๋ฐ ์๋ฒ๊ฐ ์์ง ๊ตฌ๋ ์ค์ธ์ง ํ์ธํด์ฃผ์ธ์. ๊ตฌ๋ ์ค์ด์ง ์๋ค๋ฉด wasm-game-of-life/www
๊ฒฝ๋ก์์ ๋ค์ ์์ํด์ฃผ์ธ์:
npm run start
http://localhost:8080/ ํ์ด์ง๋ฅผ ์น ๋ธ๋ผ์ฐ์ ์์ ์๋ก๊ณ ์นจํ๋ฉด ๊ตฌํ๋ ํฅ๋ฏธ์ง์งํ Game of Life๊ฐ ์์๋๊ฒ ๋ฉ๋๋ค.
์ถ๊ฐ๋ก ๊ด์ฌ์ด ์๋ค๋ฉด, hashlife ๋ผ๋ ์์ฒญ ๋ฉ์ง Game of Life ์๊ณ ๋ฆฌ์ฆ ๊ตฌํ๋ ์์ผ๋ ํ๋ฒ ํ์ธํด ๋ณด์ธ์. ์ด ์๊ณ ๋ฆฌ์ฆ์ ๊ณต๊ฒฉ์ ์ธ ๋ฉ๋ชจ์ด์ ์ด์ (aggressive memoizing) ๊ธฐ๋ฒ์ ์ฌ์ฉํ๋๋ฐ, ๋๋ถ์ ์ฝ๋๊ฐ ๋ ์ค๋ ๊ตฌ๋๋๋ ๋งํผ ๋ฏธ๋ ์ธ๋๋ค์ ๊ธฐํ๊ธ์์ ์ผ๋ก ๋ ๋น ๋ฅด๊ฒ ๊ณ์ฐํ ์ ์๊ฒ ํด์ค๋๋ค. hashlife๋ฅผ ์ด ํํ ๋ฆฌ์ผ์์ ๊ตฌํํด ๋ณด๋ฉด ์ ๋ง ์ฌ๋ฐ๊ฒ ์ง๋ง, ์ด ์ฑ ์ ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ ์ฌ์ฉ์ ์ค์ ์ ๋๊ณ ์์ผ๋ฏ๋ก ๋ค๋ฃจ์ง ์๋๋ก ํ๊ฒ ์ต๋๋ค. ํ์ง๋ง hashlife์ ๋ํด ๋ฐ๋ก ๋ฐฐ์๋ณด๊ธธ ์ ๊ทน์ ์ผ๋ก ๊ถ์ฅํฉ๋๋ค.
์ฐ์ตํด ๋ณด๊ธฐ
-
space ship ํจํด ํ๋๋ฅผ ํ์ํ๋ ์ธ์์ ๋ง๋ค์ด๋ณด์ธ์.
-
์ด๊ธฐ ์ธ์์ ํ๋์ฝ๋ฉ ํ๋ ๋์ , ๊ฐ ์ธํฌ๊ฐ 50% ํ๋ฅ ๋ก ์ด์์๊ฑฐ๋ ์ฃฝ์ด์๋ ์ํ๋ก ๋๋คํ๊ฒ ์์ฑ๋ ์ ์๋๋ก ํด๋ณด์ธ์.
ํํธ: the
js-sys
crate ํฌ๋ ์ดํธ๋ฅผ ์ฌ์ฉํ์ฌMath.random
์๋ฐ์คํฌ๋ฆฝํธ ํจ์๋ฅผ ์ํฌํธํด๋ณด์ธ์.์ ๋ต
๋จผ์ ,
wasm-game-of-life/Cargo.toml
ํ์ผ์ ์ด๊ณjs-sys
๋ฅผ ์ข ์์ฑ์ผ๋ก ์ถ๊ฐํด ์ฃผ์ธ์:# ... [dependencies] js-sys = "0.3" # ...
๊ทธ ๋ค์
js_sys::Math::random
ํจ์๋ฅผ ์ฌ์ฉํด์ 50% ํ๋ฅ ๋ก ๊ฐ์ ๊ฒฐ์ ํด ์ฃผ์ธ์:#![allow(unused)] fn main() { extern crate js_sys; // ... if js_sys::Math::random() < 0.5 { // ์ธํฌ๊ฐ ์ด์์๊ฒ ํจ } else { // ์ธํฌ๊ฐ ์ฃฝ์ด์๊ฒ ํจ } }
-
๊ฐ ์ธํฌ๋ฅผ byte ๊ฐ์ผ๋ก ํํํ๋ฉด์ ์ํ๋ฅผ ์ฝ๊ฒ ํ ์ ์์ง๋ง, ๋ฉ๋ชจ๋ฆฌ ์์์ ๋ญ๋นํ๋ค๋ ๋จ์ ์ด ์์ต๋๋ค. 1 byte๋ 8 bit์ธ๋ฐ, ์ค์ ๋ก ์ธํฌ ์์กด ์ฌ๋ถ๋ฅผ ํ์ํ ๋๋ 1 bit๋ง ์ฌ์ฉํ๊ณ ์์ต๋๋ค. ์ด ๋ฐ์ดํฐ ํํ๋ค์ ๋ฆฌํฉํ ๋งํ์ฌ ๊ฐ ์ธํฌ๊ฐ 1 bit์ ์ฌ์ด์ฆ๋ง ์ฌ์ฉํ ์ ์๋๋ก ์ฝ๋๋ฅผ ์์ฑํด ๋ณด์ธ์.
์ ๋ต
๋ฌ์คํธ ์ธ์ด์์
Vec<Cell>
ํ์ ๋์ (์ถ๊ฐ ๊ธฐ๋ฅ์ ์ ๊ณตํ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ธ)fixedbitset
ํฌ๋ ์ดํธ์FixedBitSet
ํ์ ์ ์ฌ์ฉํ์ฌ ์ธํฌ๋ค์ ๋ํ๋ด๋ณผ ์๋ ์์ต๋๋ค.#![allow(unused)] fn main() { // Cargo.toml์ ์ข ์์ฑ์ ์ถ๊ฐํ๋์ง ํ์ธํด ์ฃผ์ธ์! extern crate fixedbitset; use fixedbitset::FixedBitSet; // ... #[wasm_bindgen] pub struct Universe { width: u32, height: u32, cells: FixedBitSet, } }
Universe์ ์์ฑ์๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์์ ํด ๋ณด๊ฒ ์ต๋๋ค:
#![allow(unused)] fn main() { pub fn new() -> Universe { let width = 64; let height = 64; let size = (width * height) as usize; let mut cells = FixedBitSet::with_capacity(size); for i in 0..size { cells.set(i, i % 2 == 0 || i % 7 == 0); } Universe { width, height, cells, } } }
๋ค์ ํฑ์์ ์ธํฌ๋ฅผ ์ ๋ฐ์ดํธํ ์ ์๋๋ก
FixedBitSet
ํ์ ์set
๋ฉ์๋๋ฅผ ์ฌ์ฉํด ๋ด ์๋ค:#![allow(unused)] fn main() { next.set(idx, match (cell, live_neighbors) { (true, x) if x < 2 => false, (true, 2) | (true, 3) => true, (true, x) if x > 3 => false, (false, 3) => true, (otherwise, _) => otherwise }); }
์๋ฐ์คํฌ๋ฆฝํธ์์ ์์ํ๋ bit๋ฅผ ๊ฐ๋ฆฌํค๋ ํฌ์ธํฐ๋ฅผ ๋ฐํํด์ผ ํ๋ฏ๋ก,
FixedBitSet
ํ์ ์ ์ฌ๋ผ์ด์ค(slice)๋ก ๋ณํํ ๋ค์ ๋ณํ๋ ์ฌ๋ผ์ด์ค๋ฅผ ํฌ์ธํฐ๋ก ๋ค์ ๋ณํํด ์ฃผ์ธ์:#![allow(unused)] fn main() { #[wasm_bindgen] impl Universe { // ... pub fn cells(&self) -> *const u32 { self.cells.as_slice().as_ptr() } } }
์๋ฐ์คํฌ๋ฆฝํธ์ ์น์ด์ ๋ธ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ์์
Uint8Array
๋ฅผ ์์ฑํด์ค๋ ๋ฐฉ๋ฒ์ ์ด์ ๊ณผ ๋์ผํฉ๋๋ค. ํ์ง๋ง ์ด๋ฒ์๋ ๊ฐ ์ธํฌ๋ฅผ ๋ํ๋ด๊ธฐ ์ํด byte ๋์ bit์ ์ฌ์ฉํ๋ฏ๋ก ๋ฐฐ์ด์ ๊ธธ์ด๊ฐ ๋ ์ด์width * height
๊ฐ ์๋๊ณwidth * height / 8
์ด ๋์ด์ผ ํ๋ ๋ค์ ์ ํ์ธํด ์ฃผ์ธ์:const cells = new Uint8Array(memory.buffer, cellsPtr, width * height / 8);
๋ค์ ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ธ๋ฑ์ค์
Uint8Array
๊ฐ ์ฃผ์ด์ง ๋ n๋ฒ์งธ bit์ ๊ฐ์ด 0์ธ์ง 1์ธ์ง ํ์ธํ ์ ์์ต๋๋ค:const bitIsSet = (n, arr) => { const byte = Math.floor(n / 8); const mask = 1 << (n % 8); return (arr[byte] & mask) === mask; };
์ด์ ์ค๋น๊ฐ ๋์ผ๋
drawCells
ํจ์๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ์ ๋ฐ์ดํธํด์ค์๋ค:const drawCells = () => { const cellsPtr = universe.cells(); // ์์ ๋ ๋ถ๋ถ์ ๋๋ค! const cells = new Uint8Array(memory.buffer, cellsPtr, width * height / 8); ctx.beginPath(); for (let row = 0; row < height; row++) { for (let col = 0; col < width; col++) { const idx = getIndex(row, col); // ์์ ๋ ๋ถ๋ถ์ ๋๋ค! ctx.fillStyle = bitIsSet(idx, cells) ? ALIVE_COLOR : DEAD_COLOR; ctx.fillRect( col * (CELL_SIZE + 1) + 1, row * (CELL_SIZE + 1) + 1, CELL_SIZE, CELL_SIZE ); } } ctx.stroke(); };
Game of Life ํ ์คํ ํ๊ธฐ
๋ธ๋ผ์ฐ์ ์๋ฐ์คํฌ๋ฆฝํธ ํ๊ฒฝ์์ ์คํํ ์ ์๋๋ก Game of Life๋ฅผ ๊ตฌํํ์ผ๋ ์ด์ ๋ฌ์คํธ ์ฝ๋์์ ์น์ด์ ๋ธ๋ฆฌ ํจ์๋ฅผ ํ ์คํ ํ๋ ๋ฐฉ๋ฒ์ ๋ํด ์์๋ด ์๋ค.
tick
ํจ์๋ก ์์๊ฐ๊ณผ ์ผ์นํ๋ ์ฌ๋ฐ๋ฅธ ๊ฐ์ ๋ถ๋ฌ์ฌ ์ ์๋์ง ํ
์คํ
ํด๋ณด๊ฒ ์ต๋๋ค.
์ฐ์ ํ
์คํ
์ ์์ฑํ๊ธฐ ์ ์ wasm_game_of_life/src/lib.rs
ํ์ผ์ ์์ฑํ impl Universe
๋ธ๋ญ ๋ด๋ถ์ setter ํจ์์ getter ํจ์๋ค์ ์กฐ๊ธ ๋ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค. set_width
์ set_height
ํจ์๋ฅผ ์์ฑํด์ ๋ค๋ฅธ ์ฌ์ด์ฆ์ Universe
๋ค์ ๋ง๋ค์ด๋ณผ ์ ์๋๋ก ํด๋ด
์๋ค.
#![allow(unused)] fn main() { #[wasm_bindgen] impl Universe { // ... /// ์ธ์์ ๋๋น๋ฅผ ์ค์ ํฉ๋๋ค. /// /// ๋ชจ๋ ์ธํฌ๋ฅผ ์ฃฝ์ ์ํ๋ก ๋ฆฌ์ ํฉ๋๋ค. pub fn set_width(&mut self, width: u32) { self.width = width; self.cells = (0..width * self.height).map(|_i| Cell::Dead).collect(); } /// ์ธ์์ ๋์ด๋ฅผ ์ค์ ํฉ๋๋ค. /// /// ๋ชจ๋ ์ธํฌ๋ฅผ ์ฃฝ์ ์ํ๋ก ๋ฆฌ์ ํฉ๋๋ค. pub fn set_height(&mut self, height: u32) { self.height = height; self.cells = (0..self.width * height).map(|_i| Cell::Dead).collect(); } } }
wasm_game_of_life/src/lib.rs
ํ์ผ์ #[wasm_bindgen]
์์ฑ ์์ด impl Universe
๋ธ๋ญ์ ํ๋ ๋ ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค. ์ถ๊ฐ๋ก ํ
์คํ
์ ์ฌ์ฉํ๋ ๋ฐ ํ์ํ ํจ์๊ฐ ๋ช ๊ฐ ์๋๋ฐ, ์ด ํจ์๋ค์ ์๋ฐ์คํฌ๋ฆฝํธ๋ก ๋
ธ์ถ์ํค์ง ์์์ผ ํฉ๋๋ค. ๋ฌ์คํธ๋ก ์์ฑํ ์น์ด์
๋ธ๋ฆฌ ํจ์๋ ๋์ฌํ ์ฐธ์กฐ๋ฅผ ๋ฐํํ์ง ๋ชปํ๋๋ฐ, ์ด ํจ์๋ค ์์ #[wasm_bindgen]
์์ฑ์ ์ถ๊ฐํด ๋ณด๊ณ ์ด๋ค ์๋ฌ๋ฅผ ํ์ธํ ์ ์๊ฒ ๋๋์ง ์ดํด๋ด
์๋ค.
get_cells
ํจ์๋ฅผ ๊ตฌํํด์ Universe
์ cells
ํ๋ ๊ฐ์ ๊ฐ์ ธ์ ๋ณด๊ฒ ์ต๋๋ค. set_cells
ํจ์๋ ์์ฑํด์ ์ฃผ์ด์ง ํ๊ณผ ์ด์ ์์นํ Universe
์ ์ธํฌ๋ฅผ Alive
์ํ๋ก ์
๋ฐ์ดํธํ ์ ์๋๋ก ํด๋ณด๊ฒ ์ต๋๋ค.
#![allow(unused)] fn main() { impl Universe { /// ์ธ์์ ์กด์ฌํ๋ ๋ชจ๋ ์ฃฝ์ด์๋ ์ธํฌ์ ์ด์์๋ ์ธํฌ๋ฅผ ๋ฐํํฉ๋๋ค. pub fn get_cells(&self) -> &[Cell] { &self.cells } /// ๋ฐฐ์ด๋ก ์ฃผ์ด์ง ํ๊ณผ ์ด๋ค์ ํ์ธํ๊ณ ์ธํฌ๋ค์ ์ด์์๋ ์ํ๋ก ์ ๋ฐ์ดํธํฉ๋๋ค. pub fn set_cells(&mut self, cells: &[(u32, u32)]) { for (row, col) in cells.iter().cloned() { let idx = self.get_index(row, col); self.cells[idx] = Cell::Alive; } } } }
์ด์ wasm_game_of_life/tests/web.rs
ํ์ผ์ ํ
์คํ
์ฝ๋๋ฅผ ์์ฑํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
์งํํ๊ธฐ ์ ์, ์ด๋ฏธ ์์ฑ๋ ํ
์คํ
์ฝ๋๊ฐ ์์ผ๋ ํ๋ฒ ์ดํด๋ด
์๋ค. wasm-game-of-life
๊ฒฝ๋ก์์ wasm-pack test --chrome --headless
๋ช
๋ น์ด๋ฅผ ์คํํ์ฌ ๋ฌ์คํธ๋ก ์์ฑํ ์น์ด์
๋ธ๋ฆฌ ํ
์คํ
์ฝ๋๊ฐ ์ ์๋ํ๋์ง ํ์ธํ ์ ์์ต๋๋ค. --firefox
, --safari
, --node
์ต์
์ ์ฌ์ฉํ์ฌ ํน์ ๋ธ๋ผ์ฐ์ ํ๊ฒฝ์์ ์ฝ๋๋ฅผ ํ
์คํธํ ์๋ ์์ต๋๋ค.
์ฐ์ wasm_game_of_life/tests/web.rs
ํ์ผ์์, wasm_game_of_life
ํฌ๋ ์ดํธ์ Universe
๋ฅผ ์ต์คํฌํธ ํด์ค์๋ค.
#![allow(unused)] fn main() { extern crate wasm_game_of_life; use wasm_game_of_life::Universe; }
wasm_game_of_life/tests/web.rs
ํ์ผ์์ spaceship ์์ ํจํด์ ๋ง๋๋ ํจ์๋ค์ ์์ฑํด ๋ด
์๋ค.
tick
ํจ์๋ฅผ ํธ์ถํ ๋ ์ฌ์ฉํ ํจํด๊ณผ, ํ ํฑ ์ดํ์ ๊ฒฐ๊ด๊ฐ์ ๋น๊ตํ ๋ ์ฌ์ฉํ ์์๊ฐ์ด ํ์ํ๋ฐ, ์ฐ์ ์ input_spaceship
ํจ์์์ ์ด๋ค ์ธํฌ๋ค์ Alive
์ํ๋ก ์์ฑํ ์ง ์ ํด์ค์๋ค. expected_spaceship
ํจ์๋ฅผ ํ
์คํธํด ๋ณด๋๋ฐ, input_spaceship
ํธ์ถ ์ดํ ์์ ํฑ์ spaceship ํจํด์ ์ง์ ๊ณ์ฐํด์ ๊ฐ์ ์ฑ์๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
#![allow(unused)] fn main() { #[cfg(test)] pub fn input_spaceship() -> Universe { let mut universe = Universe::new(); universe.set_width(6); universe.set_height(6); universe.set_cells(&[(1,2), (2,3), (3,1), (3,2), (3,3)]); universe } #[cfg(test)] pub fn expected_spaceship() -> Universe { let mut universe = Universe::new(); universe.set_width(6); universe.set_height(6); universe.set_cells(&[(2,1), (2,3), (3,2), (3,3), (4,2)]); universe } }
๋ง์ง๋ง์ผ๋ก test_tick
ํจ์๋ฅผ ๊ตฌํํด ์ฃผ๋๋ก ํ๊ฒ ์ต๋๋ค. ๋จผ์ input_spaceship()
์ expected_spaceship()
๋ฅผ ํธ์ถํด์ ์ธ์คํด์ค๋ค์ ๋ง๋ค์ด์ค์๋ค. ๊ทธ๋ค์์ input_universe
์ธ์คํด์ค์ tick
ํจ์๋ฅผ ํธ์ถํด ์ฃผ๋๋ก ํฉ์๋ค. ์ถ๊ฐ๋ก, assert_eq!
๋งคํฌ๋ก๋ฅผ ์ฌ์ฉํด์ get_cells()
๋ฅผ ํธ์ถํ ๋ค์ input_universe
์ expected_universe
๊ฐ ๋์ผํ Cell
ํ์
์ ๋ฐฐ์ด์ ๊ฐ์ผ๋ก ๊ฐ์ง๊ณ ์๋์ง ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค. ๋ง๋ฌด๋ฆฌ๋ก๋ ์ด ์ฝ๋ ๋ธ๋ญ ์์ #[wasm_bindgen_test]
์์ฑ์ ์ถ๊ฐํด์ wasm-pack test
๋ช
๋ น์ด๋ก ๋ฌ์คํธ๋ก ์์ฑํ ์น์ด์
๋ธ๋ฆฌ ์ฝ๋๋ฅผ ํ
์คํธํ ์ ์๋๋ก ํด์ค์๋ค.
#![allow(unused)] fn main() { #[wasm_bindgen_test] pub fn test_tick() { // ์์ ์ฌ์ด์ฆ์ Universe๊ณผ spaceship ํจํด์ ์์ฑํด ๋ด ์๋ค. let mut input_universe = input_spaceship(); // `input_universe` ์ธ์คํด์ค์ ๋ค์ ํฑ ๊ฒฐ๊ด๊ฐ๊ณผ ๋น๊ตํ๊ฒ ๋ spaceship ํจํด์ ๋๋ค. let expected_universe = expected_spaceship(); // `tick` ํจ์๋ฅผ ์คํํ๊ณ ๋ `Universe`์ ์ธํฌ๋ค์ด ๋์ผํ์ง ํ์ธํฉ๋๋ค. input_universe.tick(); assert_eq!(&input_universe.get_cells(), &expected_universe.get_cells()); } }
์ด์ wasm-game-of-life
๊ฒฝ๋ก์์ wasm-pack test --firefox --headless
๋ช
๋ น์ด๋ฅผ ์คํํ์ฌ ํ
์คํ
์ฝ๋๋ฅผ ์คํํด ์ฃผ๋ฉด ๋ฉ๋๋ค.
๋๋ฒ๊น
์ฝ๋๋ฅผ ๋ ์์ฑํ๊ธฐ ์ ์, ๋๋ฒ๊น ํด๋ค์ ์กฐ๊ธ ์ดํด๋ณด๊ณ ์ฅ์ ํด ๋ณผ๊น์? ๊ด์ฌ์ด ์๋ค๋ฉด ๋ฌ์คํธ๋ก ์น์ด์ ๋ธ๋ฆฌ ๋ฐ์ด๋๋ฆฌ๋ฅผ ์์ฑํ๋ ๋ฐ ์ฌ์ฉํด ๋ณผ ์ ์๋ ์ ๊ทผ ๋ฐฉ๋ฒ๋ค๊ณผ ํด๋ค์ ๋ํด ๋ค๋ฃจ๋ ์ฐธ์กฐ ํ์ด์ง๋ ํ์ธํด ๋ณด์ธ์!
ํจ๋ ๋ก๊ทธ ํ์ฑํํ๊ธฐ
ํ์๋ก ์ฌ์ฉํ ํ์๋ ์์ง๋ง wasm-pack-template
์ wasm-game-of-life/src/utils.rs
ํ์ผ์ ๊ธฐ๋ณธ์ผ๋ก ํ์ฑํ๋ผ ์๋ ์ข
์์ฑ์ธ the console_error_panic_hook
ํฌ๋ ์ดํธ๋ฅผ ํฌํจํฉ๋๋ค. ์ด ํ
(hook)์ ์์ฑ์์ ํฌํจ์์ผ์ฃผ๋ฉด ์ด ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค. wasm-game-of-life/src/lib.rs
ํ์ผ ๋ด์ Universe::new
์์ฑ์์์ ๋ถ๋ฌ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค.
#![allow(unused)] fn main() { pub fn new() -> Universe { utils::set_panic_hook(); // ... } }
๊ตฌํํ Game of Life์ ๋ก๊ทธ ๊ธฐ๋ฅ ์ถ๊ฐํ๊ธฐ
web-sys
ํฌ๋ ์ดํธ๋ฅผ ํตํด console.log
ํจ์๋ฅผ ์ฌ์ฉํด ๋ณด๊ณ , ๋งค ์ธํฌ๋ค์ ์ ๋ณด๋ฅผ ๋ก๊ทธ ํ ์ ์๋๋ก ๊ธฐ๋ฅ์ ์ถ๊ฐํด ๋ณด๊ฒ ์ต๋๋ค.
์ฐ์ , wasm-game-of-life/Cargo.toml
ํ์ผ์ ์ด๊ณ web-sys
๋ฅผ ์ข
์์ฑ์ผ๋ก ์ถ๊ฐํ ๋ค์ "console"
๊ธฐ๋ฅ์ ํ์ฑํํด ์ฃผ์ธ์:
[dependencies]
# ...
[dependencies.web-sys]
version = "0.3"
features = [
"console",
]
๋ ๋์ ๊ฐ๋ฐ์ ๊ฒฝํ์ ์ํด console.log
ํจ์๋ฅผ println!
์คํ์ผ์ ๋งคํฌ๋ก๋ก ๊ฐ์ธ๋ณด๊ฒ ์ต๋๋ค:
#![allow(unused)] fn main() { extern crate web_sys; // `println!(...)` ์คํ์ผ์ ๋ฌธ๋ฒ์ `console.log`์ ์ฌ์ฉํ ์ ์๊ฒ ํด์ฃผ๋ ๋งคํฌ๋ก. macro_rules! log { ( $( $t:tt )* ) => { web_sys::console::log_1(&format!( $( $t )* ).into()); } } }
์ด์ ๋ฌ์คํธ ์ฝ๋์์ log
๋งคํฌ๋ก๋ฅผ ํธ์ถํด์ ์ฝ์์ ๋ฉ์ธ์ง๋ฅผ ๋ก๊ทธ ํด๋ด
์๋ค. ์๋ฅผ ๋ค์ด์, ์ธํฌ์ ์ํ, ์ด์ ์, ๋ค์ ํฑ ์ํ๋ฅผ ๋ก๊ทธ ํ ์ ์๋๋ก wasm-game-of-life/src/lib.rs
ํ์ผ์ ์์ ํด ์ฃผ์ธ์:
diff --git a/src/lib.rs b/src/lib.rs
index f757641..a30e107 100755
--- a/src/lib.rs
+++ b/src/lib.rs
@@ -123,6 +122,14 @@ impl Universe {
let cell = self.cells[idx];
let live_neighbors = self.live_neighbor_count(row, col);
+ log!(
+ "cell[{}, {}] is initially {:?} and has {} live neighbors",
+ row,
+ col,
+ cell,
+ live_neighbors
+ );
+
let next_cell = match (cell, live_neighbors) {
// ๊ท์น 1: ์ธ๊ตฌ ๋ถ์กฑ์ผ๋ก 2๊ฐ ๋ฏธ๋ง์ ์ด์์ ๊ฐ์ง ์ธํฌ๋ ์ฃฝ๊ฒ ๋ฉ๋๋ค.
@@ -140,6 +147,8 @@ impl Universe {
(otherwise, _) => otherwise,
};
+ log!(" it becomes {:?}", next_cell);
+
next[idx] = next_cell;
}
}
๋๋ฒ๊ฑฐ๋ฅผ ์ฌ์ฉํ์ฌ ๋งค ํฑ๋ง๋ค ์ผ์์ ์ง ์ํค๊ธฐ
์๋ฅผ ๋ค์ด์, universe.tick()
ํธ์ถ ์ด์ ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋์ debugger;
์ค์ ์ถ๊ฐํ๋ฉด ๋๋ฒ๊น
ํด์ ์ฌ์ฉํ์ฌ renderLoop
์ ๋งค ์ํ๋ง๋ค ์ฝ๋ ์คํ์ ์ผ์์ ์ง์ํฌ์ ์๊ฒ ๋ฉ๋๋ค.
const renderLoop = () => {
debugger;
universe.tick();
drawGrid();
drawCells();
requestAnimationFrame(renderLoop);
};
์ด์ ๋ก๊ทธ ๋ฉ์ธ์ง๋ฅผ ์ฝ๊ฒ ์ดํด๋ณผ ์ ์๋๋ก ๊ฐํธํ ์ฒดํฌํฌ์ธํธ(checkpoint) ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๊ฒ ๋์ต๋๋ค. ์ด ๊ธฐ๋ฅ์ ํตํด ํ์ฌ ๋ ๋๋ ํ๋ ์๊ณผ ์ด์ ํ๋ ์์ ๋น๊ตํ ์ ์์ต๋๋ค.
์ฐ์ตํด ๋ณด๊ธฐ
-
์ฃฝ๊ฒ ๋๊ฑฐ๋ ์ด์๋๊ฒ ๋๋ ์์ผ๋ก ์ํ๊ฐ ์ ํ๋๋ ๊ฐ๊ฐ ์ธํฌ์ ํ๊ณผ ์ด์ ๊ธฐ๋กํ ์ ์๋๋ก
tick
ํจ์๋ฅผ ๋ก๊ทธ ํ๋ ์ฝ๋๋ฅผ ์ถ๊ฐํด ๋ณด์ธ์. -
Universe::new
๋ฉ์๋์panic!()
๋งคํฌ๋ก๋ฅผ ์ถ๊ฐํด์ ์น ๋ธ๋ผ์ฐ์ ์ ์๋ฐ์คํฌ๋ฆฝํธ ๋๋ฒ๊น ํด์์ ํจ๋ํ ์ฝ๋๋ฅผ ํด๊ฐ๊ฒ์(backtrace) ํ ์ ์๋๋ก ์์ ํด ๋ณด์ธ์. ์ถ๊ฐ๋ก debug ์ฌ๋ณผ์ ๋นํ์ฑํํ ๋ค์console_error_panic_hook
์ ํ์ ์ข ์์ฑ์ ๋ค์ ๋น๋ํ์ ๋ ์คํ ์ถ์ ๋ ๋ค์ ํ์ธํด ๋ณด์ธ์. ๊ทธ๋ ๊ฒ ์ ์ฉํ์ง ์์ ๊ฒ ๊ฐ์๋ฐ, ์ ๊ทธ๋ฐ๊ฐ์?
์ํธ์์ฉ ์ถ๊ฐํ๊ธฐ
๊ตฌํํ Game of Life์ ์ํธ์์ฉ์ ์กฐ๊ธ ๋ ์ถ๊ฐํด ๋ณด๋ฉด์ ์๋ฐ์คํฌ๋ฆฝํธ์ ์น์ด์ ๋ธ๋ฆฌ ์ธํฐํ์ด์ค๋ฅผ ์กฐ๊ธ ๋ ํํํด ๋ณด๋๋ก ํ๊ฒ ์ต๋๋ค. ์ด ์น์ ์์๋ ์ ์ ๋ค์ด ์ธํฌ๋ฅผ ํด๋ฆญํด์ ์์กด ์ฌ๋ถ๋ฅผ ์ ํ์ํฌ์ ์๋๋ก ํด๋ณด๊ณ , ์ผ์์ ์ง ๊ธฐ๋ฅ๋ ๊ตฌํํด์ ์ธํฌ ํจํด์ ์ ์ ๋ค์ด ์์ฝ๊ฒ ๊ทธ๋ ค๋ณผ ์ ์๋๋ก ํด๋ณผ ์์ ์ ๋๋ค.
์ผ์์ ์ง ๋ฒํผ
๊ฒ์์ ์ผ์์ ์ง ์ํฌ ์ ์๋๋ก ๋ฒํผ์ ํ๋ ๋ง๋ค์ด ๋ด
์๋ค. wasm-game-of-life/www/index.html
ํ์ผ ๋ด์ <canvas>
๋ฐ๋ก ์์ ์ด ๋ฒํผ์ ์ถ๊ฐํด์ฃผ์ธ์:
<button id="play-pause"></button>
wasm-game-of-life/www/index.js
์๋ฐ์คํฌ๋ฆฝํธ ํ์ผ์ ๋ค์ ๋ณ๊ฒฝ์ฌํญ๋ค์ ์ ์ฉํด ๋ณด๊ฒ ์ต๋๋ค:
-
์ทจ์์ํค๊ณ ์ถ์ ์๋ณ์๋ฅผ
cancelAnimationFrame
ํจ์๋ก ์ ๋ฌ์ํฌ ์ ์๋๋กrequestAnimationFrame
ํจ์๊ฐ ์ ์ผ ๋ง์ง๋ง์ผ๋ก ๋ฐํํ ์๋ณ์๋ฅผ ์ถ์ ํฉ๋๋ค. -
์ฌ์/์ผ์์ ์ง ๋ฒํผ์ ๋๋ ์ ๋, ๋๊ธฐ ์ค์ธ ์ ๋๋ฉ์ด์ ํ๋ ์์ ๊ฐ๋ฆฌํค๋ ์๋ณ์๋ฅผ ๊ฐ์ง๊ณ ์๋์ง ํ์ธํฉ๋๋ค. ์ด ์๋ณ์๋ฅผ ๊ฐ์ง๊ณ ์๋ ์์ ์์๋ ๊ฒ์์ด ์คํ ์ค์ผ ๊ฒ์ด๋ฏ๋ก
renderLoop
๊ฐ ๋ค์ ํธ์ถ๋์ง ์๋๋ก ์ ๋๋ฉ์ด์ ์ ์ผ์์ ์ง ์ํต๋๋ค. ์๋ณ์๋ฅผ ๊ฐ์ง๊ณ ์์ง ์๋ค๋ฉด ๊ฒ์์ด ๋ฉ์ถฐ์๋ค๋ ์๋ฏธ์ด๋ฏ๋กrequestAnimationFrame
ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ๊ฒ์์ ๋ค์ ์์ํด์ฃผ๋๋ก ํฉ๋๋ค.
์ด ์์ ์ ์๋ฐ์คํฌ๋ฆฝํธ ํ๊ฒฝ์์ ์งํํด์ผ ํ๊ธฐ ๋๋ฌธ์ ๋ฐ๋ก ๋ฌ์คํธ ์์ค ์ฝ๋๋ฅผ ์์ ํ ํ์๊ฐ ์์ต๋๋ค.
animationId
๋ณ์๋ฅผ ์ถ๊ฐํ์ฌ requestAnimationFrame
ํจ์๊ฐ ๋ฐํํ๋ ์๋ณ์๋ฅผ ์ถ์ ํด ๋ด
์๋ค. ๋๊ธฐ ์ค์ธ ์ ๋๋ฉ์ด์
์ด ์์ ๋๋ ์ด ๊ฐ์ null
๋ก ์ค์ ํด ์ฃผ๊ฒ ์ต๋๋ค.
let animationId = null;
// `requestAnimationFrame`์ ๋ฐํ๊ฐ์ด `animationId`์
// ํ ๋น๋๋ค๋ ์ ์ธ์๋ ๊ธฐ์กด๊ณผ ๋์ผํ ํจ์์
๋๋ค.
const renderLoop = () => {
drawGrid();
drawCells();
universe.tick();
animationId = requestAnimationFrame(renderLoop);
};
์ธ์ ๋ animationId
์ ๊ฐ์ ํ์ธํด์ ๊ฒ์์ด ๋ฉ์ถฐ์๋์ง ํ์ธํ ์ ์์ต๋๋ค:
const isPaused = () => {
return animationId === null;
};
์ด์ ์ฌ์/์ผ์์ ์ง ๋ฒํผ์ด ํด๋ฆญ ๋์ ๋ ๊ฒ์์ ์ฌ์ ์ฌ๋ถ๋ฅผ ํ์ธํ ์ ์๊ฒ ๋์ต๋๋ค. ๋ง์ฐฌ๊ฐ์ง๋ก renderLoop
ํจ์๋ฅผ ํตํด ์ ๋๋ฉ์ด์
์ ๋ค์ ์์ํ๊ฑฐ๋ ์ผ์์ ์ง ์ํฌ์๋ ์์ต๋๋ค. ์ถ๊ฐ๋ก, ๋ฒํผ์ ํด๋ฆญํ์ ๋ ํ์๋๋ ํ
์คํธ์ ์์ด์ฝ์ ์
๋ฐ์ดํธํด ์ฃผ๋๋ก ํ๊ฒ ์ต๋๋ค.
const playPauseButton = document.getElementById("play-pause");
const play = () => {
playPauseButton.textContent = "โธ";
renderLoop();
};
const pause = () => {
playPauseButton.textContent = "โถ";
cancelAnimationFrame(animationId);
animationId = null;
};
playPauseButton.addEventListener("click", event => {
if (isPaused()) {
play();
} else {
pause();
}
});
๋ง์ง๋ง์ผ๋ก, ์ถ๊ฐํ ๋ฒํผ์ด ์ฌ๋ฐ๋ฅธ ์ด๊ธฐ ํ
์คํธ ์์ด์ฝ์ ํ์ํ๋๋ก ์ด์ ์ requestAnimationFrame(renderLoop)
์ ์ง์ ๋ถ๋ฌ์ ๋น ๋ฅด๊ฒ ์์ํ๋ ๋ถ๋ถ์ play
ํจ์๋ฅผ ๋์ ํธ์ถํ๋๋ก ๋ณ๊ฒฝํด ๋ณด๊ฒ ์ต๋๋ค.
// ๊ธฐ์กด์๋ `requestAnimationFrame(renderLoop)`๋ฅผ ํธ์ถํ๋ ์ค์ด์์ต๋๋ค.
play();
http://localhost:8080/ ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํ๋ฉด ์ผ์์ ์ง/์ฌ์ ๋ฒํผ์ด ์ถ๊ฐ๋ ๋ถ๋ถ์ ํ์ธํ ์ ์์ต๋๋ค!
onclick
์ด๋ฒคํธ๋ก ์ธํฌ ์ํ ์ ํํ๊ธฐ
์ด์ ๊ฒ์์ ์ผ์์ ์ง ํ ์ ์๊ฒ ๋์ผ๋ ์ธํฌ๋ค์ ํด๋ฆญํด์ ์ํ๋ฅผ ๋ฐ๊ฟ ์ ์๋๋ก ์ฝ๋๋ฅผ ์์ ํด ๋ณด๊ฒ ์ต๋๋ค.
์ธํฌ๋ฅผ ํด๋ฆญํ ๋, ํด๋ฆญํ ์ธํฌ์ ์์กด ์ฌ๋ถ๋ฅผ ์ ํํ ์ ์๋๋ก ์ฝ๋๋ฅผ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค. toggle
๋ฉ์๋๋ฅผ wasm-game-of-life/src/lib.rs
ํ์ผ ๋ด์ Cell
์ ์ถ๊ฐํด ์ค์๋ค.
#![allow(unused)] fn main() { impl Cell { fn toggle(&mut self) { *self = match *self { Cell::Dead => Cell::Alive, Cell::Alive => Cell::Dead, }; } } }
์ฃผ์ด์ง ํ๊ณผ ์ด์ ์์นํ ์ธํฌ์ ์ํ๋ฅผ ์ ํํ ์ ์๋๋ก, ํ๊ณผ ์ด์ ๋ฒกํฐ์ ์ธ๋ฑ์ค๋ก ๋ณํํ๊ณ , ๋ณํํ ์ธ๋ฑ์ค๋ฅผ ์ธํฌ ๋ฒกํฐ๋ก ๋ณํํ๊ฒ ์ต๋๋ค. ์ธํฌ ๋ฒกํฐ๊ฐ ์ค๋น๋๋ค๋ฉด ๋ณํ๋ ์ธ๋ฑ์ค์ ์์นํ๋ ์ธํฌ๋ฅผ ์ ํํ ์ ์๋๋ก ๋ฉ์๋๋ฅผ ํธ์ถํด ๋ด ์๋ค:
#![allow(unused)] fn main() { /// Public ๋ฉ์๋, ์๋ฐ์คํฌ๋ฆฝํธ๋ก ์ต์คํฌํธ ํ ์ ์๋๋ก ํจ. #[wasm_bindgen] impl Universe { // ... pub fn toggle_cell(&mut self, row: u32, column: u32) { let idx = self.get_index(row, column); self.cells[idx].toggle(); } } }
์ด impl
๋ธ๋ญ ๋ด์ ์ ์๋ ๋ฉ์๋์ #[wasm_bindgen]
์์ฑ์ ์ถ๊ฐํ์ฌ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋์์ ํธ์ถํ ์ ์๋๋ก ํ์ต๋๋ค.
wasm-game-of-life/www/index.js
ํ์ผ์์ <canvas>
์์์ ํด๋ฆญ ์ด๋ฒคํธ๋ฅผ ์์ (listening)ํด๋ณด๊ฒ ์ต๋๋ค. ์ฝ๋ฐฑ ํจ์ ๋ด๋ถ์๋ ํ์ด์ง์ ์๋ ์ขํ๋ฅผ ์บ๋ฒ์ค ์๋ ์ขํ๋ก ๋ณํํ ๋ค์ toggle_cell
๋ฉ์๋๋ฅผ ํธ์ถํ์ฌ ์ฅ๋ฉด์ ๋ค์ ๊ทธ๋ ค๋ณผ์ ์๋๋ก ์ฝ๋๋ฅผ ์์ฑํด๋ณด๊ฒ ์ต๋๋ค.
canvas.addEventListener("click", event => {
const boundingRect = canvas.getBoundingClientRect();
const scaleX = canvas.width / boundingRect.width;
const scaleY = canvas.height / boundingRect.height;
const canvasLeft = (event.clientX - boundingRect.left) * scaleX;
const canvasTop = (event.clientY - boundingRect.top) * scaleY;
const row = Math.min(Math.floor(canvasTop / (CELL_SIZE + 1)), height - 1);
const col = Math.min(Math.floor(canvasLeft / (CELL_SIZE + 1)), width - 1);
universe.toggle_cell(row, col);
drawGrid();
drawCells();
});
wasm-game-of-life
๊ฒฝ๋ก์์ wasm-pack build
๋ช
๋ น์ด๋ฅผ ์คํํ์ฌ ๋ค์ ๋น๋ํ ๋ค์, http://localhost:8080/ ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจํด ๋ณด์ธ์. ์ด์ ์ธํฌ๋ฅผ ์ง์ ํด๋ฆญํด์ ์ํ๋ฅผ ์ ํํ ์ ์๊ฒ ๋์ต๋๋ค. ํจํด์ ํ๋ฒ ๊ทธ๋ ค๋ณด์ธ์!
์ฐ์ตํด ๋ณด๊ธฐ
-
<input type="range">
์์ ฏ์ ์ถ๊ฐํ์ฌ ๋งค ํ๋ ์๋ง๋ค ๋ฐ์ํ๋ ํฑ์ ์๋ฅผ ์กฐ์ ํ ์ ์๋๋ก ์ฝ๋๋ฅผ ์์ฑํด ๋ณด์ธ์. -
์ธ์์ ๋๋คํ ์ด๊ธฐ ์ํ๋ก ๋ฆฌ์ ํ ์ ์๋๋ก ๋ฒํผ์ ํ๋ ๋ ์ถ๊ฐํด ๋ณด์ธ์. ๋ ๋ค๋ฅธ ๋ฒํผ์ ์ถ๊ฐํด์ ๋ชจ๋ ์ธํฌ๊ฐ ์ฃฝ์ ์ํ๋ก ์๋ก ์์ํ ์ ์๋๋ก ๊ตฌํํด ๋ด๋ ์ข์ต๋๋ค.
-
Ctrl์ ๋๋ฅธ ์ํ๋ก ์ธํฌ๋ฅผ ํด๋ฆญํ๋ฉด (
Ctrl + ํด๋ฆญ
) ํด๋ฆญํ ์ธํฌ๋ฅผ ์ค์ฌ์ผ๋ก glider ํจํด์ด ์ถ๊ฐ๋๋๋ก ์ฝ๋๋ฅผ ์์ฑํด ๋ณด์ธ์. Shift๋ฅผ ๋๋ฅธ ์ํ๋ก ํด๋ฆญํ ๋๋ (Shift + Click
) pulsar ํจํด์ ๊ทธ๋ฆฌ๋๋ก ์์ฑํด ๋ด๋ ์ข์ต๋๋ค.
ํ์ ํ๋กํ์ผ๋ง
์ด ์ฑํฐ์์๋ ํ์ ํ๋กํ์ผ๋ง ์์ ์ ํด๋ณด๋ฉด์ ์ด์ ์ ๊ตฌํํ Game of Life์ ์ฑ๋ฅ์ ๊ฐ์ ์์ผ๋ณด๊ฒ ์ต๋๋ค.
์์ํ๊ธฐ ์ ์, ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ ์ฝ๋์ ์ฌ์ฉํด ๋ณผ ์ ์๋ ํ์ ํ๋กํ์ผ๋ง ํด๋ค์ ์ดํด๋ณด์ ๋ ์ข์ต๋๋ค.
window.performance.now
ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ด๋น ํ๋ ์ (FPS, Frames Per Second) ํ์ด๋จธ ๋ง๋ค๊ธฐ
์ด FPS ํ์ด๋จธ๋ ๊ตฌํํ ๊ฒ์์ ๋ ๋๋ง ์๋๋ฅผ ์ด๋ป๊ฒ ๊ฐ์ ์ํฌ์ง ์ดํด๋ณผ ๋ ๋งค์ฐ ์ ์ฉํ๊ฒ ์ฌ์ฉ๋ ์์ ์ ๋๋ค.
wasm-game-of-life/www/index.js
ํ์ผ์ fps
๊ฐ์ฒด๋ฅผ ์ถ๊ฐํ๋ ๊ฒ์ผ๋ก ์์ํด ๋ด
์๋ค:
const fps = new class {
constructor() {
this.fps = document.getElementById("fps");
this.frames = [];
this.lastFrameTimeStamp = performance.now();
}
render() {
// ๋ง์ง๋ง ํ๋ ์ ๋ ๋๋ถํฐ์ ๋ธํ ์๊ฐ์ fps ๋จ์๋ก ๋ณํํฉ๋๋ค.
const now = performance.now();
const delta = now - this.lastFrameTimeStamp;
this.lastFrameTimeStamp = now;
const fps = 1 / delta * 1000;
// ๋ง์ง๋ง 100๊ฐ์ ํ์ด๋ฐ๋ง ์ ์ฅํฉ๋๋ค.
this.frames.push(fps);
if (this.frames.length > 100) {
this.frames.shift();
}
// ์ต๋, ์ต์ ํ์ด๋ฐ๊ณผ ๋ง์ง๋ง 100๊ฐ ํ์ด๋ฐ์ ํ๊ท ์ ์ฐพ์ต๋๋ค.
let min = Infinity;
let max = -Infinity;
let sum = 0;
for (let i = 0; i < this.frames.length; i++) {
sum += this.frames[i];
min = Math.min(this.frames[i], min);
max = Math.max(this.frames[i], max);
}
let mean = sum / this.frames.length;
// ํต๊ณ๋ฅผ ๋ ๋ํฉ๋๋ค.
this.fps.textContent = `
Frames per Second:
latest = ${Math.round(fps)}
avg of last 100 = ${Math.round(mean)}
min of last 100 = ${Math.round(min)}
max of last 100 = ${Math.round(max)}
`.trim();
}
};
fps
๊ฐ์ฒด์ render
ํจ์๋ฅผ renderLoop
ํจ์์ ๋งค ๋ฐ๋ณต๋ง๋ค ํธ์ถํด ๋ณด๊ฒ ์ต๋๋ค:
const renderLoop = () => {
fps.render(); //new
universe.tick();
drawGrid();
drawCells();
animationId = requestAnimationFrame(renderLoop);
};
๋ง์ง๋ง์ผ๋ก, fps
์์๋ฅผ wasm-game-of-life/www/index.html
ํ์ผ์ ์์ง ์๊ณ ์ถ๊ฐํด ์ค์๋ค. <canvas>
๋ฐ๋ก ์์ ์ถ๊ฐํด ์ฃผ์ธ์:
<div id="fps"></div>
CSS ํ๋กํผํฐ๋ฅผ ์ถ๊ฐํด์ ๊น๋ํ๊ฒ ํฌ๋งทํด ์ฃผ๊ฒ ์ต๋๋ค:
#fps {
white-space: pre;
font-family: monospace;
}
์ง์! ์ด์ http://localhost:8080 ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจ ํ๋ฉด FPS ์นด์ดํฐ๋ฅผ ํ์ธํ ์ ์๊ฒ ๋์ต๋๋ค!
Universe::tick
์ ๋งค ํฑ์ console.time
, console.timeEnd
๋ฅผ ์ฌ์ฉํ์ฌ ์ธก์ ํ๊ธฐ
web-sys
ํฌ๋ ์ดํธ์ console.time
, console.timeend
๋ฅผ ํ์ฉํ์ฌ ๊ฐ Universe::tick
์ด ํธ์ถ๋๋๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ์ ํ์ธํด ๋ณผ ์ ์์ต๋๋ค.
๋จผ์ , wasm-game-of-life/Cargo.toml
ํ์ผ์ ์ด๊ณ web-sys
๋ฅผ ์ข
์์ฑ์ผ๋ก ์ถ๊ฐํด ์ฃผ์ธ์:
[dependencies.web-sys]
version = "0.3"
features = [
"console",
]
console.time
๋ฅผ ํธ์ถํ ๋๋ง๋ค console.timeEnd
๋ ๊ฐ์ด ํธ์ถ๋ ์์ ์ด๊ธฐ ๋๋ฌธ์, RAII ํ์
์ผ๋ก ๋ฌถ์ด์ ๊ฐํธํ๊ฒ ์ฌ์ฉ ํ ์๋ ์์ต๋๋ค:
#![allow(unused)] fn main() { extern crate web_sys; use web_sys::console; pub struct Timer<'a> { name: &'a str, } impl<'a> Timer<'a> { pub fn new(name: &'a str) -> Timer<'a> { console::time_with_label(name); Timer { name } } } impl<'a> Drop for Timer<'a> { fn drop(&mut self) { console::time_end_with_label(self.name); } } }
๊ทธ๋ค์, ๋ฉ์๋ ์ต์๋จ์ ๋ค์ ์ฝ๋๋ฅผ ์ถ๊ฐํด์ ๊ฐ Universe::tick
ํธ์ถ์ด ์ผ๋ง๋ ์ค๋ ๊ฑธ๋ฆฌ๋์ง ์ธก์ ํด ๋ณผ ์ ์์ต๋๋ค:
#![allow(unused)] fn main() { let _timer = Timer::new("Universe::tick"); }
์ฝ์์ Universe::tick
๋ฅผ ํธ์ถํ๋ ๋ฐ ๊ฑธ๋ฆฌ๋ ์๊ฐ์ ๋ก๊ทธ๋ก ํ์ํฉ๋๋ค:
์ถ๊ฐ๋ก, ๋ธ๋ผ์ฐ์ ํ๋กํ์ผ๋ฌ(profiler)์ timeline ํน์ waterfall ๋ทฐ์์ console.time
๊ณผ console.timeEnd
๊ฐ ๊ฐ์ด ์คํ๋ ๋ถ๋ถ์ ํ์ธํ ์ ์์ต๋๋ค:
Game of Life ์ธ์์ ์ฌ์ด์ฆ ๋๋ ค๋ณด๊ธฐ
โ ๏ธ ์ด ์น์ ์ FireFox์ ์คํฌ๋ฆฐ์ท์ ์์๋ก ๋ณด์ฌ์ค๋๋ค. ๊ฑฐ์ ๋ชจ๋ ๋ชจ๋ ๋ธ๋ผ์ฐ์ ๊ฐ ๋น์ทํ ๊ธฐ๋ฅ๋ค์ ๊ฐ์ง๊ณ ์์ง๋ง, ๋ธ๋ผ์ฐ์ ๋ง๋ค ๊ฐ๋ฐ์ ๋๊ตฌ์ ์ฝ๊ฐ์ ์ฐจ์ด๊ฐ ์์ ์๋ ์์ต๋๋ค. ๋ฏ์ด๋ณด๊ฒ ๋ ํ๋กํ์ผ ์ ๋ณด๋ ๊ธฐ๋ณธ์ ์ผ๋ก ๊ฐ์ง๋ง, ๋ณด๊ฒ ๋ ๋ทฐ๋ ๋๊ตฌ ์ด๋ฆ ๋ฑ์ด ์ด์ง ๋ค๋ฅผ ์ ์๋ ์ ์ ๋ฏธ๋ฆฌ ํ์ธํด ์ฃผ์ธ์.
๊ตฌํํ Game of Life ์ธ์์ ๋ ํฌ๊ฒ ๋ง๋ค์ด๋ณด๋ฉด ์ด๋จ๊น์? 64 x 64 ์ฌ์ด์ฆ์ ์ธ์์ 128 x 128 ์ฌ์ด์ฆ๋ก ๋๋ ค๋ด
์๋ค. (wasm-game-of-life/src/lib.rs
ํ์ผ์์ Universe::new
๋ฅผ ์์ ํด ์ฃผ์ธ์.) ์ ์ปดํจํฐ์์๋ ์ฌ์ด์ฆ๋ฅผ ๋๋ ธ์ ๋ ๋ถ๋๋ฝ๊ฒ ์๋ํ๋ 60fps ํ๋ฉด์ด ๋ฒ๋ฒ
์ด๋ฉด์ 40fps ์ฒ๋ผ ๋ณด์ด๋ ๋ถ๋ถ์ด ํ์ธ๋ฉ๋๋ค.
ํ๋กํ์ผ์ ๊ธฐ๋กํ๊ณ waterfall ๋ทฐ๋ฅผ ํ์ธํ๋ฉด, ๊ฐ ์ ๋๋ฉ์ด์ ์ด ์ฒ๋ฆฌ๋๋๋ฐ 20 ๋ฐ๋ฆฌ์ด๋ณด๋ค ๋ ๋ง์ด ๊ฑธ๋ฆฌ๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. 60fps๋ก ํ์๋์ ๋๋ ํ๋ ์ ์ ์ฒด๋ฅผ ๋ ๋ํ๋๋ฐ 16 ๋ฐ๋ฆฌ์ด๊ฐ ๊ฑธ๋ฆฐ ๊ฒ์ ๋ ์ฌ๋ ค๋ณด๋ฉด ํ์คํ ์ฐจ์ด๊ฐ ์๋ ๊ฒ ๊ฐ์ต๋๋ค. ์ฐธ๊ณ ๋ก, ์๋ฐ์คํฌ๋ฆฝํธ์ ์น์ด์ ๋ธ๋ฆฌ ์ธ์๋ ํ์ด์ง๋ฅผ ๊ทธ๋ฆฌ๋ ๋ฑ ๋ธ๋ผ์ฐ์ ๊ฐ ์ํํ๋ ๋ค๋ฅธ ์์ ์ ์ํฅ๋ ์์ผ๋ ์ฐธ๊ณ ํด ์ฃผ์ธ์.
ํ ์ ๋๋ฉ์ด์
ํ๋ ์ ๋์ ์ด๋ค ์ผ์ด ์ผ์ด๋๋์ง ์ ํ์ธํด ๋ณด๋ฉด, CanvasRenderingContext2D.fillStyle
์ setter๊ฐ ๋ง์ ์ฑ๋ฅ์ ์๊ตฌํ๋ ๋ถ๋ถ์ ํ์ธํ ์ ์์ต๋๋ค.
โ ๏ธ FireFox ๋ธ๋ผ์ฐ์ ์์ ์ ๋ด์ฉ์์ ์ธ๊ธ๋
CanvasRenderingContext2D.fillStyle
๋์ ์ "DOM"์ด ํ์๋๋ค๋ฉด ์ฑ๋ฅ ๊ฐ๋ฐ์ ๋๊ตฌ (performance developer tools) ์์ "Gecko ํ๋ซํผ ๋ฐ์ดํฐ ํ์ํ๊ธฐ (Show Gecko Platform Data)" ์ต์ ์ ํ์ฑํํด์ค์ผ ํ ์๋ ์์ต๋๋ค:
๋ง์ ํ๋ ์์ ํธ์ถ ํธ๋ฆฌ ์ง๊ณ (call tree's aggregation) ๋ฅผ ์ดํด๋ณด๋ฉด ์ด๊ฒ ์ ํ ์ด์ํ ๋์์ด ์๋์ ํ์ธํ ์ ์์ต๋๋ค:
์ด์ด์ฟ ! ๊ฑฐ์ 40% ๋ถ๋์ ์ด setter์ ์ฌ์ฉํด๋ฒ๋ ธ๋ค์!
โก
tick
๋ฉ์๋๊ฐ ์ฑ๋ฅ ๋ณ๋ชฉ์ ์ผ์ผํค๋๋ฐ ํน๋ณํ ์ด์ ๊ฐ ์์ ๊ฒ ๊ฐ์์ง๋ง, ์ฌ์ค ๊ทธ๋ ์ง ์์ ๋ถ๋ถ์ ํ์ธํ์ต๋๋ค. ์ด๋ ๊ฒ ์์ ์ ํ๋ค ๋ณด๋ฉด ์์์น ๋ชปํ ๋ถ๋ถ์์ ์๊ฐ์ ๋ง์ด ์ฐ๊ฒ ๋ ์๋ ์์ผ๋, ํญ์ ์ ๋ง ์ค์ํ ํ๋กํ์ผ๋ง ๋๊ตฌ๋ฅผ ๋จผ์ ์ดํด๋ณด๋๋ก ํฉ์๋ค.
wasm-game-of-life/www/index.js
ํ์ผ ๋ด์ drawCells
ํจ์์์ fillStyle
ํ๋กํผํฐ๊ฐ ํ๋ฒ ์ ํด์ง๋ฉด ์ธ์ ๋ด์ ๋ชจ๋ ์ธํฌ์ ๋ชจ๋ ์ ๋๋ฉ์ด์
์ ์ด ํ๋กํผํฐ๊ฐ ์ฌ์ฉ๋๊ฒ ๋ฉ๋๋ค.
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const idx = getIndex(row, col);
ctx.fillStyle = cells[idx] === DEAD
? DEAD_COLOR
: ALIVE_COLOR;
ctx.fillRect(
col * (CELL_SIZE + 1) + 1,
row * (CELL_SIZE + 1) + 1,
CELL_SIZE,
CELL_SIZE
);
}
}
fillStyle
๊ฐ ๋ง์ ์ฑ๋ฅ์ ์๊ตฌํ๋ ๋ถ๋ถ์ ํ์ธํ๋๋ฐ, ๊ทธ๋ ๋ค๋ฉด ์ด๋ค ์์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํด์ ๊ฐ์ ์ํฌ์ ์์๊น์? ์ธํฌ์ ์์กด ์ฌ๋ถ์ ๋ฐ๋ผ fillStyle
๋ฅผ ์ฌ์ฉํ๋๋ก ๋ฐ๊ฟ๋ด
์๋ค. fillStyle = ALIVE_COLOR
๋ฅผ ์ถ๊ฐํด ์ค์ ์ด์์๋ ์ธํฌ๋ง ๊ทธ๋ฆฌ๋๋ก ํ๊ณ fillStyle = DEAD_COLOR
๋ ์ถ๊ฐํด ์ค์ ์ฃฝ์ ์ธํฌ๋ ๋์ผํ ๋ฐฉ์์ผ๋ก ์ฒ๋ฆฌ๋ฅผ ํด์ค๋ค๋ฉด ์ธํฌ๋ค์ ๋ชจ๋ ํ ๋ฒ์ ๊ทธ๋ฆฌ๋ ๋์ fillStyle
์ ๋ ๋ฒ๋ง ์ค์ ํ๊ฒ ๋ฉ๋๋ค.
// ์ด์์๋ ์ธํฌ๋ค์ ์ฒ๋ฆฌํฉ๋๋ค.
ctx.fillStyle = ALIVE_COLOR;
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const idx = getIndex(row, col);
if (cells[idx] !== Cell.Alive) {
continue;
}
ctx.fillRect(
col * (CELL_SIZE + 1) + 1,
row * (CELL_SIZE + 1) + 1,
CELL_SIZE,
CELL_SIZE
);
}
}
// ์ฃฝ์ด์๋ ์ธํฌ๋ค์ ์ฒ๋ฆฌํฉ๋๋ค.
ctx.fillStyle = DEAD_COLOR;
for (let row = 0; row < height; row++) {
for (let col = 0; col < width; col++) {
const idx = getIndex(row, col);
if (cells[idx] !== Cell.Dead) {
continue;
}
ctx.fillRect(
col * (CELL_SIZE + 1) + 1,
row * (CELL_SIZE + 1) + 1,
CELL_SIZE,
CELL_SIZE
);
}
}
์ฝ๋ ํ์ผ์ ์ ์ฅํ๊ณ http://localhost:8080/ ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจ ํด์ฃผ๋ฉด, ์น์ฌ์ดํธ๊ฐ 60fps๋ก ๋ค์ ๋ถ๋๋ฝ๊ฒ ๋ ๋ ๋ฉ๋๋ค.
ํ๋กํ์ผ์ ๋ค์ ํ์ธํด ๋ณด๋ฉด, ๊ฐ ์ ๋๋ฉ์ด์ ํ๋ ์๋ง๋ค ์ค์ง 10 ๋ฐ๋ฆฌ์ด๋ง ๊ฑธ๋ฆฌ๋ ๋ถ๋ถ๋ ํ์ธํ ์ ์์ต๋๋ค.
ํ ํ๋ ์์ ๋ค์ ๋ถ์ํด ๋ณด๋ฉด, fillStyle
์ด ๋ ์ด์ ์ฑ๋ฅ์ ๋ง์ด ์ฌ์ฉํ์ง ์๊ณ , fillRect
๊ฐ ๊ฐ ์ธํฌ ์ฌ๊ฐํ์ ๊ทธ๋ฆฌ๋๋ฐ ๋๋ถ๋ถ์ ์๊ฐ์ด ์๋น๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค.
Game of Life๊ฐ ๋ ๋น ๋ฅด๊ฒ ์งํ๋๋๋ก ๋ง๋ค์ด๋ณด๊ธฐ
์ด๋ค ์ฌ๋๋ค์ ๋นจ๋ฆฌ๋นจ๋ฆฌ ์งํํ๋ ๊ฒ์ ์ ํธํด์ ๋งค ํ๋ ์๋ง๋ค 1ํฑ์ด ์๋๋ผ 9ํฑ์ฉ ์งํ๋๋๋ก ์์ ํ๊ณ ์ถ์ ์๋ ์์ต๋๋ค. ๋๋๊ฒ๋ ์ด ์์
์ wasm-game-of-life/www/index.js
ํ์ผ์ renderLoop
ํจ์๋ฅผ ์์ ํด์ ์๊ฐ ์ธ๋ก ๊ฐ๋จํ๊ฒ ํด๋ณผ ์ ์์ต๋๋ค:
for (let i = 0; i < 9; i++) {
universe.tick();
}
์ ์ปดํจํฐ์์ ๊ตฌํํ ๊ฒ์์ด 35fps ์๋๋ก ๋ค์ ๋๋ ค์ง ๊ฒ ๊ฐ์ต๋๋ค. ์ข์ง ์์ ํ์์ด๋ ๋ค์ 60fps ๋ก ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค!
์ ํ์ธํด๋ณด๋ฉด Universe::tick
์์ ์๊ฐ์ด ๋ง์ด ์์๋๋ ๊ฒ์ผ๋ก ๋ณด์
๋๋ค. Timer
๋ฅผ ์ถ๊ฐํด์ console.time
๊ณผ console.timeEnd
ํธ์ถ์ ๊ฐ์ธ๊ณ ๋ค์ ํ๋ฒ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ์ ์์๋๋ก๋ผ๋ฉด ๋งค ํฑ๋ง๋ค ์ธํฌ์ ์ ๋ฒกํฐ๋ฅผ ํ ๋นํ๊ณ ๊ธฐ์กด ๋ฒกํฐ๋ฅผ ํด์ (freeing)ํ๋ ์์
์ ๋ง์ ์ฑ๋ฅ์ ์ฌ์ฉํ ๋ฟ ์๋๋ผ ์๊ฐ๋ ๋ง์ด ์๋นํ๊ฒ ๋ ๊ฒ ๊ฐ์ต๋๋ค.
#![allow(unused)] fn main() { pub fn tick(&mut self) { let _timer = Timer::new("Universe::tick"); let mut next = { let _timer = Timer::new("๋ค์ ์ธํฌ๋ค์ ํ ๋นํฉ๋๋ค."); self.cells.clone() }; { let _timer = Timer::new("๋ค์ ์ธ๋๋ฅผ ์ฒ๋ฆฌํฉ๋๋ค."); for row in 0..self.height { for col in 0..self.width { let idx = self.get_index(row, col); let cell = self.cells[idx]; let live_neighbors = self.live_neighbor_count(row, col); let next_cell = match (cell, live_neighbors) { // ๊ท์น 1: ์ธ๊ตฌ ๋ถ์กฑ์ผ๋ก 2๊ฐ ๋ฏธ๋ง์ ์ด์์ ๊ฐ์ง ์ธํฌ๋ ์ฃฝ๊ฒ ๋ฉ๋๋ค. (Cell::Alive, x) if x < 2 => Cell::Dead, // ๊ท์น 2: 2๊ฐ ํน์ 3๊ฐ์ ์ด์์ ๊ฐ์ง ์ธํฌ๋ ๋ค์ ์ธ๋์์ ๊ณ์ ์ด์์์ต๋๋ค. (Cell::Alive, 2) | (Cell::Alive, 3) => Cell::Alive, // ๊ท์น 3: ๊ณผ์ ์ธ๊ตฌ๋ก 3๊ฐ ์ด๊ณผ์ ์ด์์ ๊ฐ์ง ๋ชจ๋ ์ธํฌ๋ ์ฃฝ๊ฒ ๋ฉ๋๋ค. (Cell::Alive, x) if x > 3 => Cell::Dead, // ๊ท์น 4: ์ธํฌ ์ฆ์์ผ๋ก ์ ํํ 3๊ฐ์ ์ด์์ ๊ฐ์ง ์ธํฌ๋ ์ด์๋๊ฒ ๋ฉ๋๋ค. (Cell::Dead, 3) => Cell::Alive, // ๊ท์น์ด ์ ์ฉ๋์ง ์๋ ์ธํฌ๋ค์ ์ํ๋ ๊ทธ๋๋ก ์ ์ง๋๊ฒ ๋ฉ๋๋ค. (otherwise, _) => otherwise, }; next[idx] = next_cell; } } } let _timer = Timer::new("๊ธฐ์กด ์ธํฌ๋ค์ ํด์ ํฉ๋๋ค."); self.cells = next; } }
๋ธ๋ผ์ฐ์ ๊ฐ๋ฐ์ ๋๊ตฌ์์ ํ์ด๋ฐ(timing)์ ์ ํ์ธํด ๋ณด๋ฉด ์ด ์์์ด ์ฌ์ค์ ๋ช ๋ฐฑํ๊ฒ ํ๋ฆฐ ๋ถ๋ถ์ ํ์ธํ ์ ์์ต๋๋ค. ์ค์ ๋ก๋ ๋๋ถ๋ถ์ ์๊ฐ์ด ๋ค์ ์ธ๋ ์ธํฌ๋ค์ ๊ณ์ฐํ๋๋ฐ ์ฌ์ฉ๋๊ฒ ๋ฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ ์์ธ๋ก ๋งค ํฑ๋ง๋ค ๋ฒกํฐ ๊ฐ์ ํ ๋นํ๊ณ ํด์ ํ๋ ์์ ์ด ๊ทธ๋ ๊ฒ ์ฑ๋ฅ์ ๋ง์ด ์ฌ์ฉํ์ง ์์ต๋๋ค. ๋ค์ ํ๋ฒ ์ ๋ง ์ค์ํ ํ๋กํ์ผ๋ง์ ํด๋ณด๊ฒ ์ต๋๋ค!
์ฌ์ฉํ๊ฒ ๋ ํ
์คํธ ๊ธฐ๋ฅ ๊ฒ์ดํธ (test feature gate) ๊ฐ nightly
๋ฒ์ ์ ๊ฐ์ด ํฌํจ๋ผ ์๊ธฐ ๋๋ฌธ์, nightly
์ปดํ์ผ๋ฌ๊ฐ ํ์ํฉ๋๋ค. ๊ทธ๋ฌ๋ฉด ๋ฒค์น๋งํน์ ์ด ๊ธฐ๋ฅ์ ํ๋ฒ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค. cargo benchcmp์ด๋ผ๋ ํด๋ ํ์ํ๋ ์ค์นํด ์ฃผ๋๋ก ํฉ์๋ค. ์ฐธ๊ณ ๋ก cargo benchcmp๋ cargo bench
๋ก ์์ฑํ ๋ง์ดํฌ๋ก ๋ฒค์น๋งํน์ ๋น๊ตํ๋ ๋ฐ ์ฌ์ฉํ๋ ์์ ์ฌ์ด์ฆ์ ์ ํธ๋ฆฌํฐ์
๋๋ค.
์น์ด์
๋ธ๋ฆฌ์ ๋์ผํ ์์
์ ์ํํ๋ ๋ค์ดํฐ๋ธ ์ฝ๋์ธ #[bench]
๋ฅผ ์์ฑํด ๋ด
์๋ค. ์ด๋ ๊ฒ ๋ค์ดํฐ๋ธ ์ฝ๋๋ฅผ ์์ฑํ๊ฒ ๋๋ฉด ๋ ๋ง์ ๊ธฐ๋ฅ์ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค. ์ด์ ์๋กญ๊ฒ ์์ฑ๋ wasm-game-of-life/benches/bench.rs
์ ํ์ธํด ๋ณด๊ฒ ์ต๋๋ค:
#![allow(unused)] #![feature(test)] fn main() { extern crate test; extern crate wasm_game_of_life; #[bench] fn universe_ticks(b: &mut test::Bencher) { let mut universe = wasm_game_of_life::Universe::new(); b.iter(|| { universe.tick(); }); } }
#[wasm_bindgen]
์์ฑ์ ๋ชจ๋ ์ฃผ์ ์ฒ๋ฆฌ ํด์ฃผ๋๋ก ํ๊ณ , Cargo.toml
ํ์ผ์ "cdylib"
๋ ์ฃผ์ ์ฒ๋ฆฌ ํด์ฃผ๊ฒ ์ต๋๋ค. ์ด๋ ๊ฒ ์ฃผ์ ์ฒ๋ฆฌ๋ฅผ ํ์ง ์์ผ๋ฉด ๋น๋๊ฐ ์คํจํ๊ณ ๋งํฌ ์ค๋ฅ(link errors)๊ฐ ๋ฐ์ํ๊ฒ ๋ฉ๋๋ค.
์ค๋น๊ฐ ๋ค ๋๋ค๋ฉด, cargo bench | tee before.txt
๋ช
๋ น์ด๋ฅผ ์คํํด์ ์ฝ๋๋ฅผ ์ปดํ์ผํ๊ณ ๋ฒค์น๋งํฌ๋ฅผ ์คํํด ๋ณด๊ฒ ์ต๋๋ค. | tee before.txt
๋ฅผ ํฌํจํ๋ฉด์ cargo bench
๋ช
๋ น์ด์ ์ถ๋ ฅ๊ฐ์ ๊ฐ์ ธ์์ before.txt
ํ์ผ์ ์ ์ฅํ ์์ ์ด๋ ์ด ๋ถ๋ถ๋ ๋ค์ ํ์ธํด ์ฃผ์ธ์.
$ cargo bench | tee before.txt
Finished release [optimized + debuginfo] target(s) in 0.0 secs
Running target/release/deps/wasm_game_of_life-91574dfbe2b5a124
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Running target/release/deps/bench-8474091a05cfa2d9
running 1 test
test universe_ticks ... bench: 664,421 ns/iter (+/- 51,926)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out
์ฌ๊ธฐ์ ๋ฐ์ด๋๋ฆฌ ํ์ผ์ ๊ฒฝ๋ก๋ ๊ฐ์ด ํ์๋๋ ๋ถ๋ถ์ ํ์ธํ ์ ์์ต๋๋ค. ์ด๋ฒ์๋ ์ด์์ฒด์ ์ ํ๋กํ์ผ๋ฌ๋ฅผ ์ฌ์ฉํ์ฌ ๋ฒค์น๋งํน์ ๋ค์ ํ๋ฒ ํด๋ณด๊ฒ ์ต๋๋ค. ์ ๋ ๋ฆฌ๋
์ค(Linux)๋ฅผ ์ฌ์ฉํ๊ณ ์์ผ๋ perf
๋ฅผ ์์ ๋ก ์ฌ์ฉํ๊ฒ ์ต๋๋ค:
$ perf record -g target/release/deps/bench-8474091a05cfa2d9 --bench
running 1 test
test universe_ticks ... bench: 635,061 ns/iter (+/- 38,764)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out
[ perf record: Woken up 1 times to write data ]
[ perf record: Captured and wrote 0.178 MB perf.data (2349 samples) ]
perf report
๋ช
๋ น์ด๋ก ํ๋กํ์ผ์ ๋ก๋ํ๋ฉด, ์์ํ ๋๋ก ๋๋ถ๋ถ์ ์๊ฐ์ด Universe::tick
๋ฅผ ์คํํ๋๋ฐ ์๋น๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค:
perf
๋ฅผ ์คํํ ๋ค์ a
ํค๋ฅผ ๋๋ฅด๋ฉด ์ด๋ค ์ด์
๋ธ๋ฆฌ ๋ช
๋ น์ด(instruction)์ด ํจ์๋ฅผ ์ฒ๋ฆฌํ ๋ ์ฌ์ฉ๋๊ณ ์๋์ง ํ์ธํ ์ ์์ต๋๋ค:
์ ๋ด์ฉ์ ํ์ธํ๋ฉด 26.67%์ ์๊ฐ์ด ์ด์ ์ธํฌ๋ค์ ๋ง๋ค์ด๋ด๋ ๋ฐ ์ฌ์ฉ๋๊ณ , 23.41%๋ฅผ ์ด์๋ค์ ์ด ์ธ๋ฑ์ค, ๊ทธ๋ฆฌ๊ณ ๋๋จธ์ง 15.42%๋ฅผ ํ ์ธ๋ฑ์ค๋ฅผ ๋ํ๋ ๋ฐ ์ฌ์ฉ๋๋ ๊ฒ์ ์ ์ ์์ต๋๋ค. ์ ์ผ ๋ง์ ์ฑ๋ฅ์ ์ฌ์ฉํ๋ ์ธ ๋ช
๋ น์ด๋ค ์ค, ๋ ๋ฒ์งธ์ ์ธ ๋ฒ์งธ๋ก ๋น์ฉ์ด ๋ง์ด ๋๋ ๋ช
๋ น์ด๊ฐ div
๋ช
๋ น์ด์ธ ๋ถ๋ถ๋ ํ์ธํ ์ ์์ต๋๋ค. ์ด div
๊ตฌํ๋ค์ด Universe::live_neighbor_count
ํจ์์์ ๋๋จธ์ง ์ฐ์ฐ์๋ก ์ธ๋ฑ์ฑ ํ๋๋ก ๊ตฌํํ๋ ๋ถ๋ถ์
๋๋ค. (modulo indexing logic)
wasm-game-of-life/src/lib.rs
ํ์ผ์์ live_neighbor_count
๋ฅผ ์ ์ํ๋ ๋ด์ฉ์ ๋ค์ ๋ ์ฌ๋ ค๋ด
์๋ค:
#![allow(unused)] fn main() { fn live_neighbor_count(&self, row: u32, column: u32) -> u8 { let mut count = 0; for delta_row in [self.height - 1, 0, 1].iter().cloned() { for delta_col in [self.width - 1, 0, 1].iter().cloned() { if delta_row == 0 && delta_col == 0 { continue; } let neighbor_row = (row + delta_row) % self.height; let neighbor_col = (column + delta_col) % self.width; let idx = self.get_index(neighbor_row, neighbor_col); count += self.cells[idx] as u8; } } count } }
์ฝ๋๋ฅผ ๋ค์ ํ์ธํด ๋ณด๋ฉด if
๋ธ๋ญ๋ค๋ก ์ฒซ์งธ์ ๋ง์ง๋ง ํ๊ณผ ์ด์ ์ฃ์ง ์ผ์ด์ค(edge case) ๋ฅผ ์ฒ๋ฆฌํ๋๋ฐ ์ฝ๋๋ฅผ ๋ถํ์ํ๊ฒ ๊ธธ๊ฒ ์์ฑํ๋ ๋์ ๋๋จธ์ง ์ฐ์ฐ์ (modulo operator) ๋ฅผ ์ฌ์ฉํ๊ณ ์๋ ๋ถ๋ถ์ ํ์ธํ ์ ์์ต๋๋ค. row
๊ณผ column
๋ ๋ค ์ธ์์ ๊ฐ์ฅ์๋ฆฌ์ ์์ง ์๊ณ ๋๋จธ์ง ์ฐ์ฐ์๋ฅผ ์ธ ํ์๊ฐ ์๋ ์ผ๋ฐ์ ์ธ ๊ฒฝ์ฐ๋ฅผ ์ฒ๋ฆฌํ๋๋ฐ๋ div
๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํด์ ๋ง์ ์ฑ๋ฅ์ ์ฌ์ฉํ๋ ๋ถ๋ถ๋ ํ์ธ๋ฉ๋๋ค. ๋ฐ๋ณต๋ฌธ์ ์ฌ์ฉํ๋ ๋์ ์ if
๋ฌธ์ผ๋ก ์ด๋ฌํ ์ฃ์ง ์ผ์ด์ค๋ค์ ์ฒ๋ฆฌํ๊ณ , ์ฌ๋ฌ ๋ถ๊ธฐ๋ค์ CPU์ ๋ถ๊ธฐ ์์ธก๊ธฐ(branch predictor)๊ฐ ์์ธกํ๊ธฐ ์ฝ๋๋ก ๋ง๋ค์ด๋ณด๊ฒ ์ต๋๋ค.
live_neighbor_count
๋ฅผ ๋ค์๊ณผ ๊ฐ์ด ๋ค์ ์์ฑํด ๋ด
์๋ค:
#![allow(unused)] fn main() { fn live_neighbor_count(&self, row: u32, column: u32) -> u8 { let mut count = 0; let north = if row == 0 { self.height - 1 } else { row - 1 }; let south = if row == self.height - 1 { 0 } else { row + 1 }; let west = if column == 0 { self.width - 1 } else { column - 1 }; let east = if column == self.width - 1 { 0 } else { column + 1 }; let nw = self.get_index(north, west); count += self.cells[nw] as u8; let n = self.get_index(north, column); count += self.cells[n] as u8; let ne = self.get_index(north, east); count += self.cells[ne] as u8; let w = self.get_index(row, west); count += self.cells[w] as u8; let e = self.get_index(row, east); count += self.cells[e] as u8; let sw = self.get_index(south, west); count += self.cells[sw] as u8; let s = self.get_index(south, column); count += self.cells[s] as u8; let se = self.get_index(south, east); count += self.cells[se] as u8; count } }
๋ฒค์น๋งํน์ ๋ค์ ํด๋ณด๊ฒ ์ต๋๋ค! ์ด๋ฒ์๋ ์ถ๋ ฅ๋๋ ๋ฉ์ธ์ง๋ค์ after.txt
๋ก ์ถ๋ ฅํด ๋ด
์๋ค.
$ cargo bench | tee after.txt
Compiling wasm_game_of_life v0.1.0 (file:///home/fitzgen/wasm_game_of_life)
Finished release [optimized + debuginfo] target(s) in 0.82 secs
Running target/release/deps/wasm_game_of_life-91574dfbe2b5a124
running 0 tests
test result: ok. 0 passed; 0 failed; 0 ignored; 0 measured; 0 filtered out
Running target/release/deps/bench-8474091a05cfa2d9
running 1 test
test universe_ticks ... bench: 87,258 ns/iter (+/- 14,632)
test result: ok. 0 passed; 0 failed; 0 ignored; 1 measured; 0 filtered out
ํจ์ฌ ๋์ ๊ฒ ๊ฐ์ต๋๋ค! benchcmp
ํด๊ณผ ๋ฐฉ๊ธ ์์ฑํ ๋ ํ
์คํธ ํ์ผ์ ํ์ธํด ๋ณด๋ฉด ์ผ๋ง๋ ๋์ด์ก๋์ง ๋ณผ ์ ์์ต๋๋ค:
$ cargo benchcmp before.txt after.txt
name before.txt ns/iter after.txt ns/iter diff ns/iter diff % speedup
universe_ticks 664,421 87,258 -577,163 -86.87% x 7.61
์ฐ์! 7.61 ๋ฐฐ๋ ๋นจ๋ผ์ก๋ค์!
์น์ด์ ๋ธ๋ฆฌ๋ ์๋์ ์ผ๋ก ์ผ๋ฐ์ ์ธ ํ๋์จ์ด ์ํคํ ์ฒ์ ๋ฐ์ ํ๊ฒ ๋งคํ(mapping)๋๋๋ก ์ค๊ณ๋ผ ์์ง๋ง, ์ด๋ฌํ ๋ค์ดํฐ๋ธ ์ฝ๋์ ์๋ ํฅ์์ด ์น์ด์ ๋ธ๋ฆฌ ์ฑ๋ฅ ํฅ์์ผ๋ก๋ ์ด์ด์ง ์ ์๋์ง ํ์คํ ํ์ธํด ๋ณด๋ ๊ฒ๋ ์ค์ํฉ๋๋ค.
wasm-pack build
๋ช
๋ น์ด๋ฅผ ์คํํด์ .wasm
ํ์ผ์ ๋ค์ ๋น๋ํ๊ณ http://localhost:8080/ ํ์ด์ง๋ฅผ ์๋ก๊ณ ์นจ ํด์ฃผ์ธ์. ์ ์ปดํจํฐ์์๋ 60 fps์ ์๋๋ก ๋ค์ ์๋ํ๋ ๊ฒ์ด ํ์ธ๋ฉ๋๋ค. ๋ธ๋ผ์ฐ์ ์ ํ๋กํ์ผ๋ฌ๋ฅผ ์ฌ์ฉํด์ ๋ค์ ํ์ธํด ๋ณด๋ ๊ฐ ์ ๋๋ฉ์ด์
ํ๋ ์์ด 10 ๋ฐ๋ฆฌ์ด์ฉ ๊ฑธ๋ฆฌ๋ ๊ฒ์ผ๋ก ํ์ธ๋ฉ๋๋ค.
์ฑ๊ณต์ ์ผ๋ก ์ ๋ง๋ฌด๋ฆฌํ ๊ฒ ๊ฐ์ต๋๋ค!
์ฐ์ตํด ๋ณด๊ธฐ
-
ํ์ฌ๋ก๋ ํ ๋น๊ณผ ํด์ ๋ฅผ ์ฒ๋ฆฌํ๋ ์ฝ๋๋ฅผ ์ง์ฐ๋ ๊ฒ
Universe::tick
์๋๋ฅผ ํฅ์์ํค๋ ๊ฐ์ฅ ์ฌ์ด ๋ฐฉ๋ฒ์ ๋๋ค.Universe
๊ฐ ๋ ๋ฒกํฐ๋ง ๊ด๋ฆฌํ๋๋ก ํ๊ณ , ๋ ๋ฒกํฐ๋ฅผ ์ฝ๋๊ฐ ์คํ๋๋ ๋ด๋ด ํด์ ๋๊ฑฐ๋tick
ํจ์์์ ์ ๋ฒํผ๋ฅผ ํ ๋นํ์ง ์๋๋ก ์ธํฌ๋ค์ ์ด์ค ๋ฒํผ๋ง (double buffering) ๊ธฐ๋ฒ์ผ๋ก ๊ตฌํํด ๋ณด์ธ์. -
"Game of Life ๊ตฌํํ๊ธฐ" ์น์ ์์ ๋ธํ ๊ธฐ๋ฐ์ผ๋ก ์ค๊ณํ ๋ด์ฉ์ ํ ๋๋ก, ๋ฌ์คํธ ์ฝ๋๊ฐ ์ํ๊ฐ ๋ฐ๋ ์ธํฌ๋ค์ ๋ชฉ๋ก์ ๋ฐํํ๋ ๋ฐฉ์์ผ๋ก ๋ค์ ์ฝ๋๋ฅผ ์ค๊ณํด ๋ณด์ธ์.
<canvas>
๊ฐ ๋ ๋นจ๋ฆฌ ๋ ๋๋๋ ๊ฒ ๋ณด์ด์๋์? ๋งค ํฑ๋ง๋ค ์ ๋ธํ ๋ชฉ๋ก์ ํ ๋นํ์ง ์์ผ๋ฉด์ ๊ตฌํํ์ค์ ์์๊ฒ ๊ฐ์ผ์ ๊ฐ์? -
ํ๋กํ์ผ๋ง ํด๋ก ํ์ธํ ์ ์๋ฏ, 2D
<canvas>
๋ ๋๋ง์ด ํน๋ณํ ๋น ๋ฅด์ง๋ ์์ ๊ฒ ๊ฐ์ต๋๋ค. 2D ์บ๋ฒ์ค ๋ ๋๋ฌ ๋์ WebGL ๋ ๋๋ฌ๋ฅผ ๋์ ์ฌ์ฉํด์ ์ฝ๋๋ฅผ ๋ค์ ๊ตฌํํด ๋ณด์ธ์. WebGL๋ก ๊ตฌํํ ๋ฒ์ ์ ์ผ๋ง๋ ๋ ๋น ๋ฅธ๊ฐ์? ๋ต๋ตํ๊ฒ ๋๋ ธ๋ WebGL๊ณผ ๋น๊ตํ๋ฉด ์ผ๋ง๋ ํฐ ์ฌ์ด์ฆ์ ์ธ์์ ๊ฐ๋ณ๊ฒ ์ฒ๋ฆฌํ ์ ์๊ฒ ๋๋์?
.wasm
ํ์ผ ์ฌ์ด์ฆ ์ค์ด๊ธฐ
๊ตฌํํ๋ Game of Life ํ๋ก๊ทธ๋จ๊ณผ ๊ฐ์ด .wasm
ํ์์ผ๋ก ์ปดํ์ผ๋ ๋ฐ์ด๋๋ฆฌ๋ฅผ ์ ์ ๋ค์๊ฒ ๋คํธ์ํฌ๋ก ์ ์กํ ๋ ์ฝ๋ ์ฌ์ด์ฆ๋ฅผ ์ ๊ฒฝ ์ฐ๋ ํธ์ด ์ข์ต๋๋ค. ๋๋ถ๋ถ์ ๊ฒฝ์ฐ์๋ .wasm
ํ์ผ์ ์ฌ์ด์ฆ๊ฐ ์์์๋ก ํ์ด์ง๊ฐ ๋นจ๋ฆฌ ๋ก๋๋๊ณ ์ ์ ๊ฒฝํ์ด ๋ ๋์์ง๋๋ค.
๋น๋ ์ค์ ์ผ๋ก .wasm
๋ฐ์ด๋๋ฆฌ ์ฌ์ด์ฆ๋ฅผ ์ผ๋ง๋ ์ค์ผ ์ ์๋์?
(debug ์ฌ๋ณผ์ด ๋น ์ ธ์๋) ๊ธฐ๋ณธ ๋น๋ ์ค์ ์ผ๋ก Game of Life๋ฅผ ๋น๋ํ๋ฉด 29,410 bytes ์ฌ์ด์ฆ์ ๋ฐ์ด๋๋ฆฌ๊ฐ ์ถ๋ ฅ๋๊ฒ ๋ฉ๋๋ค:
$ wc -c pkg/wasm_game_of_life_bg.wasm
29410 pkg/wasm_game_of_life_bg.wasm
LTO๋ฅผ ํ์ฑํํ ๋ค์ opt-level = "z"
๋ฅผ ์ค์ ํ๊ณ wasm-opt -Oz
๋ช
๋ น์ด๋ฅผ ์คํํด์ ๋น๋ํ๋ฉด ์ถ๋ ฅ๋๋ .wasm
๋ฐ์ด๋๋ฆฌ์ ์ฌ์ด์ฆ๊ฐ 17,317 bytes ์ ๋๋ก ์ค์ด๋๋ ๋ถ๋ถ์ ํ์ธํ ์ ์์ต๋๋ค.
$ wc -c pkg/wasm_game_of_life_bg.wasm
17317 pkg/wasm_game_of_life_bg.wasm
๊ทธ๋ค์์๋ ์ถ๋ ฅ๋ ๋ฐ์ด๋๋ฆฌ๋ฅผ (๊ฑฐ์ ๋ชจ๋ HTTP ์๋ฒ๊ฐ ํ๋ ๋๋ก) gzip
๋ฅผ ์ฌ์ฉํ์ฌ ์์ถํด ๋ณด๋๋ฐ, ์ ๋ง ๊ท์ฝ๊ฒ๋ ๋ฐ์ด๋๋ฆฌ ํ์ผ์ ์ฌ์ด์ฆ๋ฅผ 9,045 bytes๊น์ง ์ค์ผ ์ ์๊ฒ ๋ฉ๋๋ค!
$ gzip -9 < pkg/wasm_game_of_life_bg.wasm | wc -c
9045
์ฐ์ตํด ๋ณด๊ธฐ
-
wasm-snip
ํด์ ์ฌ์ฉํด์ ๊ตฌํํ๋ Game of Life์.wasm
๋ฐ์ด๋๋ฆฌ์์ ํจ๋ ๋๋ฒ๊น ํจ์๋ฅผ ์ ์ธ์์ผ๋ณด์ธ์. ํ์ผ์ ์ฌ์ด์ฆ๊ฐ ์ผ๋ง๋ ์ค์ด๋๋์? -
wee_alloc
๋ฅผ ์ ์ญ ํ ๋น์ (global allocator)๋ฅผ ์ฌ์ฉํด ๋ณด๊ณ ํ์ผ์ ์ฌ์ด์ฆ๋ฅผ ์ด์ ๊ณผ ๋น๊ตํด ๋ณด์ธ์. ํ๋ก์ ํธ๋ฅผ ์์ํ ๋ ํด๋ก ํ๋rustwasm/wasm-pack-template
ํ ํ๋ฆฟ์ "wee_alloc" ์ด๋ผ๋ cargo ๊ธฐ๋ฅ์ ํฌํจํ๊ณ ์๋๋ฐ, ์ด ๊ธฐ๋ฅ์wasm-game-of-life/Cargo.toml
ํ์ผ์ ์๋[features]
์น์ ์default
ํ๋์ ์ถ๊ฐํด์ ํ์ฑํํ ์ ์์ต๋๋ค.[features] default = ["wee_alloc"]
wee_alloc
์ ํ์ฑํํ๋ฉด.wasm
๋ฐ์ด๋๋ฆฌ ์ฌ์ด์ฆ๊ฐ ์ผ๋ง๋ ์ค์ด๋๋์? -
ํํ ๋ฆฌ์ผ์ ์งํํ๋ ๋ด๋ด ํ
Universe
๋ง ํ์ด์ง์ ํฌํจ์์ผฐ๋๋ฐ, ์์ฑ์๋ฅผ ์ฌ์ฉํ๋ ๋์ ๋จ ํ ๊ฐ์static mut
์ ์ญ ์ธ์คํด์ค (global instance) ๋ง ์์ ํ๋ ์ฝ๋๋ฅผ ์ต์คํฌํธ ํด๋ณผ ์๋ ์์ต๋๋ค. ์ด์ ์ฑํฐ์์ ๋ค๋ฃฌ ์ด์ค ๋ฒํผ๋ง ๊ธฐ๋ฒ (double buffering technique) ์ ์ฌ์ฉํ๊ณ ์ถ๋ค๋ฉด, ์ด๋ฌํ ๋ฒํผ๋static mut
ํค์๋๋ฅผ ์ฌ์ฉํ์ฌ ์ ์ญ์ ์ผ๋ก ๋ง๋ค์ด๋ณผ ์ ์์ต๋๋ค. ์ด๋ฐ ๋ฐฉ์์ผ๋ก, ๊ตฌํํ ๊ฒ์์์ ๋ชจ๋ ๋์ ํ ๋น (dynamic allocation) ์ ์์จ ์ ์๊ณ ,#![no_std]
์์ฑ์ ์ถ๊ฐํ์ฌ ํ ๋น๊ธฐ(allocator)๊ฐ ์๋ ํฌ๋ ์ดํธ๋ฅผ ๋ง๋ค์ด๋ณผ ์๋ ์์ต๋๋ค. ๋์ ํ ๋น์ ํ์ํ ์ข ์์ฑ์ ๋ค ์์ ๋ฉด.wasm
ํ์ผ์ ์ฌ์ด์ฆ๊ฐ ์ผ๋ง๋ ์ค์ด๋๋์?
npm์ ๋ฐฐํฌํ๊ธฐ
์ฌ์ด์ฆ๊ฐ ์๊ณ ๋น ๋ฅด๊ฒ ์๋ํ๋ wasm-game-of-life
ํจํค์ง๊ฐ ์ค๋น๋์ต๋๋ค. ์ด์ ๋ค๋ฅธ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ๋ฐ์๋ค์ด Game of Life ์ฝ๋๋ฅผ ๋ฐ๋ก ์ข
์์ฑ์ผ๋ก ๋ฐ์ ์ฌ์ฉํด ๋ณผ ์ ์๋๋ก npm์ ๋ฐฐํฌํด ๋ณด๊ฒ ์ต๋๋ค.
์ค๋น ์ฌํญ
์ฐ์ , ๊ธฐ์กด์ ์์ฑ๋ npm ๊ณ์ ์ด ์๋์ง ํ์ธํด์ฃผ์ธ์.
๊ทธ ๋ค์, ๋ค์ ๋ช ๋ น์ด๋ฅผ ์คํํ์ฌ ๋ก์ปฌ ๋จธ์ ์์ ๊ณ์ ์ ๋ก๊ทธ์ธ๋์ด ์๋์ง ํ์ธํด ์ฃผ์ธ์:
wasm-pack login
๋ฐฐํฌ
wasm-game-of-life
๊ฒฝ๋ก์์ wasm-pack
๋ช
๋ น์ด๋ฅผ ์คํํ์ฌ wasm-game-of-life/pkg
๊ฒฝ๋ก์ ์์ฑํ ์ฝ๋๊ฐ ์ ๋น๋๋ผ ์๋์ง ํ์ธํด ์ฃผ์ธ์:
wasm-pack build
wasm-game-of-life/pkg
ํด๋์ ๋ด์ฉ๋ฌผ๋ค์ ํ๋ฒ ์ดํด๋ณด๊ฒ ์ต๋๋ค. ์ด ํด๋์ ์๋ ํ์ผ๋ค์ ์ด์ npm์ ๋ฐฐํฌํด ๋ณผ ์์ ์
๋๋ค!
์ค๋น๊ฐ ๋๋ค๋ฉด, wasm-pack publish
๋ฅผ ์คํํด์ ํจํค์ง๋ฅผ npm์ ์
๋ก๋ํด๋ณด์ธ์:
wasm-pack publish
์ ๋ง ๋๋๊ฒ๋ ์ด๊ฒ ๋ต๋๋ค! ์ด๋ ๊ฒ npm์ ํจํค์ง๋ฅผ ์ ๋ก๋ํ ์ ์๋๋ฐ...
... ์ด ํํ ๋ฆฌ์ผ์ ๋๋ธ ๋ค๋ฅธ ์ฌ๋๋ค๋ npm์ ๋ฐฐํฌ๋ฅผ ํ์ ๊ฐ๋ฅ์ฑ์ด ๋์ต๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ wasm-game-of-life
๋ผ๋ ํจํค์ง ์ด๋ฆ์ด ๋์ ํ๋ฅ ๋ก ์ด๋ฏธ ์ฌ์ฉ ์ค์ผ ์๋ ์์ต๋๋ค. ๋ง์ง๋ง์ผ๋ก ์คํํ ๋ช
๋ น์ด๊ฐ ์ด๋ฌํ ์ด์ ๋ก ์ฑ๊ณต์ ์ผ๋ก ์คํ๋์ง ๋ชปํ ๋ถ๋ถ์ด ํ์ธ๋๋์?
wasm-game-of-life/Cargo.toml
ํ์ผ์ ์ด๊ณ ์์ฑํ ํจํค์ง๊ฐ ๊ณ ์ ํ ์ด๋ฆ์ ๊ฐ์ง ์ ์๋๋ก name
ํ๋์ ์ํ๋ ์ ์ ์ด๋ฆ์ ์ถ๊ฐํด ์ฃผ์ธ์.
[package]
name = "wasm-game-of-life-my-username"
์ด์ ๋ค์ ๋น๋ํ๊ณ ๋ฐฐํฌํด ๋ณด๊ฒ ์ต๋๋ค:
wasm-pack build
wasm-pack publish
์ด๋ฒ์๋ ์ ๋ฐฐํฌ๋์ ๊ฒ๋๋ค!
์ฐธ์กฐ
์ด ์น์ ์ ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ ๊ฐ๋ฐ์ ๋์์ด ๋๋ ์ฐธ์กฐ ์๋ฃ๋ฅผ ํฌํจํ๊ณ ์์ต๋๋ค. ์์๊ฐ ์ ํด์ ธ ์์ง ์์ ์ฒ์๋ถํฐ ๋๊น์ง ์ฝ์ ํ์๊ฐ ์์ต๋๋ค. ๊ฐ ํ์ ์น์ ์ ๊ฐ๋ณ์ ์ผ๋ก ๊ฐ์ ๋ค๋ฅธ ๋ด์ฉ์ ๋ค๋ฃน๋๋ค.
์๋ฉด ์ข์ ํฌ๋ ์ดํธ๋ค
๋ฌ์คํธ์ WebAssembly๋ก ๊ฐ๋ฐํ ๋ ์๋ฉด ์ข์ ํฌ๋ ์ดํธ๋ค์ ๋ชจ์๋์ ๋ชฉ๋ก์ ๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ์ DOM ์กฐ์ํ๊ธฐ
wasm-bindgen
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
wasm-bindgen
์ ๋ฌ์คํธ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฌ์ด์ ๊ณ ๋ ๋ฒจ ์ํธ์์ฉ์ ๋์์ฃผ๋ ํฌ๋ ์ดํธ์
๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ์ ๋ฌ์คํธ๋ฅผ ๋๋๋ค๋ฉด์ ์ํฌํธ๋ฅผ ํ ์ ์๋๋ก ๋์์ค๋๋ค.
wasm-bindgen-futures
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
wasm-bindgen-futures
๋ ์๋ฐ์คํฌ๋ฆฝํธ์ Promise
์ ๋ฌ์คํธ์ Future
์ ์ฐ๊ฒฐํด ์ฃผ๋ ๋ค๋ฆฌ ์ญํ ์ ํ๋ ํฌ๋ ์ดํธ์
๋๋ค. ๋ฌ์คํธ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฌ์ด์์ ์๋ฐฉํฅ์ผ๋ก ๋ณํ์ด ๊ฐ๋ฅํ๊ณ ๋ฌ์คํธ์์ ๋น๋๊ธฐ(asynchronous) ์์
์ ์ํํ ๋ ์ ์ฉํฉ๋๋ค. DOM ์ด๋ฒคํธ ๋ฐ I/O ์์
๊ณผ ์ํธ์์ฉํ ์ ์๋๋ก ํด์ค๋๋ค.
js-sys
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
wasm-bindgen
์ผ๋ก ์์ฑ๋ js-sys
ํฌ๋ ์ดํธ๋ Object
, Function
, eval
๋ฑ์ ์๋ฐ์คํฌ๋ฆฝํธ ์ ์ญ ํ์
๊ณผ ๋ฉ์๋๋ฅผ ๋ชจ๋ ์ํฌํธํฉ๋๋ค. ์ด๋ฌํ API๋ค์ ์น ๋ฟ๋ง ์๋๋ผ Node.js๋ฅผ ํฌํจํ ๋ค๋ฅธ ๋ชจ๋ ECMAScript ํ๊ฒฝ์ ์ด์ํด์ ์ฌ์ฉํ ์ ์์ต๋๋ค.
web-sys
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
DOM ์กฐ์, setTimeout
, Web GL, Web Audio ๋ฑ๊ณผ ๊ฐ์ ๋ค๋ฅธ ๋ชจ๋ ์น API๋ค์ wasm-bindgen
์ผ๋ก ์์ฑํ ํฌ๋ ์ดํธ์
๋๋ค.
์ค๋ฅ ๋ณด๊ณ ๋ฐ ๋ก๊น
console_error_panic_hook
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
console.error
๋ก ๋ฉ์ธ์ง๋ฅผ ํจ๋ ๋ฉ์ธ์ง๋ฅผ ๋๊ฒจ์ฃผ๋ ํจ๋ ํ
(panic hook) ๊ณผ ํจ๊ป, wasm32-unknown-unknown
ํ๊ฒ์ผ๋ก ํจ๋๋ค์ ๋๋ฒ๊น
ํ ์ ์๋๋ก ๋์์ฃผ๋ ํฌ๋ ์ดํธ์
๋๋ค.
console_log
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
log
ํฌ๋ ์ดํธ์ ๋ฐฑ์๋๋ฅผ ์ ๊ณตํด ์ฃผ๋ ํฌ๋ ์ดํธ์
๋๋ค. ๋ก๊ทธ ๋ ๋ฉ์ธ์ง๋ค์ ๊ฐ๋ฐ์ ๋๊ตฌ (devtools) ์ฝ์๋ก ๋๊ฒจ์ค๋๋ค.
๋์ ํ ๋น
wee_alloc
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
์ด ํฌ๋ ์ดํธ์ ์ด๋ฆ์ Wasm-Enabled, Elfin Allocator ์์ ์ ๋๋๋๋ฐ, (1000 bytes ์ดํ ์ฌ์ด์ฆ์ ์์ถ๋์ง ์์ .wasm
๋ฐ์ด๋๋ฆฌ๋ก ๊ตฌ์ฑ๋) ์ฝ๋ ์ฌ์ด์ฆ๊ฐ ํ ๋น ์ฑ๋ฅ๋ณด๋ค ๋ ์ค์ํ ๋ ์ ์ฉํ๊ฒ ์ฌ์ฉํ ์ ์๋๋ก ํ ๋น์๋ฅผ ๊ตฌํํ ์ฝ๋์
๋๋ค.
.wasm
๋ฐ์ด๋๋ฆฌ๋ฅผ ํ์ฑ(parsing)ํ๊ณ ์์ฑํ๊ธฐ
parity-wasm
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
์ง๋ ฌํ(serializing)๊ณผ ์ญ์ง๋ ฌํ(deserializing), ๊ทธ๋ฆฌ๊ณ .wasm
๋ฐ์ด๋๋ฆฌ๋ฅผ ๋น๋ํ๋๋ฐ ์ฌ์ฉํ๋ ์ ๋ ๋ฒจ ์น์ด์
๋ธ๋ฆฌ ํฌ๋งท์
๋๋ค. "names" ๋ฐ "reloc.WHATEVER"๊ณผ ๊ฐ์ด ์ ์๋ ค์ง ์น์
๋ค์ด ์ ์ง์๋ผ ์์ต๋๋ค.
wasmparser
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
์น์ด์ ๋ธ๋ฆฌ ๋ฐ์ด๋๋ฆฌ ํ์ผ์ ์ฝ๋ ๋ฐ ์ฌ์ฉํ๋ ๊ฐ๋จํ ์ด๋ฒคํธ ๊ธฐ๋ฐ (event-driven) ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋๋ค. ๊ฐ๊ฐ ํ์ฑํ ๋ด์ฉ์ ๋ฐ์ดํธ ์คํ์ (byte offset) ์ ์ ๊ณตํ๋๋ฐ, ์๋ฅผ ๋ค์ด reloc์ ์ฝ๋ ์์ ๋ฑ์ ํ์ํฉ๋๋ค.
์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ปดํ์ผํ๊ณ ์ธํฐํ๋ฆฌํ (Interpreting)ํ๊ธฐ
wasmi
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
Parity ๋ผ๋ ํ์ฌ์์ ๋ง๋ ์๋ฒ ๋ฉ ํ ์ ์๋ ์น์ด์ ๋ธ๋ฆฌ ์ธํฐํ๋ฆฌํฐ์ ๋๋ค.
cranelift-wasm
| crates.io | ๋ ํฌ์งํ ๋ฆฌ
์น์ด์ ๋ธ๋ฆฌ ์ฝ๋๋ฅผ ๋ค์ดํฐ๋ธ ํธ์คํธ์ ๊ธฐ๊ณ์ด(machine code)๋ก ์ปดํ์ผํด ์ฃผ๋ ํฌ๋ ์ดํธ์ธ Cranelift (nรฉ Cretonne) ์ฝ๋ ์์ฑ๊ธฐ ํ๋ก์ ํธ์ ์ผ๋ถ์ ๋๋ค.
์๋ฉด ์ข์ ํด๋ค
๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ๋ก ๊ฐ๋ฐํ ๋ ์๋ฉด ์ข์ ํด๋ค์ ๋ชจ์๋์ ๋ชฉ๋ก์ ๋๋ค.
๊ฐ๋ฐ, ๋น๋ ๋ฐ ์ํฌํ๋ก์ฐ ๊ด๋ฆฌ (Orchestration)
wasm-pack
| ๋ ํฌ์งํ ๋ฆฌ
wasm-pack
์ Web๊ณผ Node.js ํ๊ฒฝ์์ ์๋ฐ์คํฌ๋ฆฝํธ์ ์ํธ ์ด์ฉํ๋ฉด์ ๋ฌ์คํธ๋ก ์น์ด์
๋ธ๋ฆฌ ์์
์ ํ ๋ ์ฌ์ฉํ ์ ์๋ ๋ง๋ฅ ํด์
๋๋ค. wasm-pack
์ ์ฌ์ฉํด์ ๋ ์ฝ๊ฒ ์น์ด์
๋ธ๋ฆฌ๋ฅผ ๋น๋ํ๊ณ npm ๋ ์ง์คํธ๋ฆฌ์ ๋ฐฐํฌํ๊ณ , ๊ธฐ์กด์ ์ด๋ฏธ ์ฌ์ฉํ๋ ์ํฌํ๋ก์ฐ๊ฐ ์ฌ์ฉํ๋ ํจํค์ง๋ค๊ณผ ๊ฐ์ด ์ฌ์ฉํ ์ ์๋๋ก ๋์์ค๋๋ค.
.wasm
๋ฐ์ด๋๋ฆฌ ์ต์ ํ์ ์กฐ์ํ๊ธฐ
wasm-opt
| ๋ ํฌ์งํ ๋ฆฌ
wasm-opt
๋ ์น์ด์
๋ธ๋ฆฌ๋ฅผ ์
๋ ฅ์ผ๋ก ๋ฐ๊ณ ๋ณํํ๊ณ ์ต์ ํํ ๋ค์, ์ํ๋ค๋ฉด ์ฝ๋๋ฅผ ์ธก์ /๋ถ์๊น์ง ํ ๋ค์ ์น์ด์
๋ธ๋ฆฌ ํ์ผ์ ์ถ๋ ฅํฉ๋๋ค. rustc
๊ฐ ์๋ํ๋ ๋ฐฉ์์ฒ๋ผ, LLVM์ผ๋ก ์์ฑํ .wasm
๋ฐ์ด๋๋ฆฌ๋ฅผ ์
๋ ฅ์ผ๋ก ๋ฃ๊ณ wasm-opt
๋ฅผ ์คํํ๋ฉด ๋ ์๊ณ ๋ ๋น ๋ฅด๊ฒ ์คํํ ์ ์๋ .wasm
๋ฐ์ด๋๋ฆฌ ํ์ผ์ ์์ฑํ ์ ์์ต๋๋ค.
wasm2js
| ๋ ํฌ์งํ ๋ฆฌ
wasm2js
ํด์ WebAssembly ํ์ผ์ "๊ฑฐ์ asm.js"์ฒ๋ผ ์ปดํ์ผ ํด์ค๋๋ค. Internet Explorer 11์ฒ๋ผ ์น์ด์
๋ธ๋ฆฌ ๊ธฐ๋ฅ์ด ๊ตฌํ๋์ง ์์ ๋ธ๋ผ์ฐ์ ๋ฅผ ์ง์ํ ๋ ์ข์ต๋๋ค. ์ด ํด์ binaryen
ํ๋ก์ ํธ์ ์ผ๋ถ์
๋๋ค.
wasm-gc
| ๋ ํฌ์งํ ๋ฆฌ
์น์ด์
๋ธ๋ฆฌ๋ฅผ ๊ฐ๋น์ง ์ฝ๋ ํ
ํ๊ณ ํ์ํ์ง ์์ ์ต์คํฌํธ, ์ํฌํธ, ํจ์ ๋ฑ์ ์ง์ธ ๋ ์ฌ์ฉ ์ ์๋ ์ฌํํ ํด์
๋๋ค. ์น์ด์
๋ธ๋ฆฌ ํ์ผ๊ณผ ํจ๊ป --gc-sections
๋ง์ปค ํ๋๊ทธ (linker flag) ๋ฅผ ์ฌ์ฉํ๋ฉด ๋ ํจ๊ณผ์ ์
๋๋ค.
๋ณดํต์ ๋ค์๊ณผ ๊ฐ์ ์ด์ ๋ก ์ด ํด์ ๊ฐ๋ฐ์๊ฐ "์ง์ " ํ๋ก์ ํธ์ ํฌํจ์ํค์ง ์์ต๋๋ค:
rustc
์ปดํ์ผ๋ฌ๊ฐ ์ด์ ์ ๋ฒ์ ์lid
๋ฅผ ์ง์ํ๋๋ฐ, ์ด ๋ฒ์ ์ด--gc-sections
๋ผ๋ ํ๋๊ทธ๋ฅผ ์ง์ํฉ๋๋ค. ์ด ํ๋๊ทธ๋ LTO ๋น๋๋ฅผ ํ๋๋ฐ ์๋์ผ๋ก ํ์ฑํ๋๊ฒ ๋ฉ๋๋ค.wasm-bindgen
CLI (์ปค๋งจ๋ ๋ผ์ธ ์ธํฐํ์ด์ค / Command Line Interface) ํด์ด ์๋์ผ๋กwasm-gc
๋ฅผ ์คํ์์ผ ์ค๋๋ค.
wasm-snip
| ๋ ํฌ์งํ ๋ฆฌ
wasm-snip
๋ ์น์ด์
๋ธ๋ฆฌ ํจ์์ ๋ด์ฉ์ unreachable
๋ช
๋ น์ด(instruction)์ผ๋ก ๋ฐ๊ฟ์ค๋๋ค.
์ด๋ค ํจ์๊ฐ ์ค์ ๋ก ํธ์ถ๋์ง ์๋๊ฑธ ์๋๋ฐ ์ปดํ์ผ๋ฌ๊ฐ ์ด๊ฑธ ๋ชจ๋ฅผ ๋๊ฐ ์๋์? ์ผ๋จ ์ปดํ์ผํ๊ณ wasm-gc
๋ฅผ ์คํํด ๋ณด์ธ์! (๋ฐํ์(runtime)์์ ํธ์ถ๋์ง ์๋) ๋ค๋ฅธ ๊ฐ์ ์ ์ผ๋ก ํธ์ถ๋๋ ๋ค๋ฅธ ํจ์๋ค๋ ๋ชจ๋ ์ ๊ฑฐ๋ฉ๋๋ค.
๋๋ฒ๊น ์ฝ๋๊ฐ ์๋ ์ค์ ๋ก ๋ฐฐํฌํ๋ ๋น๋ (production build) ์์ ๊ฐ์ ๋ก ๋ฌ์คํธ ์ ํจ๋ ์ธํ๋ผ(infrastructure)๋ฅผ ์ ์ธํ ๋ ์ ์ฉํฉ๋๋ค.
.wasm
๋ฐ์ด๋๋ฆฌ ์ดํด๋ณด๊ธฐ
twiggy
| ๋ ํฌ์งํ ๋ฆฌ
twiggy
๋ .wasm
๋ฐ์ด๋๋ฆฌ์ ์ฌ์ฉํ๋ ์ฝ๋ ํ๋กํ์ผ๋ฌ์
๋๋ค. ๋ฐ์ด๋๋ฆฌ์ ํธ์ถ ๊ทธ๋ํ (call graph) ๋ฅผ ๋ถ์ํ๊ณ ๋ค์๊ณผ ๊ฐ์ ๋ด์ฉ์ ์๋ ค์ค๋๋ค:
-
์ ์ด์ ๋น๋ํ ๋ ์ด๋ค ํจ์๊ฐ ์ ๋ฐ์ด๋๋ฆฌ์ ํฌํจ๋๋์? ์: ์ด๋ค ์ต์คํฌํธํ ํจ์๋ค์ด ๊ฐ์ ์ ์ผ๋ก ํธ์ถ๋๊ณ ์๋์?
-
์ด๋ค ํจ์๋ฅผ ์ง์ฐ๋ฉด ์ผ๋ง๋ ๊ณต๊ฐ์ ์๋ ์ ์๋์? ์: ์ด ํจ์์ ๋ค๋ฅธ ์ฌ์ฉ๋๋ ํจ์๋ค๊น์ง ์ง์ฐ๋ฉด ์ผ๋ง๋ ๊ณต๊ฐ์ด ์ ์ฝ๋๋์?
twiggy
๋ฅผ ์ฌ์ฉํด์ ๋ฐ์ด๋๋ฆฌ๋ฅผ ๋ ๊ฐ๋ณ๊ฒ ๋ง๋ค์ด๋ณด์ธ์!
wasm-objdump
| ๋ ํฌ์งํ ๋ฆฌ
wasm
๋ฐ์ด๋๋ฆฌ์ ์ ๋ ๋ฒจ ์์ธ ์ ๋ณด์ ์น์
๋ค์ ์ถ๋ ฅํฉ๋๋ค. WAT ํ
์คํธ ํฌ๋งท์ผ๋ก ๋์ด์
๋ธ๋ง(disassembling)ํ ์๋ ์์ต๋๋ค. ์น์ด์
๋ธ๋ฆฌ์ ์ฌ์ฉํ๋ objdump
์ผ๋ก ์๊ฐํด๋ ์ข์ต๋๋ค. ์ด ํด์ WABT ํ๋ก์ ํธ์ ์ผ๋ถ์
๋๋ค.
wasm-nm
| ๋ ํฌ์งํ ๋ฆฌ
.wasm
๋ฐ์ด๋๋ฆฌ ๋ด์ ์ํฌํธ๋๊ณ , ์ต์คํฌํธ๋๊ณ , ๊ทธ๋ฆฌ๊ณ private์ธ ํจ์ ์ฌ๋ณผ๋ค (function symbols) ์ ๋์ดํฉ๋๋ค. ์น์ด์
๋ธ๋ฆฌ์ ์ฌ์ฉํ๋ nm
์ผ๋ก ์๊ฐํด๋ ์ข์ต๋๋ค.
ํ๋ก์ ํธ ํ ํ๋ฆฟ
"The Rust and WebAssembly Working Group"์ ๊ฐ๋ฐ์๋ค์ด ๋น ๋ฅด๊ฒ ์ ํ๋ก์ ํธ๋ฅผ ์์ํ๊ณ ์คํํ ์ ์๋๋ก ์ฌ๋ฌ ๊ฐ์ง ํ๋ก์ ํธ ํ ํ๋ฆฟ ๋ชฉ๋ก์ ๋ง๋ค๊ณ ๊ด๋ฆฌํฉ๋๋ค.
wasm-pack-template
wasm-pack
์ผ๋ก ์
์
๋ ์ด ํ
ํ๋ฆฟ์ ๋ฌ์คํธ์ ์น์ด์
๋ธ๋ฆฌ ํ๋ก์ ํธ๋ฅผ ์์ฝ๊ฒ ์์ํ ์ ์๋๋ก ์ค๋น๋ผ ์์ต๋๋ค.
cargo generate
๋ช
๋ น์ด๋ฅผ ์คํํ์ฌ ํ๋ก์ ํธ ํ
ํ๋ฆฟ์ ํด๋ก ํด๋ณด์ธ์:
cargo install cargo-generate
cargo generate --git https://github.com/rustwasm/wasm-pack-template.git
create-wasm-app
์ด ํ
ํ๋ฆฟ์ wasm-pack
์ ํตํด ๋ฐฐํฌ๋ npm ํจํค์ง๋ฅผ ๊ฐํธํ๊ฒ ์ฌ์ฉํ ๋ ์ ์ฉํ ๊ธฐ๋ฅ๋ค์ ํฌํจํฉ๋๋ค.
npm init
๋ช
๋ น์ด๋ก ์ฌ์ฉํด ๋ณด์ธ์:
mkdir my-project
cd my-project/
npm init wasm-app
์ด ํ
ํ๋ฆฟ์ ์ฃผ๋ก wasm-pack-template
์ด๋ผ๋ ์ข
์์ฑ๊ดด ํจ๊ป ์ฌ์ฉ๋๋๋ฐ, create-wasm-app
์ผ๋ก ํ๋ก์ ํธ๋ฅผ ์์ฑํ๋ฉด npm link
๊ธฐ๋ฅ์ผ๋ก ์์ฑํ ํ๋ก์ ํธ์ ์ฐ๊ฒฐํ๋ ๋ฐ ์ฌ์ฉ๋ฉ๋๋ค.
rust-webpack-template
์ด ํ
ํ๋ฆฟ์ Rust ์ฝ๋๋ฅผ ์น์ด์
๋ธ๋ฆฌ๋ก ์ปดํ์ผํ๊ณ ์ถ๋ ฅ๋ ํ์ผ๋ค์ webpack์ rust-loader
๋ฅผ ์ฌ์ฉํด์ ๋ฐ๋ก ํ์ดํ๋ผ์ธ์ผ๋ก ํํน ํ์ฌ ์ฐ๊ฒฐํ ์ ์๋๋ก ๊ฑฐ์ ๋ชจ๋ ๋ณด์ผ๋ฌํ๋ ์ดํธ๋ค์ ๋์ ์ค์ ํด ์ค๋๋ค.
npm init
๋ช
๋ น์ด๋ก ์ฌ์ฉํด ๋ณด์ธ์:
mkdir my-project
cd my-project/
npm init rust-webpack
๋ฌ์คํธ๋ก ์์ฑํ ์น์ด์ ๋ธ๋ฆฌ ๋๋ฒ๊น ํ๊ธฐ
์ด ์น์ ์ ๋ฌ์คํธ๋ก ์์ฑํ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ๋๋ฒ๊น ํ๋๋ฐ ์ ์ฉํ ์ ๋ณด๋ฅผ ํฌํจํฉ๋๋ค.
debug ์ฌ๋ณผ์ ํฌํจํด์ ๋น๋ํ๊ธฐ
โก ๋๋ฒ๊น ํ ๋ ํญ์ debug ์ฌ๋ณผ์ ํฌํจํ๊ณ ๋น๋ํ๋์ง ํ์ธํด ์ฃผ์ธ์!
debug ์ฌ๋ณผ์ด ํ์ฑํ ๋์ด์์ง ์๋ค๋ฉด "name"
์ปค์คํ
์น์
์ด ์ปดํ์ผ๋ .wasm
ํ์ผ์ ๋ฐ์๋์ง ์์ ์๋ ์์ต๋๋ค. ์ด๋ฐ ๊ฒฝ์ฐ์๋ ์คํ ์ถ์ (stack trace) ์ ํ ๋ ํจ์ ์ด๋ฆ์ด ๋ฌ์คํธ ํจ์ ์ด๋ฆ ๋์ wasm-function[42]
์ฒ๋ผ ์ฝ๊ธฐ ์ด๋ ต๊ฒ ํ์๋ฉ๋๋ค. ์ ์์ ์ผ๋ก ์ด ์ฌ๋ณผ์ด ํ์ฑํ ๋ ๊ฒฝ์ฐ์๋ wasm_game_of_life::Universe::live_neighbor_count
์ ๊ฐ์ด ํ์๋ฉ๋๋ค.
(wasm-pack build --debug
ํน์ cargo build
๋ช
๋ น์ด๋ก) "debug" ๋น๋๋ฅผ ํ ๊ฒฝ์ฐ ์ด ์ฌ๋ณผ์ด ๊ธฐ๋ณธ๊ฐ์ผ๋ก ํ์ฑํ๋ฉ๋๋ค.
"release" ๋น๋๋ฅผ ํ ๋๋ debug ์ฌ๋ณผ์ด ๊ธฐ๋ณธ๊ฐ์ผ๋ก ํ์ฑํ๋์ง ์์ผ๋ ์ฐธ๊ณ ํด์ฃผ์ธ์. ๊ทธ๋๋ ํ์ฑํ๋ฅผ ํ๊ณ ์ถ๋ค๋ฉด Cargo.toml
ํ์ผ์ ์ด๊ณ [profile.release]
์น์
์ debug = true
๋ฅผ ํฌํจ์์ผ์ฃผ์ธ์.
[profile.release]
debug = true
console
API๋ก ๋ก๊น
ํ๊ธฐ
๋ก๊น
(logging) ์์
์ ์ฝ๋๋ฅผ ์ค๊ณํ๋ฉด์ ๋ง๋ ๊ฐ์ค๋ค์ ํ์ธํ๊ณ ํ๋ก๊ทธ๋จ์ ๋ฒ๊ทธ๋ฅผ ์ก์๋ด๋ ๋ฐ ๋งค์ฐ ํจ๊ณผ์ ์
๋๋ค. ๋ณดํต์ ์น ํ๊ฒฝ์์ console.log
ํจ์๋ฅผ ํธ์ถํ์ฌ ๋ธ๋ผ์ฐ์ ์ ๊ฐ๋ฐ์ ์ฝ์์ ๋ฉ์ธ์ง๋ฅผ ๋ก๊ทธํ ์ ์์ต๋๋ค.
ํ์ง๋ง web-sys
ํฌ๋ ์ดํธ๋ฅผ ์ฌ์ฉํ์ฌ console
๋ก๊น
ํจ์๋ฅผ ํด๋ณผ์๋ ์์ต๋๋ค.
#![allow(unused)] fn main() { extern crate web_sys; web_sys::console::log_1(&"Hello, world!".into()); }
๋ ๋ค๋ฅธ ์ต์
์ผ๋ก๋ console.error
ํจ์๊ฐ ์์ต๋๋ค. console.error
๋ console.log
์ ๊ฐ์ ํ์
์๊ทธ๋์ฒ(signature)๋ฅผ ํฌํจํ๊ณ ์์ง๋ง ๊ฐ๋ฐ์ ํด์์ ๋ก๊ทธ ๋ฉ์ธ์ง์ ํจ๊ป ์คํ ์ถ์ ์ ์ฐพ๊ณ ํ์ํ๋ ๋ฐ ์ฌ์ฉํ ์ ์์ต๋๋ค.
์ฐธ์กฐ
-
web-sys
ํฌ๋ ์ดํธ๋กconsole.log
ํจ์ ์ฌ์ฉํ๊ธฐ: -
web-sys
ํฌ๋ ์ดํธ๋กconsole.error
ํจ์ ์ฌ์ฉํ๊ธฐ: -
Console Chrome DevTools ์ฝ์์ ์ฌ์ฉํ์ฌ ์์ํด ๋ณด๊ธฐ
ํจ๋ ๋ก๊ทธ ํ๊ธฐ
console_error_panic_hook
ํฌ๋ ์ดํธ๋ console.error
ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์์์น ๋ชปํ ํจ๋๋ค์ ๋ก๊ทธ ํฉ๋๋ค. ์ด ํฌ๋ ์ดํธ๋ฅผ ํตํด ์ธ๊ณ์ด๊ฐ์ด ๋ณด์ด๊ณ ๋๋ฒ๊น
ํ๊ธฐ ์ด๋ ค์ด RuntimeError: unreachable executed
์๋ฌ ๋ฉ์ธ์ง ๋์ ๋ฌ์คํธ ํ๊ฒฝ์์ ๋ก๊ทธ ํ๋ ๊ฒ๊ณผ ๊ฐ์ด ๊น๋ํ๊ฒ ํฌ๋งท๋ ํจ๋ ๋ฉ์ธ์ง๋ฅผ ํ์ํ ์ ์์ต๋๋ค.
์ ๋ง ๊ฐ๋จํ๊ฒ๋ ์ฝ๋๊ฐ ์์๋๋ ํจ์์์ console_error_panic_hook::set_once()
๋ฅผ ํธ์ถํด์ ํ
(hook)์ ์ค์ ํ๊ธฐ๋ง ํ๋ฉด ๋ฉ๋๋ค.
#![allow(unused)] fn main() { #[wasm_bindgen] pub fn init_panic_hook() { console_error_panic_hook::set_once(); } }
๋๋ฒ๊ฑฐ ์ฌ์ฉํ๊ธฐ
์ ๋ง ์์ฝ๊ฒ๋ ์น์ด์ ๋ธ๋ฆฌ ๋๋ฒ๊น ๊ฒฝํ์ ์์ง ๊ฐ์ ๋ผ์ผํ ๋ถ๋ถ์ด ๋ง์ต๋๋ค. ๋๋ถ๋ถ์ ์ ๋์ค ์์คํ ์์๋ DWARF๋ฅผ ์ฌ์ฉํ์ฌ ์์ค ๋ ๋ฒจ์์ ์คํํ๋ ํ๋ก๊ทธ๋จ์ ์ดํด๋ณด๊ฒ ๋๋๋ฐ, ์ด DWARF๋ฅผ ์ฌ์ฉํ์ฌ ๋๋ฒ๊ฑฐ๊ฐ ํ์๋ก ํ๋ ์ ๋ณด๋ฅผ ์ธ์ฝ๋ฉ(encoding)ํ ์ ์์ต๋๋ค. ๋ฐ๋ฉด์ ์๋์ฐ์์๋ ๋์ ์ฌ์ฉํ ์ ์๋ ํฌ๋งท์ด ์์์๋ ํ์ฌ๋ก์๋ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ง์ ์ ์ผ๋ก ์ง์ํ์ง๋ ์์ต๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก ๋ฐ๋ก ์์ฑํ๋ ๋ฌ์คํธ ์์ค ํ ์คํธ ๋์ ์ปดํ์ผ๋ฌ๊ฐ ์ถ๋ ฅํ ์น์ด์ ๋ธ๋ฆฌ ๋ช ๋ น์ด๋ฅผ ๊ทธ๋๋ก ํ์ธํด์ผ ํฉ๋๋ค.
W3C ์น์ด์ ๋ธ๋ฆฌ ๊ทธ๋ฃน์ ๋๋ฒ๊น ํ์ ์กฐํญ๋ ์์ผ๋ฏ๋ก ๋ฏธ๋์๋ ์ด๋ฐ ๋ฌธ์ ๊ฐ ๊ฐ์ ๋ ๊ฒ์ผ๋ก ์์๋ฉ๋๋ค!
๊ทธ๋๋ ์น์ด์ ๋ธ๋ฆฌ์ ํจ๊ป ์์ฑ๋ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ์ดํด๋ณด๊ณ ์น์ด์ ๋ธ๋ฆฌ์ ์ํ๋ฅผ ์ง์ ์ดํด๋ณด๋๋ฐ ๋๋ฒ๊ฑฐ๊ฐ ๋งค์ฐ ์ ์ฉํ๋ฏ๋ก ์ฐธ๊ณ ํด ์ฃผ์ธ์.
์ฐธ์กฐ
- Firefox ๊ฐ๋ฐ์ ๋๊ตฌ โ ๋๋ฒ๊ฑฐ
- Microsoft Edge ๊ฐ๋ฐ์ ๋๊ตฌ โ ๋๋ฒ๊ฑฐ
- Chrome DevTools์์ ์๋ฐ์คํฌ๋ฆฝํธ ๋๋ฒ๊น ์์ํด๋ณด๊ธฐ
์ฒ์๋ถํฐ ์น์ด์ ๋ธ๋ฆฌ ๋๋ฒ๊น ์ ์ต์ํํ ์ ์๋๋ก ์ ๊ฒฝ ์จ์ ์ฝ๋๋ฅผ ์์ฑํด ์ฃผ์ธ์
๊ณ ์ณ์ผ ํ ๋ฒ๊ทธ๊ฐ ์๋ฐ์คํฌ๋ฆฝํธ๋ Web API ํ๊ฒฝ์ ํ์๋ก ํ๋ค๋ฉด wasm-bindgen-test
์ผ๋ก ํ
์คํ
์ฝ๋๋ฅผ ์์ฑํด ๋ณด์ธ์.
๊ทธ๋ ์ง ์๋ค๋ฉด #[test]
์์ฑ์ ํฌํจํ์ฌ ์ผ๋ฐ์ ์ธ ๋ฌ์คํธ ์ฝ๋์ฒ๋ผ ์คํ ํ๊ฒฝ์ ์ฌํํด ๋ณด์ธ์. ์ด๋ฐ ๋ฐฉ์์ผ๋ก ์ด์์ฒด์ ์ ์ฑ์ํ ๋ค์ดํฐ๋ธ ํด๋ง์ ์ต๋ํ ํ์ฉํ์ฌ ๋๋ฒ๊น
ํ ์ ์๊ฒ ๋ฉ๋๋ค. quickcheck
๊ณผ ๊ฐ์ ํ
์คํ
ํฌ๋ ์ดํธ์ ํ
์คํ
์ฝ๋ ์ถ์๊ธฐ๋ ๋ณด๋ฉด์ ๊ธฐ๊ณ์ ์ผ๋ก ํ
์คํธ ์ฝ๋์ ์ฌ์ด์ฆ๋ฅผ ์ค์ฌ๋ณผ์๋ ์์ต๋๋ค. ์ต์ข
์ ์ผ๋ก๋, ์๋ฐ์คํฌ๋ฆฝํธ ํ๊ฒฝ์ด ์๊ตฌ๋์ง ์๋๋ก ๋ ์์ ๋ฌ์คํธ ํ
์คํธ ์ฝ๋๋ก ๋ถ๋ฆฌ์ํค๋ ์์ผ๋ก ๋ฒ๊ทธ๋ฅผ ๋ ์ฝ๊ฒ ์ฐพ๊ณ ๊ณ ์น ์ ์๊ฒ ๋ฉ๋๋ค.
์ปดํ์ผ๋ฌ์ ๋ง์ปค(linker) ์ค๋ฅ ์์ด ๋ค์ดํฐ๋ธ #[test]
์์ฑ์ ์ฌ์ฉํ๋ ค๋ฉด Cargo.toml
ํ์ผ์ [lib.crate-type]
๋ฐฐ์ด์ rlib
๊ฐ ํฌํจ๋ผ ์๋์ง ํ์ธํด ์ฃผ์ธ์.
[lib]
crate-type ["cdylib", "rlib"]
ํ์ ํ๋กํ์ผ๋ง
์ด ์น์ ์ ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ฌ ์ฑ๋ฅ์ ๋ ์ฐ๋ฉด์๋ ์ง์ฐ ์๊ฐ๋ ์ค์ผ ์ ์๋๋ก ํ์ด์ง๋ฅผ ํ๋กํ์ผ๋งํ๋ ๋ฐฉ๋ฒ์ ๋ํด ๋ค๋ฃน๋๋ค.
โก ํ๋กํ์ผ๋ง์ ํ ๋์๋ ํญ์ ์ต์ ํ๋ ๋น๋๋ฅผ ์ฌ์ฉํด์ฃผ์ธ์!
wasm-pack build
๋ช ๋ น์ด๋ฅผ ์ฌ์ฉํ๊ณ ์๋ค๋ฉด ์ด๋ฌํ ์ต์ ํ ์ต์ ์ด ๊ธฐ๋ณธ์ ์ผ๋ก ํ์ฑํ๋ผ์์ผ๋ ์ฐธ๊ณ ํด ์ฃผ์ธ์.
์ฌ์ฉ ๊ฐ๋ฅํ ํด๋ค
window.performance.now()
ํ์ด๋จธ
performance.now()
ํจ์๋ ์น ํ์ด์ง๊ฐ ๋ก๋๋ ์์ ๋ถํฐ ์ธก์ ๋ ๋ฐ๋ฆฌ์ด ๋จ์์ ๋จ์กฐ ์๊ฐ (monotonic timestamp)๋ฅผ ๋ฐํํฉ๋๋ค.
performance.now
๋ฅผ ํธ์ถํ ๋์๋ ๊ฑฐ์ ์ค๋ฒํค๋๊ฐ ๋ฐ์ํ์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก ์์คํ
์ฑ๋ฅ์ ํฐ ์ํฅ์ ์ฃผ๊ฑฐ๋ ์ธก์ ๊ฐ์ ํธํฅ์ ์ฃผ์ง ์์ผ๋ฉด์ ๊ฐ๋จํ๊ณ ์์ธํ ์ธก์ ์ ์ํํ๊ฒ ํ ์ ์๊ฒ ๋ฉ๋๋ค.
์ด ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ํ๋ก๊ทธ๋จ์ ์ฌ๋ฌ ์์
๋ค์ด ์คํ๋๋ ์๊ฐ์ ์ธก์ ํด ๋ณผ ์ ์๊ณ , web-sys
ํฌ๋ ์ดํธ๋ฅผ ํตํด window.performance.now()
์ ์ ๊ทผํ ์๋ ์์ต๋๋ค:
#![allow(unused)] fn main() { extern crate web_sys; fn now() -> f64 { web_sys::window() .expect("should have a Window") .performance() .expect("should have a Performance") .now() } }
๊ฐ๋ฐ์ ๋๊ตฌ ๋ด์ ํ๋กํ์ผ๋ฌ
๋ชจ๋ ์น ๋ธ๋ผ์ฐ์ ์ ๊ฐ๋ฐ์ ๋๊ตฌ๋ ํ๋กํ์ผ๋ฌ๋ฅผ ํฌํจํ๊ณ ์์ต๋๋ค. ์ด๋ฌํ ํ๋กํ์ผ๋ฌ๋ค์ "call tree"๋ "flame graph"์ ๊ฐ์ ์ผ๋ฐ์ ์ธ ์๊ฐ ์๋ฃ์ ํจ๊ป ์ด๋ค ํจ์๋ค์ด ๊ฐ์ฅ ๋ง์ ์๊ฐ์ ์๋นํ๋์ง ํ์ํด ์ค๋๋ค.
"name" ์ปค์คํ
์น์
์ด ์น์ด์
๋ธ๋ฆฌ ๋ฐ์ด๋๋ฆฌ์ ํฌํจ๋๋๋ก debug ์ฌ๋ณผ์ ํฌํจํด์ ๋น๋ํ๋ฉด ํ๋กํ์ผ๋ฌ๊ฐ wasm-function[123]
์ ๊ฐ์ด ๋ณต์กํ๊ณ ๋ถํฌ๋ช
ํ ์ด๋ฆ ๋์ Rust ํจ์ ์ด๋ฆ์ ๋์ ํ์ํ๊ฒ ๋ฉ๋๋ค.
์ด๋ฌํ ํ๋กํ์ผ๋ฌ๋ค์ ์ธ๋ผ์ธ ํจ์๋ฅผ ํ์ํ์ง ์๋๋ค๋ ์ ์ ๊ธฐ์ตํด ์ฃผ์ธ์. ๋ฌ์คํธ์ LLVM๋ ์ธ๋ผ์ธ ์์ ์ ๋งค์ฐ ๋ฌด๊ฒ๊ฒ ์ํํ๊ธฐ ๋๋ฌธ์ ์ด๋ฌํ ์์ ์ ํด์ฃผ๋๋ผ๋ ๊ฒฐ๊ด๊ฐ์ด ์กฐ๊ธ์ ์ฝ๊ธฐ ์ด๋ ค์ธ์ ์์ต๋๋ค.
์ถ๊ฐ ์๋ฃ
- Firefox ๊ฐ๋ฐ์ ๋๊ตฌ โ ์ฑ๋ฅ
- Microsoft Edge ๊ฐ๋ฐ์ ๋๊ตฌ โ ์ฑ๋ฅ
- Chrome DevTools ์๋ฐ์คํฌ๋ฆฝํธ ํ๋กํ์ผ๋ฌ
console.time
์ console.timeEnd
ํจ์
console.time
๊ณผ console.timeEnd
ํจ์๋ฅผ ์ฌ์ฉํด์ ๋ธ๋ผ์ฐ์ ๊ฐ๋ฐ์ ๋๊ตฌ ์ฝ์์ ๋ช
๋ช
๋ ์์
์ ํ์ด๋ฐ์ ๊ธฐ๋กํ ์ ์์ต๋๋ค. ์์
์ด ์์๋ ๋ console.time("์ด๋ค ๊ฑฐ ํ๋ ์์
")
๋ฅผ ํธ์ถํ๊ณ , ์์
์ด ์๋ฃ๋ ๋๋ console.timeEnd("์ด๋ค ๊ฑฐ ํ๋ ์์
")
๋ฅผ ํธ์ถํฉ๋๋ค. ์ฐธ๊ณ ๋ก ์์
์ ์ด๋ฆ์ ์ง์ด์ฃผ๋ ๋ฌธ์์ด ์ธ์๋ ํ์๊ฐ ์๋๋๋ค.
web-sys
ํฌ๋ ์ดํธ๋ฅผ ํตํด ์ด๋ฌํ ํจ์๋ค์ ์ง์ ์ ์ผ๋ก ์ฌ์ฉํ ์ ์์ต๋๋ค:
web_sys::console::time_with_label("์ด๋ค ๊ฑฐ ํ๋ ์์ ")
web_sys::console::time_end_with_label("์ด๋ค ๊ฑฐ ํ๋ ์์ ")
๋ธ๋ผ์ฐ์ ์ ์ฝ์์ console.time
๋ก๊ทธ๋ฅผ ํ์ํ๋ ์คํฌ๋ฆฐ์ท์ ํ์ธํด๋ณด์ธ์:
์ถ๊ฐ๋ก, ๋ค์ ์ด๋ฏธ์ง์ ๋ณด์ด๋ ๊ฒ๊ณผ ๊ฐ์ด ๋ธ๋ผ์ฐ์ ํ๋กํ์ผ๋ฌ์ "timeline"๊ณผ "waterfall" ๋ทฐ์ console.time
๊ณผ console.timeEnd
์ ๋ก๊ทธ๊ฐ ํ์๋ฉ๋๋ค.
#[bench]
์์ฑ์ผ๋ก ๋ค์ดํฐ๋ธ ํ๊ฒฝ์ ์ฌ์ฉํ ๋
์น ๋ธ๋ผ์ฐ์ ์์ #[test]
์์ฑ์ ๋์ ์ด์ฉํ์ฌ ์ด์์ฒด์ ์ ๋ค์ดํฐ๋ธ ์ฝ๋๋ก ๋๋ฒ๊น
์ ํ๋ ๊ฒ๊ณผ ๊ฐ์ด, #[bench]
์์ฑ๊ณผ ํจ๊ป ํจ์๋ฅผ ์์ฑํด์ ์ด์ ์ฒด์ ์ ๋ค์ดํฐ๋ธ ์ฝ๋ ํ๋กํ์ผ๋ง ํด์ ์ฌ์ฉํด ๋ณผ ์๋ ์์ต๋๋ค.
์์
์ค์ธ ํฌ๋ ์ดํธ์ ํ์ ๊ฒฝ๋ก์ธ benches
์ ๋ฒค์น๋งํน ์ฝ๋๋ฅผ ์์ฑํด ๋ณด๊ฒ ์ต๋๋ค. ๋จผ์ crate-type
์ด "rlib"
์ด ํฌํจํ๊ณ ์๋์ง ํ์ธํด ์ฃผ์ธ์. ๊ทธ๋ ์ง ์๋ค๋ฉด ๋ฒค์น๋งํฌ ๋ฐ์ด๋๋ฆฌ๊ฐ ๋ฉ์ธ ๋ผ์ด๋ธ๋ฌ๋ฆฌ (main lib) ์ ๋งํฌ๋์ง ๋ชปํ๊ฒ ๋ฉ๋๋ค.
ํ์ง๋ง ์ค์ ๋ก๋ ๊ฑฐ์ ์ฌ์ฉํ์ง ์๋ ์ฝ๋์ ์๊ฐ์ ์๋นํ๊ณ ์์ ์๋ ์์ผ๋, ๋ค์ดํฐ๋ธ ์ฝ๋๋ก ๋ฒค์น๋งํน์ ํ๊ธฐ ์ ์ ๋ธ๋ผ์ฐ์ ํ๋กํ์ผ๋ง ํด์ ๋จผ์ ํ์ธํด ๋ณด๋ ๊ฒ๋ ์ข์ต๋๋ค!
์ถ๊ฐ ์๋ฃ
- Linux ํ๊ฒฝ์์
perf
ํ๋กํ์ผ๋ฌ ์ฌ์ฉํ๊ธฐ - macOS ํ๊ฒฝ์์ Instruments.app ํ๋กํ์ผ๋ฌ ์ฌ์ฉํ๊ธฐ
- VTune ํ๋กํ์ผ๋ฌ๋ Windows์ Linux๋ฅผ ์ง์ํฉ๋๋ค.
.wasm
ํ์ผ ์ฌ์ด์ฆ ์ค์ด๊ธฐ
์ด ์น์
์์๋ ์ด๋ป๊ฒ ๋ฌ์คํธ ์ฝ๋๋ฅผ ์์ ํ๊ณ .wasm
๋น๋๋ฅผ ์ต์ ํํด์ผ ๋ ์์ ์ฌ์ด์ฆ์ ๋ฐ์ด๋๋ฆฌ๋ฅผ ์ถ๋ ฅํ ์ ์๋์ง ์ดํด๋ณด๊ฒ ์ต๋๋ค.
์ ์ถ๋ ฅ๋๋ ํ์ผ์ ์ฌ์ด์ฆ๊ฐ ์ค์ํ๊ฐ์?
.wasm
ํ์ผ์ ๋คํธ์ํฌ๋ก ์ ์กํ ๋ ํ์ผ์ ์ฌ์ด์ฆ๊ฐ ์์์๋ก ํด๋ผ์ด์ธํธ์์ ๋ ๋น ๋ฅด๊ฒ ๋ค์ด๋ก๋ ํ ์ ์์ต๋๋ค. .์น์ด์
๋ธ๋ฆฌ
ํ์ผ์ด ๋นจ๋ฆฌ ๋ค์ด๋ก๋ ๋ ์๋ก ํ์ด์ง๊ฐ ๋ ๋นจ๋ฆฌ ๋ก๋๋๊ณ ์ ์ ๊ฒฝํ์ด ๋ ๋์์ง๊ฒ ๋ฉ๋๋ค.
ํ์ง๋ง ์ฝ๋ ์ฌ์ด์ฆ๊ฐ ์ด์ฉ๋ฉด ์ ์ผ ์ค์ํ๊ฒ ํ์ธํด์ผ ํ ๋ถ๋ถ์ด ์๋ ์๋ ์์ต๋๋ค. ๋ชจํธํ๊ณ ์ธก์ ํ๊ธฐ ์ด๋ ต๊ฒ ์ง๋ง "ํ์ด์ง๊ฐ ๋ก๋๋๊ณ ์ฌ์ฉํ ์ ์๊ฒ ๋ ๋๊น์ง ๊ฑธ๋ฆฌ๋ ์๊ฐ"์ด ์ค์ ๋ก๋ ๋ ์ค์ํ๊ฒ ๊ณ ๋ คํด ๋ด์ผ ํ ๋ถ๋ถ์ผ ์๋ ์์ต๋๋ค. (์ฝ๋๊ฐ ๋ก๋๋ผ์ผ ์ฌ์ดํธ๊ฐ ์๋ํ๊ธฐ ์์ํ๋ค๋ ๋ด์ฉ์ ์๊ฐํด ๋ณธ๋ค๋ฉด) ์ฝ๋ ์ฌ์ด์ฆ๊ฐ ์ด ์๊ฐ์ ํฐ ์ํฅ์ ๋ฏธ์น๊ธด ํ์ง๋ง ์ด๊ฒ ์ ์ผํ๊ฒ ํ์ธํด์ผ ํ ๋ถ๋ถ์ ์๋ ๊ฒ์ ์ ์ ์์ต๋๋ค.
์น์ด์ ๋ธ๋ฆฌ๋ ๋ณดํต gzip ํ์ผ ํฌ๋งท ํ์์ผ๋ก ์์ถ๋์ด ์ ์ก๋๋๋ฐ, ๊ทธ๋ฌ๋ฏ๋ก ์ ์ ์ ํตํด ํ์ผ์ ๋ ๋น ๋ฅด๊ฒ ๋ณด๋ผ ์ ์๋๋ก gzip ํฌ๋งท์ผ๋ก ์์ถ๋ ํ์ผ์ ์ฌ์ด์ฆ๋ฅผ ๋น๊ตํด์ผ ํฉ๋๋ค. ์ฐธ๊ณ ๋ก ์น์ด์ ๋ธ๋ฆฌ ๋ฐ์ด๋๋ฆฌ ํฌ๋งท์ gzip ํฌ๋งท์ ์ ํฉํ๋ฏ๋ก 50% ์ด์์ผ๋ก ์ฌ์ด์ฆ๋ฅผ ์ค์ผ ์ ์์ต๋๋ค.
๊ฒ๋ค๊ฐ, ์น์ด์
๋ธ๋ฆฌ์ ๋ฐ์ด๋๋ฆฌ ํฌ๋งท์ ๋งค์ฐ ๋น ๋ฅด๊ฒ ์ฝ๊ณ ์ฒ๋ฆฌํ ์ ์๋๋ก ์ต์ ํ๊ฐ ์ ๋์ด์์ต๋๋ค. ์์ฆ ์ฌ์ฉ๋๋ ๋ธ๋ผ์ฐ์ ๋ค์ ๋ณดํต "baseline compilers" ๋ผ๋ ๊ธฐ๋ฅ์ ๊ฐ์ง๊ณ ์๋๋ฐ, ์ด ๊ธฐ๋ฅ์ ํตํด ๋คํธ์ํฌ๋ก ์น์ด์
๋ธ๋ฆฌ ํ์ผ์ ๋ณด๋ด๋ ๋์์ ๋์ผํ๊ฒ ๋น ๋ฅธ ์๋๋ก ์ ์ก๋ฐ์ ์น์ด์
๋ธ๋ฆฌ ์ฝ๋๋ฅผ ๋ค์ดํฐ๋ธ ๊ธฐ๊ณ์ด๋ก ์ปดํ์ผํ ์ ์์ต๋๋ค. ๊ทธ๋ ๊ธฐ ๋๋ฌธ์ instantiateStreaming
์ ์ฌ์ฉํ๋ค๋ฉด ์นํ์ด์ง๊ฐ ํ ๋ฒ ๋ก๋ ๋ ์ดํ๋ถํฐ๋ ์น์ด์
๋ธ๋ฆฌ ๋ชจ๋์ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค. ๋ฐ๋ฉด์ ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ฅผ ์คํํ ๋๋ ์ฃผ๋ก ํ์ฑํ๋ ์ฒ๋ฆฌ ์ธ์๋ ์ฝ๋๋ฅผ ๋น ๋ฅด๊ฒ ์คํํ ์ ์๋๋ก JIT ์ปดํ์ผ ๊ณผ์ ๋ฑ์ ๊ฑฐ์ณ์ผ ํ๊ธฐ ๋๋ฌธ์ ์ฃผ๋ก ์๊ฐ์ด ๋ ์ค๋ ๊ฑธ๋ฆฌ๊ฒ ๋ฉ๋๋ค.
๋ง์ง๋ง์ผ๋ก, ์น์ด์ ๋ธ๋ฆฌ๊ฐ ์คํ ์๋ ์ธก๋ฉด์์ ์๋ฐ์คํฌ๋ฆฝํธ์ ๋น๊ตํ์ ๋ ํจ์ฌ ์ต์ ํ๊ฐ ์ ๋ผ์๋ค๋ ๋ถ๋ถ์ ๊ธฐ์ตํด์ฃผ์ธ์. ๋ ํ์คํ๊ฒ ํ๊ณ ์ถ๋ค๋ฉด ์๋ฐ์คํฌ๋ฆฝํธ์ ์น์ด์ ๋ธ๋ฆฌ ๋ฐํ์ ์๋๋ฅผ ๊ฐ๊ฐ ์ธก์ ํด ๋ณด๊ณ ์ฝ๋ ์ฌ์ด์ฆ๊ฐ ์ผ๋ง๋ ์ค์ํ์ง ํ์ธํด๋ณผ์๋ ์์ต๋๋ค.
ํ์ง๋ง .wasm
ํ์ผ์ ์ฌ์ด์ฆ๊ฐ ์์๋ณด๋ค ํฌ๋๋ผ๋ ๋ฐ๋ก ๋๋ดํ์ง๋ ๋ง์์ฃผ์ธ์! ์ฝ๋ ์ฌ์ด์ฆ๋ ํฐ ๊ทธ๋ฆผ์ ์ผ๋ถ์ผ ๋ฟ์
๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ์ ์น์ด์
๋ธ๋ฆฌ๋ฅผ ๋น๊ตํ ๋ ์ฝ๋ ์ฌ์ด์ฆ๋ง ๋น๊ตํ๊ฒ ๋๋ค๋ฉด ๋ง์ ๋ถ๋ถ์ ๋์น๊ฒ ๋ฉ๋๋ค.
์ฝ๋ ์ฌ์ด์ฆ๋ฅผ ์ค์ผ ์ ์๋๋ก ๋น๋ ์ต์ ํํ๊ธฐ
rustc
์๋ ๋ ์์ .wasm
๋ฐ์ด๋๋ฆฌ๋ฅผ ์์ฑํ ๋ ์ ์ฉํ ์ต์
์ ๋ช๊ฐ์ง ๊ฐ์ง๊ณ ์์ต๋๋ค. ์ด๋ค ์ํฉ์์๋, ์ปดํ์ผ ์๊ฐ์ด ๋ ์ค๋ ๊ฑธ๋ฆฌ๋ ๋ถ๋ถ์ ํฌ์ํด์ .wasm
์ฌ์ด์ฆ๋ฅผ ๋ ์๊ฒ ์ค์ด๊ธฐ๋ ํฉ๋๋ค. ๋ ๋ค๋ฅธ ๊ฒฝ์ฐ์๋, ๋ ๋น ๋ฅธ ๋ฐํ์์ ์ํด .wasm
ํ์ผ ์ฌ์ด์ฆ๋ฅผ ํฌ๊ธฐํ๊ธฐ๋ ํฉ๋๋ค. ๊ฐ ์ต์
์ ์ฅ๋จ์ ์ ์ ์ดํดํ๊ณ , ํ๋กํ์ผ๋ง๊ณผ ์ธก์ ์์
์ ํด๋ณด๋ฉด์ ์ฝ๋ ์ฌ์ด์ฆ์ ๋ฐํ์ ์๋ ์ค ์ด๋ค ๊ฒ์ด ์ฐ์ ์๋์ด์ผ ํ๋์ง ์ ์๊ณ ๊ฒฐ์ ํ๋ ๊ฒ์ด ์ค์ํฉ๋๋ค.
๋งํฌ ์๊ฐ ์ต์ ํ (Link Time Optimizations, LTO) ์ ์ฌ์ฉํด์ ์ปดํ์ผํ๊ธฐ
Cargo.toml
ํ์ผ์ [profile.release]
์น์
์ lto = true
๋ฅผ ์ถ๊ฐํด ์ฃผ์ธ์:
[profile.release]
lto = true
์ด๋ ๊ฒ LLVML์ด ์ฌ์ฉํ์ง ์๋ ์ฝ๋๋ฅผ ๋ ๊ณต๊ฒฉ์ ์ผ๋ก ์ ๊ฑฐํ๊ณ ์ธ๋ผ์ธ ์์
์ ๋ ์ ๊ทน์ ์ผ๋ก ์ฒ๋ฆฌํ ์ ์๋๋ก ์ค์ ํ ์ ์์ต๋๋ค. LTO๋ฅผ ์ฌ์ฉํ๋ฉด .wasm
ํ์ผ์ ์ฌ์ด์ฆ๊ฐ ์์์ง ๋ฟ ์๋๋ผ, ๋ฐํ์์ ๋ ๋น ๋ฅด๊ฒ ๋ง๋ค ์๋ ์์ต๋๋ค! ํ์ง๋ง ์ปดํ์ผ ์์
์ด ๋ ์ค๋ ๊ฑธ๋ฆฐ๋ค๋ ๋จ์ ์ด ์์ผ๋ ์ฐธ๊ณ ํด ์ฃผ์ธ์.
๋ฐํ์ ์๋ ๋์ ์ฝ๋ ์ฌ์ด์ฆ๋ฅผ ์ต์ ํํ๋๋ก LLVM ์ค์ ํ๊ธฐ
LLVM์ ์ต์ ํ ์์
์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ฌ์ด์ฆ ๋์ ์๋ ๊ฐ์ ์ ์ค์ ์ ๋๊ณ ์งํ๋ฉ๋๋ค. [profile.release]
ํ์ผ์ [profile.release]
์น์
์ ์์ ํ์ฌ ์ด ์ค์ ์ ๋ณ๊ฒฝํ ์๋ ์์ต๋๋ค:
[profile.release]
opt-level = 's'
์ถ๊ฐ๋ก ๋ฐ์ํ ์ ์๋ ์๋ ์ ํ๋ฅผ ๊ฐ์ํด์๋ผ๋ ๋ ๊ณต๊ฒฉ์ ์ผ๋ก ์ต์ ํ๋ฅผ ํด๋ณผ ์๋ ์์ต๋๋ค:
[profile.release]
opt-level = 'z'
์ ๋ง ๋๋๊ฒ๋, ์ถ๋ ฅ๋๋ ํ์ผ์ ์ฌ์ด์ฆ๊ฐ opt-level = "z"
๋์ opt-level = "s"
๋ฅผ ์ฌ์ฉํ์ ๋ ๋ ์์์ง๋ ๊ฒฝ์ฐ๋ ์์ต๋๋ค. ํญ์ ์ ํ์ธํด ๋ณด๋ ๊ฑธ ์์ง ๋ง์ ์ฃผ์ธ์!
wasm-opt
ํด ์ฌ์ฉํ๊ธฐ
Binaryen ํดํท์ ์น์ด์
๋ธ๋ฆฌ์ ํนํ๋ ์ปดํ์ผ๋ฌ ํด๋ง์ ํฌํจํฉ๋๋ค. ๋จ์ํ LLVM์ ์น์ด์
๋ธ๋ฆฌ ๋ฐฑ์๋ ์์
์ธ์๋ ํจ์ฌ ๋ ๋ค์ํ ์์
์ ์ฌ์ฉํ ์ ์๊ณ , wasm-opt
ํด์ ์ฌ์ฉํด์ LLVM์ด ๋น๋ํ .wasm
๋ฐ์ด๋๋ฆฌ๋ฅผ ์ต์ ํ ํ๋ฉด ๋ณดํต์ ์ฝ๋ ์ฌ์ด์ฆ๋ฅผ 15-20% ์ ๋ ๋ ์ค์ผ ์ ์๊ฒ ๋ฉ๋๋ค. ๋ฐํ์ ์๋๋ ๊ฐ์ด ๊ฐ์ ๋ ์๋ ์์ผ๋ ์ฐธ๊ณ ํด ์ฃผ์ธ์!
# ์ฌ์ด์ฆ ์ต์ ํ.
wasm-opt -Os -o output.wasm input.wasm
# ๊ณต๊ฒฉ์ ์ธ ์ฌ์ด์ฆ ์ต์ ํ.
wasm-opt -Oz -o output.wasm input.wasm
# ์๋ ์ต์ ํ.
wasm-opt -O -o output.wasm input.wasm
# ๊ณต๊ฒฉ์ ์ธ ์๋ ์ต์ ํ.
wasm-opt -O3 -o output.wasm input.wasm
๋๋ฒ๊ทธ ์ ๋ณด์ ๊ดํด ์์๋๋ฉด ์ข์ ์
์น์ด์
๋ธ๋ฆฌ ๋ฐ์ด๋๋ฆฌ ์ฌ์ด์ฆ๋ฅผ ์ค์ด๋ ๋ฐ ๋ฐ์ด๋๋ฆฌ ํ์ผ์ ํฌํจ๋ ๋๋ฒ๊ทธ ์ ๋ณด์ names
์น์
์ด ์ ๋ง ํฐ ์ญํ ์ ํฉ๋๋ค. ํ์ง๋ง wasm-pack
์ด ๊ธฐ๋ณธ๊ฐ์ผ๋ก ๋๋ฒ๊ทธ ์ ๋ณด๋ฅผ ์ญ์ ํ๊ณ , ์ถ๊ฐ๋ก wasm-opt
๋ ๋ช
๋ น์ด์ -g
๊ฐ ํฌํจ๋์ง ์๋ ์ด์ names
์น์
์ ๊ธฐ๋ณธ์ ์ผ๋ก ์ง์ฐ๋ ๋ถ๋ถ์ ์ ๊ธฐ์ตํด ์ฃผ์ธ์.
์ด ์ฑ
์ ์ ๋ฐ๋ผ์๋ค๋ฉด ๊ธฐ๋ณธ์ ์ผ๋ก๋ ๋๋ฒ๊ทธ ์ ๋ณด๋ names
์น์
์์ด ์น์ด์
๋ธ๋ฆฌ ํ์ผ์ ๋น๋ํ๊ฒ ๋ฉ๋๋ค. ํ์ง๋ง, ์ด๋ฌํ ๋๋ฒ๊น
์ ๋ณด๊ฐ ์น์ด์
๋ธ๋ฆฌ ๋ฐ์ด๋๋ฆฌ์ ํฌํจ๋ผ์ผ ํ๋ ์ํฉ์์๋ ์ด ๋ด์ฉ์ ์ ์ฐธ๊ณ ํด ์ฃผ์ธ์!
์ฌ์ด์ฆ ํ๋กํ์ผ๋งํ๊ธฐ
๋น๋ ์ต์ ํ ์ค์ ์ ๋ฐ๊ฟจ๋๋ฐ๋ .wasm
์ฝ๋ ์ฌ์ด์ฆ๊ฐ ์ถฉ๋ถํ ์ค์ด๋ค์ง ์๋๋ค๋ฉด, ์ด๋ค ๋ถ๋ถ์ด ๋๋จธ์ง ๊ณต๊ฐ์ ์ฌ์ฉํ๋์ง ํ๋กํ์ผ๋ง ์์
์ ํด๋ณด๋ฉด์ ์์๋ณด๋๋ก ํฉ์๋ค.
โก ํ์ ํ๋กํ์ผ๋ง ๊ฐ์ด๋๋ฅผ ๋ฐ๋ผ์๋ ๊ฒ์ฒ๋ผ, ์ฌ์ด์ฆ ํ๋กํ์ผ๋ง ๊ฐ์ด๋๋ ํ๋ฒ ์ฝ์ด๋ณด๊ณ ์๋ํด ๋ด ์๋ค. ์ฝ์ด๋ณด๋ ๊ฒ๋ง์ผ๋ก๋ ์๊ฐ์ ์ ๋ง ๋ง์ด ์๋ ์ ์์ต๋๋ค!
twiggy
์ฝ๋ ์ฌ์ด์ฆ ํ๋กํ์ผ๋ฌ
twiggy
๋ ์น์ด์
๋ธ๋ฆฌ๋ฅผ ์
๋ ฅ์ผ๋ก ๋ฐ๋ ์ฝ๋ ์ฌ์ด์ฆ ํ๋กํ์ผ๋ฌ์
๋๋ค. ๋ฐ์ด๋๋ฆฌ์ ํธ์ถ ๊ทธ๋ํ (call graph) ๋ฅผ ๋ถ์ํ๊ณ ๋ค์๊ณฝ ๊ฐ์ ๋ด์ฉ์ ์๋ ค์ค๋๋ค:
-
์ด๋ค ํจ์๋ค์ด ์ ์ด์ ์ ๋ฐ์ด๋๋ฆฌ์ ํฌํจ๋๋ ๊ฑด๊ฐ์?
-
์ด ํจ์์ ์ฌ์ฉ๋๋ ๊ณต๊ฐ์ ํฌ๊ธฐ๊ฐ ์ด๋ป๊ฒ ๋๋์? ์: ์ด ํจ์์ ๋ด๋ถ์์ ํธ์ถํด์ ์ฌ์ฉํ๊ฒ ๋๋ ํจ์๋ค๊น์ง ์ ๊ฑฐํ๋ค๋ฉด ์ผ๋ง๋ ๊ณต๊ฐ์ ์๋ ์ ์๋์?
$ twiggy top -n 20 pkg/wasm_game_of_life_bg.wasm
Shallow Bytes โ Shallow % โ Item
โโโโโโโโโโโโโโโโผโโโโโโโโโโโโผโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโโ
9158 โ 19.65% โ "function names" subsection
3251 โ 6.98% โ dlmalloc::dlmalloc::Dlmalloc::malloc::h632d10c184fef6e8
2510 โ 5.39% โ <str as core::fmt::Debug>::fmt::he0d87479d1c208ea
1737 โ 3.73% โ data[0]
1574 โ 3.38% โ data[3]
1524 โ 3.27% โ core::fmt::Formatter::pad::h6825605b326ea2c5
1413 โ 3.03% โ std::panicking::rust_panic_with_hook::h1d3660f2e339513d
1200 โ 2.57% โ core::fmt::Formatter::pad_integral::h06996c5859a57ced
1131 โ 2.43% โ core::str::slice_error_fail::h6da90c14857ae01b
1051 โ 2.26% โ core::fmt::write::h03ff8c7a2f3a9605
931 โ 2.00% โ data[4]
864 โ 1.85% โ dlmalloc::dlmalloc::Dlmalloc::free::h27b781e3b06bdb05
841 โ 1.80% โ <char as core::fmt::Debug>::fmt::h07742d9f4a8c56f2
813 โ 1.74% โ __rust_realloc
708 โ 1.52% โ core::slice::memchr::memchr::h6243a1b2885fdb85
678 โ 1.45% โ <core::fmt::builders::PadAdapter<'a> as core::fmt::Write>::write_str::h96b72fb7457d3062
631 โ 1.35% โ universe_tick
631 โ 1.35% โ dlmalloc::dlmalloc::Dlmalloc::dispose_chunk::hae6c5c8634e575b8
514 โ 1.10% โ std::panicking::default_hook::{{closure}}::hfae0c204085471d5
503 โ 1.08% โ <&'a T as core::fmt::Debug>::fmt::hba207e4f7abaece6
LLVM-IR ์ง์ ์ดํด๋ณด๊ธฐ
LLMV-IR์ LLVM์ด ์น์ด์
๋ธ๋ฆฌ๋ฅผ ์ถ๋ ฅํ๊ธฐ ์ ์ ๊ฑฐ์น๊ฒ ๋๋ ์ต์ข
์ค๊ฐ ํํ (final intermediate representation) ์
๋๋ค. ์ด ์ต์ข
์ค๊ฐ ํํ์ ์ต์ข
์ ์ผ๋ก ์ถ๋ ฅํ๋ ์น์ด์
๋ธ๋ฆฌ ๋ฐ์ด๋๋ฆฌ์ ๋งค์ฐ ์ ์ฌํ๊ฒ ์๊ฒผ์ต๋๋ค. LLVM-IR์ ํฌ๊ธฐ๊ฐ ํด์๋ก ์ถ๋ ฅ๋๋ .wasm
ํ์ผ์ ํฌ๊ธฐ๋ ์ปค์ง๊ฒ ๋๊ณ , ํจ์๋ค์ด LLVM-IR ํฌ๊ธฐ์ 25%๊น์ง ์ฐจ์งํ๊ฒ ๋๋ค๋ฉด .wasm
ํ์ผ์์๋ ํจ์๋ค์ด ๋ง์ฐฌ๊ฐ์ง๋ก 25%๋ฅผ ์ฐจ์งํ๊ฒ ๋ฉ๋๋ค. ์ด๋ฌํ ์์น๋ค์ด ๋ณดํต์ ์ผ์นํ์ง๋ง, LLVM-IR์ .wasm
ํ์ผ์ด (DWARF์ ๊ฐ์ ๋๋ฒ๊น
์ ๋ณด ์ฒ๋ผ) ๊ฐ์ง๊ณ ์์ง ์๋ ๋ค๋ฅธ ์ค์ํ ์ ๋ณด๋ค๋ ๊ฐ์ง๊ณ ์๋ ์ ๋ํ ์ ์ฐธ๊ณ ํด ์ฃผ์ธ์. ์ด๋ฌํ ํ์ ๋ฃจํด๋ค์ ํด๋น ํจ์์ ์์น์ ์ธ๋ผ์ธ๋ฉ๋๋ค.
cargo
๋ช
๋ น์ด๋ฅผ ์คํํด์ LLVM-IR ํ์ผ์ ์ง์ ์์ฑํด ๋ณด์ธ์:
cargo rustc --release -- --emit llvm-ir
๊ทธ๋ค์ find
๋ช
๋ น์ด๋ฅผ ์ฌ์ฉํด์ .ll
ํ์ผ์ ๊ฒ์ํด ๋ณด๊ฒ ์ต๋๋ค. ์ด LLVM-IR ํ์ผ์ cargo
์ target
๊ฒฝ๋ก์ ์์นํ๊ฒ ๋ฉ๋๋ค:
find target/release -type f -name '*.ll'
์ฐธ์กฐ
ํด๊ณผ ํ ํฌ๋์ ํ์ฉํ์ฌ ๋ ๊น๊ฒ ํ๊ณ ๋ค์ด์ ์ต์ ํํ๊ธฐ
.wasm
๋ฐ์ด๋๋ฆฌ ์ฌ์ด์ฆ๋ฅผ ์ค์ด๋ ์ค์ ์ ๋ณดํต ์๋ํ๊ฐ ๋ผ ์์ต๋๋ค. ํ์ง๋ง ์ถ๊ฐ๋ก ๋ถํ์ํ ์ฝ๋๋ฅผ ์ ๊ฑฐํ๊ณ ์ต์ ํ๋ฅผ ํด์ค์ผ ํ๋ ๊ฒฝ์ฐ์๋ ๋ ๊น๊ฒ ๋ค์ด๊ฐ์ ์์ ํด ์ค์ผ ํ ๋๊ฐ ์์ต๋๋ค. ์ด ์น์
์์๋ ์ฝ๋ ์ฌ์ด์ฆ๋ฅผ ์ค์ผ ๋ ์ฌ์ฉํด ๋ณผ ์ ์๋ ํฌ๋ฐํ ๋ฐฉ๋ฒ๋ค์ ๋ํด ์์๋ณด๊ฒ ์ต๋๋ค.
๋ฌธ์์ด ํฌ๋งท ํผํ๊ธฐ
format!
์ด๋ to_string
๊ณผ ๊ฐ์ ํจ์/๋งคํฌ๋ก๋ค์ ์ฌ์ฉํ๋ฉด ์ถ๋ ฅ๋๋ ๋ฐ์ด๋๋ฆฌ์ ์ฌ์ด์ฆ๊ฐ ๋ถํ์ํ๊ฒ ์ปค์ง ์๋ ์์ต๋๋ค. ๊ฐ๋ฅํ๋ฉด ๋ฌธ์์ด ํฌ๋งท์ ๋๋ฒ๊ทธ ๋ชจ๋์์๋ง ์ฌ์ฉํ๊ณ , ๋ฐฐํฌ ๋ฒ์ ์ ๋น๋ํ ๋์๋ ์ ์ ๋ฌธ์์ด (static string) ์ ์ฌ์ฉํด ๋ณด์ธ์.
์ฝ๋ ํจ๋ ํผํ๊ธฐ
๋ง์ฒ๋ผ ์ฝ์ง๋ ์๊ฒ ์ง๋ง, twiggy
์ ๊ฐ์ ํด์ ์ฌ์ฉํ๊ฑฐ๋ LLVM-IR ํ์ผ์ ์ดํด๋ณด๋ฉด์ ์ด๋ค ํจ์๋ค์ด ํจ๋ํ๋์ง ์ดํด๋ณผ ์ ์์ต๋๋ค.
ํจ๋์ด ํญ์ panic!()
๋งคํฌ๋ก์ ํ์์ผ๋ก ๋ํ๋์ง๋ ์๊ณ ๋ณดํต์ ๋ค์๊ณผ ๊ฐ์ ์ฌ๋ฌ ๊ฐ์ง ๋ค์ํ ์ด์ ๋ก ๋ฐ์ํ ์ ์์ต๋๋ค:
-
์ฌ๋ผ์ด์ค ์ฌ์ด์ฆ ๋ฒ์๋ฅผ ๋ฒ์ด๋ ์ธ๋ฑ์ค์ ์์์ ์ ๊ทผํ๊ณ ์ ์๋ํ ๋ (out of bounds) :
my_slice[i]
-
๋๋จธ์ง ์ฐ์ฐ์๋ฅผ ์ฌ์ฉํ ๋ ๋๋๋ ์๊ฐ 0์ธ ๊ฒฝ์ฐ:
๋๋ ์ง๋ ์ / ๋๋๋ ์
-
Option
์ด๋Result
์ ๊ฐ์unwrap()
๋ฅผ ์ฌ์ฉํ์ฌ ๊ฐ์ ์ ๊ทผํ ๋:opt.unwrap()
๋๋res.unwrap()
์ฒ์ ๋ ๋ฐฉ๋ฒ ๋์ ์ฌ์ฉํด ๋ณผ ์ ์๋ ๋ ์์ ํ ๋ฐฉ๋ฒ๋ค๋ ์์ต๋๋ค. my_slice[i]
์ฒ๋ผ ์ธ๋ฑ์ค๋ฅผ ํตํด ์ง์ ์ ๊ทผํ์ง ์๊ณ my_slice.get(i)
๋ฅผ ์ฌ์ฉํ์ฌ Option
ํ์
์ ๊ฐ์ ์ ๊ทผํด ๋ณผ ์๋ ์๊ณ , check_div
ํจ์๋ฅผ ๋ถ๋ฌ์ ๊ฐ์ ๋๋์ ์๋์ง ํ์ธํด ๋ณผ ์๋ ์์ต๋๋ค. ์ด๋ ๊ฒ 3๋ฒ์งธ ๊ฒฝ์ฐ๋ง ์ง์ค์ ์ผ๋ก ์ ๊ฒฝ ์ธ ์ ์๊ฒ ๋์ต๋๋ค.
์ถ๊ฐ๋ก, ์ฝ๋๋ฅผ ํจ๋ ์ํค์ง ์์ผ๋ฉด์ Option
์ด๋ Result
ํ์
์ ๊ฐ์ "์์ ํ ๋ฐฉ๋ฒ๊ณผ ๋ถ์์ ํ ๋ฐฉ๋ฒ", ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ์ฒ๋ฆฌํด๋ณผ์๋ ์์ต๋๋ค.
์์ ํ ๋ฐฉ๋ฒ๋ถํฐ ํ๋ฒ ์ดํด๋ณด๋๋ก ํฉ์๋ค. None
์ด๋ Error
๊ฐ์ ๋ฐํ๋ฐ์์ ๋ ์ฝ๋๋ฅผ ํจ๋์ํค๋ ๋์ abort
ํจ์๋ฅผ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค:
#![allow(unused)] fn main() { #[inline] pub fn unwrap_abort<T>(o: Option<T>) -> T { use std::process; match o { Some(t) => t, None => process::abort(), } } }
์ต์ข
์ ์ผ๋ก๋ ํจ๋ ์ฝ๋๊ฐ wasm32-unknown-unknown
ํ๊ฒ์ abort ๋ช
๋ น์ด๋ก ์ฎ๊ฒจ์ง๊ธฐ ๋๋ฌธ์, ์ด๋ฐ ์์ผ๋ก ์ฝ๋๋ฅผ ์์ฑํ๋ฉด์ ๋ถํ์ํ ์ฝ๋๋ฅผ ์ง์ธ ์ ์๊ฒ ๋ฉ๋๋ค.
๋ค๋ฅธ ์๋ํด๋ณผ์ ์๋ ๋ฐฉ๋ฒ์ผ๋ก๋, unreachable
ํฌ๋ ์ดํธ๊ฐ ์์ต๋๋ค. ์ด ํฌ๋ ์ดํธ๋ Option
๊ณผ Result
ํ์
์ ๊ฐ๋ค๊ณผ ํจ๊ป ์ฌ์ฉํ ์ ์๋๋ก ๋ถ์์ ํ unchecked_unwrap
ํ์ฅ ํฌ๋ ์ดํธ๋ค์ ์ ๊ณตํ๋๋ฐ, ์ปดํ์ผ๋ฌ๊ฐ Option
์ Some
์ผ๋ก, Result
๋ฅผ Ok
๋ก ์ถ์ธกํด์ ์ฎ๊ธธ ์ ์๋๋ก ๋์์ค๋๋ค. ํ์ง๋ง ์ด๋ฐ ์ถ์ธก์ด ๋ง์๋จ์ด์ง์ง ์์ผ๋ฉด ์ ์ํ์ง ์์ ๋์ (undefined behavior) ์ด ๋ฐ์ํ๊ฒ ๋๋ฏ๋ก ์ฝ๋๊ฐ ์ ์๋ํ๋ค๊ณ ํ์ ํ์ง๋ง ์ปดํ์ผ๋ฌ๊ฐ ์ ๋ชจ๋ฅด๊ณ ์๋ ์ํฉ์ ์ ์ดํดํ๊ณ ์์ ๋๋ง ์ด ๋ฐฉ๋ฒ์ ์ฌ์ฉํด ์ฃผ์ธ์. ์ผ๋ฐ์ ์ผ๋ก๋ ์ด ๋ฐฉ๋ฒ์ ๋ฐฐํฌ ๋ฒ์ ์์๋ง ์ ์ฉํ๊ณ ๊ทธ ์ธ์๋ ์ปดํ์ผ๋ฌ๊ฐ ํ์ธํ ์ ์๋๋ก ๋๋ฒ๊ทธ ๋น๋๋ฅผ ๋ฐ๋ก ์ค์ ํ๊ธธ ๊ถ์ฅํฉ๋๋ค.
ํ ๋น์ ํผํ๊ฑฐ๋ wee_alloc
์ ๋์ ์ฌ์ฉํด ๋ณด์ธ์.
๋ฌ์คํธ๋ ๊ธฐ๋ณธ๊ฐ์ผ๋ก dlmalloc
๋ผ๋ ํ ๋น์๋ฅผ ์ด์ํด์ ์ฌ์ฉํฉ๋๋ค. ์ด ํ ๋น์๋ 10 KB ์ ๋์ ์ฌ์ด์ฆ๋ฅผ ์ฐจ์งํ๊ฒ ๋๋๋ฐ, ๋์ ํ ๋น์ ์ฌ์ฉํ์ง ์์๋ ๊ด์ฐฎ๋ค๋ฉด ์ด ์ฌ์ด์ฆ๋ฅผ ์ ์ฝํด ๋ณผ ์๋ ์์ต๋๋ค.
์์ ํ ๋์ ํ ๋น ์์ด ์์
ํ๊ธฐ๊ฐ ์ฌ์ค์ ์ฝ์ง๋ ์์ ํธ์ธ๋ฐ, ์ฝ๋์ ํซ ์คํ (hot spot) ์์ ํ ๋น์ ์์ ๋ ์์
์ ๋๋ถ๋ถ์ ํจ์ฌ ์ฌ์ด ํธ์
๋๋ค. (๋ณดํต์ ์ด ์์
์ ํตํด ํซ ์คํ์ธ ์ฝ๋๋ค์ ํจ์ฌ ๋น ๋ฅด๊ฒ ๋ง๋ค ์๋ ์์ต๋๋ค.) ์ด๋ฌํ ์ํฉ์์ ์ ์ญ ํ ๋น์ ๋์ wee_alloc
๋ฅผ ์ฌ์ฉํ๋ฉด (์ ๋ถ๋ ์๋์ง๋ง) ์ด 10 KB ๋งํผ ์ฐจ์ง๋๋ ๊ณต๊ฐ์ ๋๋ถ๋ถ์ ์ ์ฝํ ์ ์์ต๋๋ค. wee_alloc
๋ ํ ๋น์๋ฅผ ํ์๋ก ํ์ง๋ง ๊ตณ์ด ์์ฃผ ๋น ๋ฅผ ํ์๊ฐ ์๊ณ , ์คํ ์๋๊ฐ ๋๋ ค์ง๋ ๋์ ์ฝ๋ ์ฌ์ด์ฆ๋ฅผ ์ค์ฌ๋ ๊ด์ฐฎ์ ๋ ์ฌ์ฉํ๋๋ก ์ค๊ณ๋์ต๋๋ค.
์ ๋ค๋ฆญ ํ์ ๋งค๊ฐ๋ณ์ ๋์ ํธ๋ ์ดํธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด ๋ณด์ธ์
๋ค์ ์์์ ๊ฐ์ด ํ์ ๋งค๊ฐ๋ณ์๋ฅผ ์ฌ์ฉํ๋ ์ ๋ค๋ฆญ ํจ์๋ฅผ ์์ฑํ๋ค๊ณ ๊ฐ์ ํด ๋ด ์๋ค:
#![allow(unused)] fn main() { fn whatever<T: MyTrait>(t: T) { ... } }
rustc
์ LLVM๋ ์ ๋ค๋ฆญ ํจ์๊ฐ ํธ์ถ๋ ๋ ์ฌ์ฉ๋ ํ์
์ ํด๋นํ๋ ๋ฐ์ด๋๋ฆฌ ์ฝ๋๋ฅผ ๊ฐ๊ฐ ๋ฐ๋ก ์์ฑํฉ๋๋ค. ์ด๋ฐ ์ ๊ทผ์ ์ด๋ค T
ํ์
์ ์ปดํ์ผ๋ฌ๊ฐ ์ฒ๋ฆฌํ๋์ง์ ๋ฐ๋ผ ์ปดํ์ผ๋ฌ ์ต์ ํ์ ์ ์ฉํ ์๋ ์์ต๋๋ค. ํ์ง๋ง ๋์์ ์ฝ๋ ์ฌ์ด์ฆ๊ฐ ๋น ๋ฅด๊ฒ ๋์ด๋ ์๋ ์๋ค๋ ๋จ์ ๋ ๊ฐ์ง๊ณ ์์ต๋๋ค.
๋ค์๊ณผ ๊ฐ์ด ํ์ ๋งค๊ฐ๋ณ์ ๋์ ํธ๋ ์ดํธ ๊ฐ์ฒด๋ฅผ ์ฌ์ฉํด ๋ณด๊ฒ ์ต๋๋ค:
#![allow(unused)] fn main() { fn whatever(t: Box<MyTrait>) { ... } // or fn whatever(t: &MyTrait) { ... } // etc... }
์ด ์ฝ๋๋ฅผ ์ปดํ์ผํ ๋ ๊ฐ์ ํ๊ฒฝ์์ ํจ์๋ค์ ๋์ ๋์คํจ์น (dynamic dispatch) ๋ผ๋ ๋ฉ์ปค๋์ฆ์ผ๋ก ์ฒ๋ฆฌ๋ฅผ ํ๊ฒ ๋๋๋ฐ, ํ ๊ฐ์ง ๋ฒ์ ์ ํจ์๋ง .wasm
ํ์ผ๋ก ์ฒ๋ฆฌ๋๊ฒ ๋ฉ๋๋ค. ์ด๋ฌํ ์ฒ๋ฆฌ๋ฅผ ํ๋ฉด์ ์ปดํ์ผ๋ฌ ์ต์ ํ ์ธก๋ฉด์์ ์์ค์ด ์์ ์ ์๊ณ , ๊ฐ์ ์ ์ด๊ณ ๋์ ์ผ๋ก ๋์คํจ์น๋ ํจ์๋ฅผ ๋ถ๋ฅด๋๋ฐ ์ถ๊ฐ์ ์ธ ๋น์ฉ์ด ๋ค์๋ ์๋ค๋ ๋จ์ ์ด ์์ต๋๋ค.
wasm-snip
ํด์ ์ฌ์ฉํด ๋ณด์ธ์
wasm-snip
์ ์น์ด์
๋ธ๋ฆฌ ํจ์์ ์ฝ๋๋ฅผ unreachable
Assembly ๋ช
๋ น์ด๋ก ๊ต์ฒดํด ์ค๋๋ค. ํ์ง๋ง ์ ์๊ฐํด๋ณด๋ฉด ๋ชป ํ๋๋ฅผ ๋ฐ๋๋ค๊ณ ์์ฃผ ๊ฑฐ๋ํ ๋ง์น๋ฅผ ๊ฐ์ ธ์์ ์ด์ฌํ ๋๋๋ฆฌ๋ ๊ฒ ๊ฐ์ ๋๋์ด ์กฐ๊ธ์ฉ ๋ญ๋๋ค.
๋ฐํ์์์ ์ฌ์ฉํ์ง ์๋ ํจ์๋ค๊ณผ ์ด ํจ์๋ค์ด ๊ฐ์ ์ ์ผ๋ก ํธ์ถํ๋ ๋ค๋ฅธ ํจ์๋ค์ ์ ๊ฑฐํ๊ณ ์ถ์๋ฐ, ์ปดํ์ผ๋ฌ๊ฐ ์ด๋ค ํจ์๋ฅผ ์ฌ์ฉํ์ง ์๋์ง ๋ชจ๋ฅธ๋ค๋ฉด ์ด๋ป๊ฒ ํด์ผ ํ ๊น์? ์ฐ์ ์ ์ฝ๋๋ฅผ ๋น๋ํด๋ณด๊ณ wasm-opt
๋ฅผ --dce
ํ๋๊ทธ๋ฅผ ํฌํจํด์ ๋ค์ ์คํํด ๋ณด์ธ์! ๊ฐ์ ์ ์ผ๋ก ํธ์ถ๋๋ (๋ฐํ์์์ ๋ถ๋ฅด์ง ์๋) ํจ์๋ค๊น์ง ์ง์๋ฒ๋ฆด ์ ์์ต๋๋ค.
ํจ๋ ์ฝ๋๊ฐ ์ด๋ฐ ๋ฌธ์ ๋ค๋ก ์ข ์ข ์ด์ด์ง ์ ์๊ธฐ ๋๋ฌธ์, ํจ๋ ์ธํ๋ผ (panicking infrastructure) ๋ฅผ ์ง์ธ ๋ ์ด ํด์ด ์ ์ฉํ๊ฒ ์ฌ์ฉ๋ ์๋ ์์ต๋๋ค.
์๋ฐ์คํฌ๋ฆฝํธ ์ํธ ์ด์ฉํ๊ธฐ
์๋ฐ์คํฌ๋ฆฝํธ ํจ์ ์ํฌํธํ๊ณ ์ต์คํฌํธํ๊ธฐ
๋ฌ์คํธ ์ฌ์ด๋
์๋ฐ์คํฌ๋ฆฝํธ ํ๊ฒฝ์์ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๋ ๋ฌ์คํธ ์ฌ์ด๋์์ ํจ์๋ค์ ์ํฌํธํ๊ณ ์ต์คํฌํ ํ๋ ์์ ์ ์์ธ๋ก ๋งค์ฐ ๊ฐ๋จํฉ๋๋ค. ๊ทธ๋ฆฌ๊ณ C ์ธ์ด์ ์ ์ฌํ๊ฒ ์๋ํ๊ธฐ๋ ํฉ๋๋ค.
์น์ด์
๋ธ๋ฆฌ ๋ชจ๋์ ์ํฌํธ ์ํ์ค(sequence)๋ฅผ ์ ์ธํ๋๋ฐ, ๊ฐ๊ฐ ์ํ์ค๋ ๋ชจ๋ ์ด๋ฆ๊ณผ ์ํฌํธ ์ด๋ฆ์ผ๋ก ๊ตฌ์ฑ๋ฉ๋๋ค. ๊ธฐ๋ณธ๊ฐ์ผ๋ก "env" ํ์ผ์ ์ค์ ๋ ๊ฒ๊ณผ ๊ฐ์ด, #[link(wasm_import_module)]
๋ฅผ ์ฌ์ฉํ์ฌ extern { ... }
๋ธ๋ญ ๋ชจ๋ ์ด๋ฆ์ ์ง์ ์ง์ ํด ์ค ์๋ ์์ต๋๋ค.
์ต์คํฌํธ๋ ํ๊ฐ์ง ์ด๋ฆ๋ง ๊ฐ์ง ์ ์๋๋ฐ, ๋ค๋ฅธ extern
ํจ์๋ค๊ณผ ๋ง์ฐฌ๊ฐ์ง๋ก, ์น์ด์
๋ธ๋ฆฌ ์ธ์คํด์ค์ ์ ํ ๋ฉ๋ชจ๋ฆฌ๋ ๊ธฐ๋ณธ๊ฐ์ผ๋ก๋ "memory" ๋ผ๋ ์ด๋ฆ์ผ๋ก ์ต์คํฌํธ ๋ฉ๋๋ค.
#![allow(unused)] fn main() { // `foo`๋ผ๋ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์๋ฅผ `mod` ๋ชจ๋์์ ์ํฌํธํด์ต๋๋ค. #[link(wasm_import_module = "mod")] extern { fn foo(); } // `bar`๋ผ๋ ๋ฌ์คํธ ํจ์๋ฅผ ์ต์คํฌํธํฉ๋๋ค. #[no_mangle] pub extern fn bar() { /* ... */ } }
์น์ด์ ๋ธ๋ฆฌ๋ ์ ํ๋ ์์ ๊ฐ ํ์ ๋ค์ ๊ฐ์ง๊ณ ์๊ธฐ ๋๋ฌธ์, ์ด๋ฌํ ํจ์๋ค์ ์์ ์ซ์ ํ์ ์์๋ง ์๋ํด์ผ ํ๋ค๋ ๋ถ๋ถ๋ ์ฐธ๊ณ ํด ์ฃผ์ธ์.
์๋ฐ์คํฌ๋ฆฝํธ ์ฌ์ด๋
์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋ ๋ด์์๋ ์น์ด์ ๋ธ๋ฆฌ ๋ฐ์ด๋๋ฆฌ๊ฐ ES6 ๋ชจ๋๋ก ๋ณํ๋๋ ๊ฒ์ ํ์ธํ ์ ์์ต๋๋ค. ์ด ๋ชจ๋์ ์ ํ ๋ฉ๋ชจ๋ฆฌ์ ํจ๊ป ๋จผ์ ์ธ์คํด์คํ ๋ผ์ผํ๊ณ , ์ํฌํธํ ๋ด์ฉ๊ณผ ์ผ์นํ๋ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์ ๊ทธ๋ฃน์ ํ์๋ก ํฉ๋๋ค. ์ธ์คํด์คํ์ ๋ํด ๋ ์์ธํ ์์๋ณด๋ ค๋ฉด MDN๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์.
์ถ๋ ฅํ๊ฒ ๋๋ ES6 ๋ชจ๋์ ๋ฌ์คํธ์์ ์ต์คํฌํธํ ํจ์๋ค์ ๋ชจ๋ ํฌํจํ๊ฒ ๋๋๋ฐ, ์ด๋ฐ ํจ์๋ค์ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์์์ ํธ์ถํ ์ ์์ต๋๋ค.
์ ์ ์ ํ๊ณ ์คํ์ํค๋ ์ ๋ฐ์ ์ผ๋ก ๊ฐ๋จํ ์์๋ฅผ ํ์ธํ๊ณ ์ถ๋ค๋ฉด ์ฌ๊ธฐ๋ฅผ ์ฐธ๊ณ ํด ์ฃผ์ธ์.
์ซ์ ์ธ์ ๋ค๋ฅธ ๊ฐ๋ ์ฌ์ฉํด๋ณด๊ธฐ
์๋ฐ์คํฌ๋ฆฝํธ์์ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ฌ์ฉํ ๋๋ ์น์ด์ ๋ธ๋ฆฌ ๋ชจ๋์ ๋ฉ๋ชจ๋ฆฌ์ ์๋ฐ์คํฌ๋ฆฝํธ ๋ฉ๋ชจ๋ฆฌ ์ฌ์ด๊ฐ ๋ช ํํ๊ฒ ๊ตฌ๋ถ๋ฉ๋๋ค:
-
(์ด ๋ฌธ์ ์ต์๋จ์ ์ค๋ช ๋ ๋๋ก) ๊ฐ๊ฐ์ ์น์ด์ ๋ธ๋ฆฌ ๋ชจ๋์ ์ธ์คํด์คํ ๊ณผ์ ์์ ์์ฑํ๊ฒ ๋๋ ์ ํ ๋ฉ๋ชจ๋ฆฌ๋ฅผ ๊ฐ์ง๊ฒ ๋ฉ๋๋ค. ์๋ฐ์คํฌ๋ฆฝํธ ์ฝ๋๋ ์์ ๋กญ๊ฒ ์ด ๋ฉ๋ชจ๋ฆฌ๋ฅผ ์ฝ๊ณ ์ธ ์ ์์ต๋๋ค.
-
๋ฐ๋ฉด์, ์น์ด์ ๋ธ๋ฆฌ์์๋ ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด์ ์ง์ ์ ๊ทผํ ์ ์์ต๋๋ค.
๊ทธ๋ฐ ์ด์ ๋ก ๋ณดํต์ ๋ ๊ฐ์ง ๋ฐฉ๋ฒ์ผ๋ก ์ ๊ตํ๋ ์ํธ์์ฉ์ ์ฒ๋ฆฌํ๊ฒ ๋ฉ๋๋ค:
-
๋ฐ์ด๋๋ฆฌ ๋ฐ์ดํฐ๋ฅผ ์น์ด์ ๋ธ๋ฆฌ ๋ฉ๋ชจ๋ฆฌ์์ ๋ณต์ฌํ๊ฑฐ๋ ๋ถ์ฌ๋์ต๋๋ค. ์๋ฅผ ๋ค์ด์, ์ด ๋ฐฉ๋ฒ์ผ๋ก ๋ฌ์คํธ ์ฝ๋๊ฐ ์์ ํ๋
String
๊ฐ์ ์ฌ์ฉํ ์๋ ์์ต๋๋ค. -
๋ช ์์ ์ผ๋ก ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด์ ํ์ ์ค์ ํ๊ณ ์ดํ์ "addresses"๋ฅผ ํ ๋นํฉ๋๋ค. ์ด๋ ๊ฒ ์น์ด์ ๋ธ๋ฆฌ ์ฝ๋์์ (์ ์ ๊ฐ์ ์ฌ์ฉํ์ฌ) ๊ฐ์ ์ ์ผ๋ก ์๋ฐ์คํฌ๋ฆฝํธ ๊ฐ์ฒด๋ฅผ ์ฐธ์กฐํ ์ ์๊ณ , ์ํฌํธํ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์๋ฅผ ํธ์ถํด์ ์ฌ์ฉํ ์ ์๊ฒ ๋ฉ๋๋ค.
๋คํ์ค๋ฝ๊ฒ๋ ์ด ์ํธ ์ด์ฉ ์์
์ wasm-bindgen์ด๋ผ๋ bindgen
์คํ์ผ์ ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํด์ ์์ฃผ ์ฝ๊ฒ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. ์ด ํ๋ ์์ํฌ๋ฅผ ์ฌ์ฉํด์ ๊ด์ฉ์ ์ธ ์คํ์ผ์ ์๋ฐ์คํฌ๋ฆฝํธ ํจ์์ ์๋์ผ๋ก ๋งคํ๋๋๋ก ๊ด์ฉ์ ์ธ ๋ฌ์คํธ ํจ์ ์๊ทธ๋์ฒ๋ฅผ ์์ฑํ ์ ์์ต๋๋ค.
์ฌ์ฉ์ ์ ์ ์น์ (Custom Sections)
์ฌ์ฉ์ ์ ์ ์น์ ์ ํตํด ์น์ด์ ๋ธ๋ฆฌ ๋ชจ๋์ ๋ช ๋ช ๋ ์์์ ๋ฐ์ดํฐ๋ฅผ ์๋ฒ ๋ฉํ ์ ์์ต๋๋ค. ์ด ์น์ ๋ฐ์ดํฐ๋ ์ปดํ์ผ ์์ ์ ์ค์ ๋๋ฉฐ ์น์ด์ ๋ธ๋ฆฌ ๋ชจ๋์์ ์ง์ ์ฝ์ ์ ์์ง๋ง ๋ฐํ์์์๋ ์ด ๋ชจ๋์ ์์ ํ ์ ์์ต๋๋ค.
๋ฌ์คํธ ์ฝ๋์์ ๋ค์๊ณผ ๊ฐ์ด ์ฌ์ฉ์ ์ ์ ์น์
์ ์ ์ ๋ฐฐ์ด ([T; size]
)๋ก ๋ํ๋ด๊ณ #[link_section]
์์ฑ์ผ๋ก ๋
ธ์ถ์ํฌ ์ ์์ต๋๋ค:
#![allow(unused)] fn main() { #[link_section = "hello"] pub static SECTION: [u8; 24] = *b"This is a custom section"; }
์ด ์ฝ๋๋ wasm ํ์ผ์ hello
๋ผ๋ ์ฌ์ฉ์ ์ ์ ์น์
์ ์ถ๊ฐํฉ๋๋ค. ๋ณ์ ์ด๋ฆ SECTION
์ ์์๋ก ๋ช
๋ช
๋์ผ๋ฉฐ ์ด ์ด๋ฆ์ ๋ณ๊ฒฝํด๋ ์ฝ๋์ ๋์์ด ๋ฐ๋์ง ์์ต๋๋ค. ์ด ๋ณ์์ ํ
์คํธ ๋ฐ์ดํธ (bytes of text) ๋ฅผ ์ฌ์ฉํ์ง๋ง ๋ค๋ฅธ ์์์ ๋ฐ์ดํฐ๋ฅผ ์ฌ์ฉํ ์๋ ์์ต๋๋ค.
์ด๋ฐ ์ฌ์ฉ์ ์ ์ ์น์
์ JS ์ฌ์ด๋์์ WebAssembly.Module.customSections
ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์ฝ์ ์๋ ์์ต๋๋ค. ์ด ํจ์๋ฅผ ํธ์ถํ ๋, wasm ๋ชจ๋๊ณผ ์น์
์ด๋ฆ์ ์ธ์๋ก ์ฃผ๊ณ ArrayBuffer
์ ๋ฐฐ์ด์ ๋ฐํ๋ฐ๊ฒ ๋ฉ๋๋ค. ์ฌ๋ฌ ์น์
๋ค์ด ๊ฐ์ ์ด๋ฆ์ ๊ณต์ ํ๊ฒ ํ ์๋ ์๋๋ฐ, ์ด ๊ฒฝ์ฐ์๋ ํ ๋ฐฐ์ด์ ์ฌ๋ฌ ์น์
๋ค์ ๋ด๊ฒ ๋ฉ๋๋ค.
WebAssembly.compileStreaming(fetch("sections.wasm"))
.then(mod => {
const sections = WebAssembly.Module.customSections(mod, "hello");
const decoder = new TextDecoder();
const text = decoder.decode(sections[0]);
console.log(text); // -> "์ฌ์ฉ์ ์ ์ ์น์
์ ์ฝ์์ ์ถ๋ ฅํฉ๋๋ค"
});
์ด๋ค ํฌ๋ ์ดํธ๋ค์ ์น์ด์ ๋ธ๋ฆฌ์์ ๋ฐ๋ก ์ฌ์ฉํ ์ ์๋์?
๊ฐ์ฅ ๊ฐ๋จํ๊ฒ ์ด๋ค ๊ธฐ๋ฅ๋ค์ด ์น์ด์
๋ธ๋ฆฌ ํ๊ฒฝ์์ ์๋ํ์ง ์๋์ง ๋ชฉ๋ก์ ํ๋ฒ ๋ง๋ค์ด ๋ณด๊ฒ ์ต๋๋ค. ๋ค์ ๋ด์ฉ์ ํด๋นํ์ง ์๋ ํฌ๋ ์ดํธ๋ค์ ํด๋์ฑ์ด ์ข๊ณ ์น์ด์
๋ธ๋ฆฌ ํ๊ฒฝ์์ ์ ์๋ํ๋ค๊ณ ์๊ฐํด๋ ์ข๊ณ , ์๋ฒ ๋๋ ์์คํ
๊ณผ #![no_std]
๋ฅผ ์ง์ํ๋ ํฌ๋ ์ดํธ๋ค๋ ๋ณดํต์ ์น์ด์
๋ธ๋ฆฌ๋ ์ง์ํ๋ค๊ณ ๋ณผ์ ์์ต๋๋ค.
์ด๋ฌํ ํฌ๋ ์ดํธ๋ค์ ์น์ด์ ๋ธ๋ฆฌ ํ๊ฒฝ์์ ์๋ํ์ง ์์ ์๋ ์์ต๋๋ค
C ์ธ์ด์ ์์คํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ข ์์ฑ์ด ์๋ ๊ฒฝ์ฐ
์น์ด์ ๋ธ๋ฆฌ์๋ ์์คํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ ์๊ธฐ ๋๋ฌธ์ ์์คํ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ ๋ฐ์ธ๋ฉ๋ผ ์๋ ์ฝ๋๊ฐ ์๋ค๋ฉด ์๋ํ์ง ์์ต๋๋ค.
์น์ด์
๋ธ๋ฆฌ์๋ ์ธ์ด ๊ฐ ํต์ ์ ์ฌ์ฉํ ์ ์๋ ์์ ๋ ๋ฒ์ ์ ABI์ ์ธ์ด ๊ฐ ๋งํน(linking)์ด ์กด์ฌํ์ง ์์ต๋๋ค. ๊ทธ๋ฌ๋ฏ๋ก C ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ๋ ํฌ๋ ์ดํธ๋ ์๋ํ์ง ์์ต๋๋ค. ํนํ clang
์ปดํ์ผ๋ฌ๊ฐ wasm32
ํ๊ฒ์ ๊ธฐ๋ณธ์ผ๋ก ์ ๊ณตํ๊ธฐ ๋๋ฌธ์ ๊ฒฐ๊ตญ์ ์๋ํ ๊ฒ์ผ๋ก ์์๋์ง๋ง, ํ์ฌ๋ก์๋ ์๋ฒฝํ์ง ์์ต๋๋ค.
ํ์ผ I/O
์น์ด์ ๋ธ๋ฆฌ๋ ํ์ผ ์์คํ ์ ์ ๊ทผํ ์ ์์ต๋๋ค. โ ํ์ผ ์์คํ ์ ํ์๋ก ํ๊ณ ์น์ด์ ๋ธ๋ฆฌ์์ ์ฌ์ฉํ ์ ์๋๋ก ์ฝ๋๊ฐ ๋ง๋ จ๋์ง ์์ ํฌ๋ ์ดํธ๋ ์๋ํ์ง ์์ต๋๋ค.
์ค๋ ๋ ์์ฑ
์ค๋ ๋ฉ์ ์น์ด์
๋ธ๋ฆฌ์ ์ง์ํ๋ ๊ณํ์ด ์๊ธด ํ์ง๋ง ์์ง ์ค๋น๊ฐ ์๋ฒฝํ ๋์ง ์์์ต๋๋ค. wasm32-unknown-unknown
ํ๊ฒ์์ ์ค๋ ๋๋ฅผ ์์ฑํ๋ ค๊ณ ์๋ํ๋ฉด wasm ํธ๋ฉ (wasm trap) ์ด ๋ฐ์ํ๋ฉด์ ์ฝ๋๊ฐ ํจ๋ํ๊ฒ ๋ฉ๋๋ค.
์ด๋ค ๋ค๋ชฉ์ ํฌ๋ ์ดํธ๋ค์ด ์น์ด์ ๋ธ๋ฆฌ์์ ๋ฐ๋ก ์๋ํ๋ ํธ์ธ๊ฐ์?
์๊ณ ๋ฆฌ์ฆ๊ณผ ์๋ฃ ๊ตฌ์กฐ
A* ์๊ณ ๋ฆฌ์ฆ๋ splay trees์ฒ๋ผ, ํน์ ํ ์๊ณ ๋ฆฌ์ฆ์ด๋ data structure์ ๊ตฌํ์ ์ ๊ณตํ๋ ํฌ๋ ์ดํธ๋ค์ ์น์ด์ ๋ธ๋ฆฌ์ ์ ์๋ํ๋ ํธ์ ๋๋ค.
#![no_std]
๋ฌ์คํธ ์คํ ๋ค๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ์ฌ์ฉํ์ง ์๋ ํฌ๋ ์ดํธ๋ค๋ ์น์ด์ ๋ธ๋ฆฌ์ ์ ์๋ํ๋ ํธ์ ๋๋ค.
ํ์ (Parser)
์ ๋ ฅ์ ๋ฐ๊ณ I/O ์์ ์ ์ํํ์ง ์๋ ์ด์, ํ์๋ค์ ๋ณดํต ์น์ด์ ๋ธ๋ฆฌ์ ์ ์๋ํ๋ ํธ์ ๋๋ค.
ํ ์คํธ ์ฒ๋ฆฌ
์ธ๊ฐ ์ธ์ด๋ฅผ ํ ์คํธ ํํ๋ก ๋ํ๋์ ๋ ๋ฐ์ํ๋ ๋ณต์ก์ฑ์ ๋ค๋ฃจ๋ ํฌ๋ ์ดํธ๋ค์ ์น์ด์ ๋ธ๋ฆฌ์ ์ ์๋ํ๋ ํธ์ ๋๋ค.
๋ฌ์คํธ ํจํด
Rust ํ๋ก๊ทธ๋๋ฐ์ ํน์ ํ ์ํฉ์ ์ฐ๋๋ก ๊ณต์ ๋๋ ํด๊ฒฐ์ฑ ๋ค์ ์น์ด์ ๋ธ๋ฆฌ์ ์ ์๋ํ๋ ํธ์ ๋๋ค.
๋ค๋ชฉ์ ํฌ๋ ์ดํธ๊ฐ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ง์ํ๋๋ก ์ฝ๋ ์์ ํ๊ธฐ
์ด ์น์ ์ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ง์ํ๋ ๋ฐ์ ๊ด์ฌ์ด ์๋ ๋ค๋ชฉ์ ํฌ๋ ์ดํธ ์ ์๋ค์ ์ํด ์์ฑ๋์ต๋๋ค.
ํฌ๋ ์ดํธ๊ฐ ์ด๋ฏธ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ง์ํ ์๋ ์์ด์!
์ฐ์ ์ด๋ค ์์ ์ ํ๋ ๋ค๋ชฉ์ ํฌ๋ ์ดํธ๊ฐ ์น์ด์ ๋ธ๋ฆฌ์ ์ ํฉํ์ง ์๋์? ์ ๋ด์ฉ์ ๋จผ์ ์ฝ์ด๋ณด์ธ์. ์์ฑํ๋ ํฌ๋ ์ดํธ๊ฐ ํด๋น ์ฌํญ์ด ์๋ค๋ฉด, ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ง์ํ ํ๋ฅ ์ด ๋์ต๋๋ค.
์ถ๊ฐ๋ก, ์ธ์ ๋ cargo build
๋ช
๋ น์ด๋ฅผ ์
๋ ฅํด์ ์น์ด์
๋ธ๋ฆฌ ํ๊ฒ์ ์ง์ํ๋์ง ํ์ธํด ๋ณผ ์ ์์ต๋๋ค:
cargo build --target wasm32-unknown-unknown
๋ช ๋ น์ด ์คํ์ด ์คํจํ๋ค๋ฉด ํ์ฌ๋ก์๋ ํฌ๋ ์ดํธ๊ฐ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ง์ํ์ง ์๋๋ค๋ ์๋ฏธ์ ๋๋ค. ํ์ง๋ง ์ด ๋ช ๋ น์ด๊ฐ ์ฑ๊ณตํ๋๋ผ๋ ํฌ๋ ์ดํธ๊ฐ ๊ผญ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์ง์ํ๋ค๋ ์๋ฏธ๋ ์๋๋๋ค. ๋ ํ์คํ๊ฒ ํ์ธํ๊ณ ์ถ๋ค๋ฉด ์น์ด์ ๋ธ๋ฆฌ ํ ์คํ ์ฝ๋๋ฅผ ์ถ๊ฐํ๊ณ ์ง์์ฑ ํตํฉ (continuous integration) ํ๊ฒฝ์์ ํ ์คํธ๋ฅผ ์คํํด๋ณด๊ธฐ ๋ฅผ ์ฝ์ด๋ณด์ธ์.
์น์ด์ ๋ธ๋ฆฌ ์ง์ ์ถ๊ฐํ๊ธฐ
๋ฐ๋ก I/O๋ฅผ ์ํํ์ง ๋ง์์ฃผ์ธ์
์น ํ๊ฒฝ์์ I/O๋ ํญ์ ๋น๋๊ธฐ์ ์ผ ๋ฟ ์๋๋ผ ํ์ผ ์์คํ ๋ ์กด์ฌํ์ง ์์ต๋๋ค. ๋ผ์ด๋ธ๋ฌ๋ฆฌ์ I/O๋ฅผ ๋ถ๋ฆฌํ๊ณ ์ ์ ๋ค์ด I/O๋ฅผ ์ง์ ์ํํ๊ณ ์ฌ๋ผ์ด์ค๋ฅผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ก ๋์ ์ ๋ ฅํ ์ ์๋๋ก ์์ ํด ๋ด ์๋ค.
์๋ฅผ ๋ค์ด์, ์ด ์ฝ๋๋ฅผ ๋ฆฌํฉํ ๋งํด ์ฃผ์ธ์:
#![allow(unused)] fn main() { use std::fs; use std::path::Path; pub fn parse_thing(path: &Path) -> Result<MyThing, MyError> { let contents = fs::read(path)?; // ... } }
๋ค์์ ๋ฆฌํฉํ ๋ง ๋ ์ฝ๋์ ๋๋ค:
#![allow(unused)] fn main() { pub fn parse_thing(contents: &[u8]) -> Result<MyThing, MyError> { // ... } }
wasm-bindgen
์ ์ข
์์ฑ์ผ๋ก ์ถ๊ฐํ๊ธฐ
(์๋ฅผ ๋ค์ด์, ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ์๊ฐ ์ํธ์์ฉ์ ์ง์ ์ปจํธ๋กคํ๋๋ก ํ์ฉํ๋ฉด ์ ๋๋ ๊ฒฝ์ฐ์ฒ๋ผ) ์ธ๋ถ ํ๊ฒฝ๊ณผ ๋ฐ๋ก ์ํธ์์ฉ์ ํด์ผ ํ๋ ๊ฒฝ์ฐ, (ํ์ํ๋ค๋ฉด js-sys
์ web-sys
์ ํจ๊ป) wasm-bindgen
์ ์ปดํ์ผ, ํ๊ฒํ
์ข
์์ฑ์ผ๋ก ์ถ๊ฐํด์ผ ํฉ๋๋ค:
[target.'cfg(target_arch = "wasm32")'.dependencies]
wasm-bindgen = "0.2"
js-sys = "0.3"
web-sys = "0.3"
๋๊ธฐ์ I/O๋ฅผ ํผํด์ฃผ์ธ์
์น ํ๊ฒฝ์์๋ ๋น๋๊ธฐ์ I/O๋ง ์ํํ ์ ์์ผ๋ฏ๋ก ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ I/O ์์
์ ์ํํ ๋๋ ๋๊ธฐ์ ์ผ๋ก๋ ์ฒ๋ฆฌํ ์ ์์ต๋๋ค. futures
ํฌ๋ ์ดํธ ์ wasm-bindgen-futures
ํฌ๋ ์ดํธ๋ฅผ ์ฌ์ฉํด์ ๋น๋๊ธฐ I/O๋ฅผ ๊ด๋ฆฌํด ๋ณด์ธ์. ๋ผ์ด๋ธ๋ฌ๋ฆฌ๊ฐ Future ํ์
F
๋ฅผ ์ ๋ค๋ฆญ ํ์
์ผ๋ก ์ฌ์ฉํ๋ค๋ฉด, ์น ํ๊ฒฝ์ fetch
๋ ์ด์์ฒด์ ์์ ์ ๊ณตํ๋ ๋
ผ๋ธ๋กํน (non-blocking) I/O๋ก ์ฝ๋๋ฅผ ๊ตฌํํด ๋ณผ ์ ์์ต๋๋ค.
#![allow(unused)] fn main() { pub fn do_stuff<F>(future: F) -> impl Future<Item = MyOtherThing> where F: Future<Item = MyThing>, { // ... } }
ํธ๋ ์ดํธ๋ฅผ ์ ์ํ๊ณ ์น์ด์ ๋ธ๋ฆฌ์ ์น, ๋ค์ดํฐ๋ธ ํ๊ฒ์์ ์คํํ ์ ์๋๋ก ๊ตฌํํด ๋ณผ ์๋ ์์ต๋๋ค:
#![allow(unused)] fn main() { trait ReadMyThing { type F: Future<Item = MyThing>; fn read(&self) -> Self::F; } #[cfg(target_arch = "wasm32")] struct WebReadMyThing { // ... } #[cfg(target_arch = "wasm32")] impl ReadMyThing for WebReadMyThing { // ... } #[cfg(not(target_arch = "wasm32"))] struct NativeReadMyThing { // ... } #[cfg(not(target_arch = "wasm32"))] impl ReadMyThing for NativeReadMyThing { // ... } }
์ค๋ ๋ ์์ฑ์ ํผํด์ฃผ์ธ์
์น์ด์ ๋ธ๋ฆฌ๋ ์์ง ์ค๋ ๋๋ฅผ ์ง์ํ์ง ์์ต๋๋ค. (ํ์ง๋ง ์คํ์ ์ผ๋ก ์ง์ ์์ ์ด ์งํ๋๊ณ ์๊ธด ํฉ๋๋ค.) ๊ทธ๋ฌ๋ฏ๋ก, ์น์ด์ ๋ธ๋ฆฌ์์ ์ค๋ ๋๋ฅผ ์์ฑํ๋ ค๊ณ ์๋ํ๋ฉด ์ฝ๋๊ฐ ํจ๋ํ๊ฒ ๋ฉ๋๋ค.
#[cfg(..)]
๋ฅผ ์ฌ์ฉํ์ฌ ํ๊ฒ์ด ์น์ด์
๋ธ๋ฆฌ์ธ์ง ์๋์ง ์ฌ๋ถ์ ๋ฐ๋ผ ์ค๋ ๋์ ๋น์ค๋ ๋ ์ฝ๋๋ฅผ ๋ฐ๋ก ์์ฑํด ๋ณผ ์๋ ์์ต๋๋ค:
#![allow(unused)] #![cfg(target_arch = "wasm32")] fn main() { fn do_work() { // ์ด ์ค๋ ๋์์๋ง ์์ ์ ์ํํฉ๋๋ค... } #![cfg(not(target_arch = "wasm32"))] fn do_work() { use std::thread; // ํฌํผ ์ค๋ ๋๋ฅผ ์ฌ์ฉํด์ ์์ ์ ํ์ฅํฉ๋๋ค... thread::spawn(|| { // ... }); } }
ํ์ผ I/O๋ฅผ ์ ๊ฑฐํ๊ณ ์ ์ ๋ค์ด I/O ์ฝ๋๋ฅผ ๋ฐ๋ก ๊ฐ์ ธ์ฌ ์ ์๋๋ก ํ์ฉํ๋ ์ ๊ทผ ๋ฐฉ์๊ณผ ์ ์ฌํ๊ฒ, ์ค๋ ๋ ์์ฑ ์ฝ๋๋ฅผ ๋ผ์ด๋ธ๋ฌ๋ฆฌ์์ ์ ์ธ์ํจ ๋ค์ ์ ์ ๋ค์ด ์ค๋ ๋ ์ฝ๋๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ๋ฅผ ๋ฐ๋ก ๊ฐ์ ธ์ฌ ์ ์๋๋ก ๋ณ๊ฒฝํด ๋ณผ ์๋ ์์ต๋๋ค. ์ด๋ ๊ฒ ๊ตฌํํ์ ๋ ๋ผ์ด๋ธ๋ฌ๋ฆฌ ์ฌ์ฉ์๋ค์ด ์ง์ ์ค๋ ๋ ํ ์ฝ๋๋ฅผ ๋ง๋ จํ ์ ์๊ฒ ๋๋ ๊ธ์ ์ ์ธ ๋ถ์ ํจ๊ณผ๋ฅผ ๋ณด๊ฒ ๋ฉ๋๋ค.
์น์ด์ ๋ธ๋ฆฌ์ ์ง์์ ์ธ ์ง์ ์ ์งํ๊ธฐ
์ง์์ฑ ํตํฉ (CI) ํ๊ฒฝ์ ์ฌ์ฉํด์ wasm32-unknown-unknown
ํ๊ฒ์ผ๋ก ๋น๋ํ๊ธฐ
์น์ด์ ๋ธ๋ฆฌ๋ฅผ ํ๊ฒ์ผ๋ก ํ ๋, ๋ค์ ๋ช ๋ น์ด๋ฅผ CI ์คํฌ๋ฆฝํธ์ ํฌํจ์์ผ์ ์ปดํ์ผ์ด ์คํจํ์ง ์๋ ์ด์ ๋ฅผ ๋ ์์ธํ๊ฒ ํ์ธํ ์ ์์ต๋๋ค:
rustup target add wasm32-unknown-unknown
cargo check --target wasm32-unknown-unknown
์๋ฅผ ๋ค์ด์, Travis CI ์ค์ ํ์ผ์ธ .travis.yml
์ ๋ค์ ๋ด์ฉ์ ์ถ๊ฐํด ๋ณด๊ฒ ์ต๋๋ค:
matrix:
include:
- language: rust
rust: stable
name: "check wasm32 support"
install: rustup target add wasm32-unknown-unknown
script: cargo check --target wasm32-unknown-unknown
Node.js์ ํค๋๋ฆฌ์ค ๋ธ๋ผ์ฐ์ (Headless Browsers) ์์ ํ ์คํ ํ๊ธฐ
wasm-bindgen-test
์ wasm-pack-test
ํ์ ๋ช
๋ น์ด (subcommand) ๋ฅผ ์ฌ์ฉํด์ wasm ํ
์คํธ๋ค Node.js๋ ํค๋๋ฆฌ์ค ๋ธ๋ผ์ฐ์ ์์ ์คํํด๋ณด์ธ์. ์ด๋ฌํ ํ
์คํธ๋ค์ CI์ ํตํฉ์์ผ ๋ณผ ์๋ ์์ต๋๋ค.
์ฌ๊ธฐ์ ์น์ด์ ๋ธ๋ฆฌ ํ ์คํ ์ ๋ํด ๋ ์์๋ณด์ธ์.
๋ฌ์คํธ๋ก ์์ฑํ WebAssembly ์ฝ๋๋ฅผ ํ๋ก๋์ ํ๊ฒฝ์ ๋ฐฐํฌํ๊ธฐ
โก ์ฌ์ค ๋ฌ์คํธ, ์น์ด์ ๋ธ๋ฆฌ๋ก ์น ์ฑ์ ์์ฑํ๋๋ผ๋ ๋ฐฐํฌ ๊ณผ์ ์ด ๋ค๋ฅธ ์น ์ฑ์ ๋ฐฐํฌํ๋ ๊ณผ์ ๊ณผ ๊ฑฐ์ ๋์ผํฉ๋๋ค!
๋ฌ์คํธ๋ก ์์ฑํ ์น์ด์ ๋ธ๋ฆฌ๋ฅผ ์คํํ๋ ์น ์ฑ์ ๋ฐฐํฌํด ๋ณด๊ฒ ์ต๋๋ค. ๋น๋๋ ์น ์ฑ์ ํ๋ก๋์ ์๋ฒ์ ํ์ผ ์์คํ ์ผ๋ก ๋ณต์ฌํ๊ณ , ๋ณต์ฌํ ํ์ผ์ ์ ๊ทผํ ์ ์๋๋ก HTTP ์๋ฒ๋ฅผ ์ค์ ํด ์ฃผ์ธ์.
HTTP ์๋ฒ๊ฐ application/wasm
MIME ํ์
์ ์ง์ํ๋์ง ํ์ธํด์ฃผ์ธ์
ํ์ด์ง๊ฐ ๋น ๋ฅด๊ฒ ๋ก๋๋ ์ ์๋๋ก, ๋คํธ์ํฌ ์ ์ก์ ํตํด WebAssembly.instantiateStreaming
ํจ์๋ฅผ ์ฌ์ฉํ์ฌ ์น์ด์
๋ธ๋ฆฌ ์ปดํ์ผ๊ณผ ์ธ์คํด์คํ ๊ณผ์ ์ ํ์ดํ๋ผ์ธ ์ฒ๋ฆฌํด์ผ ํฉ๋๋ค. (๋ฒ๋ค๋ฌ๊ฐ ์ด ํจ์๋ฅผ ์ฌ์ฉํ ์ ์๋์ง๋ ํ์ธํด์ฃผ์ธ์.) ํ์ง๋ง instantiateStreaming
๋ฅผ ์คํํ ๋ HTTP ์๋ต์ด application/wasm
MIME ํ์
์ ํ์ ๊ฐ์ง๊ณ ์์ง ์๋ค๋ฉด ์ค๋ฅ๊ฐ ๋ฐ์ํ๊ฒ ๋๋ ์ด ๋ถ๋ถ๋ ์ ํ์ธํด ์ฃผ์ธ์.
์ถ๊ฐ ์๋ฃ
- ํ๋ก๋์ ๊ฐ๋ฐ ํ๊ฒฝ์์ webpack์ ์ฌ์ฉํ๋ ๋ชจ๋ฒ ์ฌ๋ก. ๋ง์ ๋ฌ์คํธ์ ์น์ด์ ๋ธ๋ฆฌ ํ๋ก์ ํธ๋ค์ ๋ฌ์คํธ๋ก ์์ฑํ ์น์ด์ ๋ธ๋ฆฌ์ ์๋ฐ์คํฌ๋ฆฝํธ, CSS, HTML ์ฝ๋๋ฅผ ๋ฒ๋ค๋ง ํ๊ธฐ ์ํด webpack์ ์ฌ์ฉํฉ๋๋ค. ์ด ๊ฐ์ด๋๋ ํ๋ก๋์ ํ๊ฒฝ์ ๋ฐฐํฌํ ๋ ์ด๋ป๊ฒ Webpack์ ๊ฐ์ฅ ์ ํ์ฉํ ์ ์๋์ง ์๋ ค์ฃผ๋ ์ฌ๋ฌ ์ ์ฉํ ์ ๋ณด๋ค์ ํฌํจํฉ๋๋ค.
- Apache ๋ฌธ์. Apache๋ ํ๋ก๋์ ํ๊ฒฝ์์ ๋ง์ด ์ฌ์ฉ๋๋ HTTP ์๋ฒ์ ๋๋ค.
- NGINX ๋ฌธ์. NGNIX๋ ํ๋ก๋์ ํ๊ฒฝ์์ ๋ง์ด ์ฌ์ฉ๋๋ HTTP ์๋ฒ์ ๋๋ค.
๋ฒ์ญ๋ณธ
์ด ์ฑ ์ ๋ค๋ฅธ ์ธ์ด๋ก ๋ฒ์ญํ๋ ๋ฐ์ ๊ด์ฌ์ด ์๋ค๋ฉด ๋ฉ์ธ ๋ ํฌ์งํ ๋ฆฌ๋ฅผ ํฌํฌํ๊ณ ํ ๋ฆฌํ์คํธ๋ฅผ ์์ฑํด๋ณด์ธ์.