LYnLab

소개블로그취미로그

게시물의 썸네일 이미지

Ruby on WebAssembly: 살짝 맛보기

Ruby 3.2에 추가된 WebAssembly 지원을 간단하게 테스트해봅시다.

2023-01-08#프로그래밍#Ruby#WebAssembly

💡 이 글은 작성된지 1년 이상 지났습니다. 정보글의 경우 최신 내용이 아닐 수 있음에 유의해주세요.

2022년 크리스마스에 정식 배포된 Ruby 3.2에는 WebAssembly 지원이 추가되었습니다. WebAssembly는 원래 JS 이외의 언어로 작성된 프로그램을 웹 브라우저에서 실행하기 위해 개발이 시작되었으나, 최근에는 브라우저를 벗어나서 모든 환경에서 안전하고 높은 성능으로 구동하는 기술로 주목받고 있습니다.

이 글에서는 Ruby로 작성한 간단한 코드를 WebAssembly로 포팅해보고, 제약 사항에 대해 간단히 알아보겠습니다.

간단한 사전 지식

용어 정리

비슷한 용어가 나오다보니 다소 헷갈릴 수 있습니다. 주요 사전 지식들을 가볍게 짚고 넘어가보겠습니다.

  • WASI : WASI(WebAssembly System Interface)는 WebAssembly로 작성된 프로그램을 브라우저가 아닌 시스템에서 구동하기 위한 인터페이스입니다.
  • wasmtime : 브라우저가 아닌 곳에서도 WebAssembly를 실행할 수 있도록 하는 런타임 중 하나입니다. WASI 표준을 지원합니다.
  • wasi-vfs : WASI의 가상 파일 시스템입니다. Ruby로 작성된 코드를 간단하게 WebAssembly 런타임에서 작동하도록 패키징, 릴리즈 할 수 있도록 해줍니다.
  • ruby.wasm : CRuby를 WebAssembly 런타임에서 작동하도록 포팅한 것입니다. 직접 빌드하여 사용할 수도 있지만, 이 글에서는 사전에 빌드된 바이너리를 이용할 것입니다.

WebAssembly에서 루비 코드 돌리기

아래의 루비 코드를 WASM으로 패키징해봅시다.

# src/hello.rb
3.times do
  puts "Hello, world!"
  sleep 1
end

우선 사전에 빌드된 ruby.wasm을 내려받습니다. 이 파일에는 WASM 런타임에서 동작하는 ruby 바이너리가 들어있습니다.

$ curl -L https://github.com/ruby/ruby.wasm/releases/latest/download/ruby-head-wasm32-unknown-wasi-full.tar.gz -o ruby-wasm.tar.gz
$ tar -zxf ruby-wasm.tar.gz
$ mv head-wasm32-unknown-wasi-full/usr/local/bin/ruby ruby.wasm

이어서 wasi-vfs를 이용해 WASM 모듈로 패키징합니다.

$ mkdir -p build
$ wasi-vfs pack ruby.wasm \
    --mapdir /src::./src  \
    --mapdir /usr::./head-wasm32-unknown-wasi-full/usr \
    -o build/app.wasm

마지막으로 패키징된 WASM 파일을 실행해봅시다.

$ wasmtime run build/app.wasm -- src/hello.rb
Hello, world!
Hello, world!
Hello, world!

루비 코드가 정상적으로 실행되었습니다!

기술적 제약

성능 비교

JSON 문자열을 객체로 파싱하고, 다시 문자열로 바꾸는 간단한 코드를 벤치마크 해봅시다.

# src/benchmark.rb
require "benchmark"
require "json"

'{"키": "값"}' => json_text

Benchmark.bm do |benchmark|
  benchmark.report do
    10_000.times do
      JSON.parse(json_text) => json_obj
      JSON.generate(json_obj)
    end
  end
end

아쉽지만 결과는 아직은 제법 성능 차이가 발생하고 있음을 보여주었습니다.

$ ruby src/benchmark.rb
       user     system      total        real
   0.010922   0.000186   0.011108 (  0.011108)

$ wasmtime run build/app.wasm -- src/benchmark.rb
       user     system      total        real
   0.084124   0.000000   0.084124 (  0.084120)

불완전한 구현

WebAssembly와 WASI는 아직 성장중인 기술인 만큼 여러가지 불완전한 부분이 있습니다. 대표적으로 컨텍스트 스위칭 기능이 없어 비동기 작업을 수행할 수 없습니다. 예를 들어, 스레드를 생성하는 코드를 이용한다면 아래와 같은 오류가 발생합니다.

$ wasmtime run build/app.wasm -- src/thread.rb
src/thread.rb:2:in `initialize': initialize() function is unimplemented on this machine (NotImplementedError)
        from src/thread.rb:2:in `new'
        from src/thread.rb:2:in `block in <main>'
        from src/thread.rb:1:in `each'
        from src/thread.rb:1:in `<main>'

아직은 초기 단계의 기술인 만큼 당장 이것을 어딘가에 적용한다기 보다는, 시대의 흐름에 탑승하기 위한 첫 걸음이라 생각한다면 좋을 것 같습니다.

관련된 글

Rails와 GitHub Actions에 커버리지 레포트를 달아보자

이 블로그의 CMS이기도 한 Shiori를 대폭 리팩토링하면서 테스트가 얼마나 잘 작성되어있는지 궁금해졌습니다.

Rails Global ID로 전역 객체 식별하기

Global ID는 Rails의 모든 객체를 식별할 수 있는 URI(Uniform Resource Identifier)입니다.

Ruby의 and와 &&는 다르다

Ruby로 프로그램을 짜다보면 반드시 밟게되는 지뢰가 있습니다. 바로 or 과 ||, 혹은 and 와 && 연산자의 우선순위가 달라서 발생하는 일입니다.

작성한 댓글은 giscus를 통해 GitHub Discussion에 저장됩니다.

크리에이티브 커먼즈 라이선스크리에이티브 커먼즈 저작자표시크리에이티브 커먼즈 동일조건변경허락

본 사이트의 저작물은 별도의 언급이 없는 한 크리에이티브 커먼즈 저작자표시-동일조건변경허락 4.0 국제 라이선스에 따라 이용할 수 있습니다.

© 2011 - 2024 Hoerin Doh, All rights reserved.

LYnLab 로고About MeGitHubTwitterInstagram