기본 콘텐츠로 건너뛰기

Rust를 Webassembly로 컴파일 하기

Rust를 Webassembly로 컴파일 하는 방법과 방법을 정리 해 둔다.

환경 구축이라 순차적으로 따라하면 될것 같지만, 막상 해 보면 시간도 오래 걸리고 잘 안된다. 컴파일 환경 구축 부터 Cargo 로 빌드하는 방법 까지 정리 해 둔다.
OS 선택 Rust와 Emscripten이 설치 되는 OS면 된다. Rust는 Windows, Mac, Ubuntu OS에서 실행 되는 것을 확인 했지만 Emscripten은 우분투 최신 버전과 14.04에서만 테스트를 해 보았기 때문에 최신 Ubuntu 기준으로 정리를 해둔다.
Docker Image 만들어 두면 편할 것 같았는데, 막상 컴파일 환경을 이미지로 빌드하고 보니 이미지 크기가 25GB를 넘는 문제가 있다. Rust 설치 Rust 설치와 Cargo에 대한 설명은 Cargo 패키지 시스템 - Cargo로 프로젝트 생성부터 배포까지 참고 안정된 Rust 버전과 wasm32 타겟 설치rustup install stable rustup default stable rustup target add wasm32-unknown-emscripten Emscripten 설치 참고:  http://kripken.github.io/emscripten-site/docs/building_from_source/building_emscripten_from_source_on_linux.html#building-emscripten-on-linux
우분투 14.04에서는 설치 중에 cmake 버전 때문에 문제가 생긴다. cmake 최신 버전을 설치 해주면 문제 없다.
최신 우분투에서는 아래 순으로 진행하면 된다. docker에서 실행 한 순서 이기 때문에 sudo로 실행하지 않는다. apt-get update apt-get install build-essential apt-get install cmake apt-get install python2.7 apt-get install nodejs apt-get…

Cargo 패키지 시스템 - Cargo로 프로젝트 생성부터 배포까지

왜?

Rust를 배워보자

Cargo? Cargo는 Rust의 패키지 매니저다. 자바의 Maven이나 Gradle같은 걸로 생각하면 된다. Cargo로 프로젝트 생성 부터 배포, 커스텀 빌드까지 정리 해 두기로 한다.
목차 설치프로젝트 생성빌드실행유닛 테스트 실행배포커스텀 빌드
설치 기본적으로 Cargo는 Rust와 같이 배포가 되기 때문에 rustc가 설치 되어 있다면 Cargo도 설치 되어 있다고 보면 된다.

설치는 간단하다.
curl https://sh.rustup.rs -sSf | shinfo: downloading installer Welcome to Rust! ... Current installation options: default host triple: x86_64-unknown-linux-gnu default toolchain: stable modify PATH variable: yes 1) Proceed with installation (default) 2) Customize installation 3) Cancel installation ... To configure your current shell run source $HOME/.cargo/env 1) Proceed with installation (default) 를 선택해서 진행하면 된다. 설치된 실행 파일들을 자동으로 PATH에 등록되지 않기 때문에 설치 메시지에서 알려 주는 대로 source $HOME/.cargo/env 를 해줘야 한다. ( ~/.bash_profile이나 ~/.profile에 추가해 두면 매번 실행하지 않아도 된다. )

기본 설치를 하지 않고 2) Customize installation을 선택하면 stable/beta/nightly 중에서 툴체인을 선택하거나 컴파일 타겟을 바꿀 수 있다. 나중에 필요하면 rustup명령으로 툴체인이나 타겟은 바꿀 수 있으니 기본으로 설치해도 무방하다.

설치 디렉토리($HOME/.cargo) 아래 bin 디렉토리…

Rust를 배워보자

개인적으로 명시적으로 컴파일 되는 언어를 좋아 하고 Rust의 오너쉽(Ownership)모델이 이색적이어서 기회가 되면 Rust를 익혀 봐야지 했었다. 마침 소장하고 있는 mp3 파일들을 정리를 하던 중이라 ID3 검색기 겸 태거(Tagger)를 만들어 보기로 마음 먹었다. 대충 문법만 읽고 시작했다가 큰코 다쳤던 삽질기를 정리 해 둔다.

짧은 소감은 오너쉽 모델 때문에 코딩이 더디다. 평소의 코딩 패턴도 바뀐다. 그렇지만 컴파일만 통과하면 견고한 결과물을 얻을 수 있다. 물론, 다른 언어로 개발하면 프로그램이 견고하지 않다는 뜻은 아니다. Mutable 변수와 Immutable 변수로 선언하는데 그치지 않고 값을 넘겨 줄 때 즉, 빌려줄 때 "소유권 이전", "읽기용 참조" 그리고 "수정용 참조"로 명시 할 수 있어서 예상치 못한 데이터 변경 오류에 견고하다는 의미다.

개발 할 땐 참고 서적/사이트, 개발 툴과 정보를 얻을 수 있는 커뮤니티를 중요하게 생각 하는 편인데,

아직 Rust 관련된 책이 몇 권 없는게 아쉬웠다. 그렇지만 공식 사이트 http://rust-lang.github.io/bookhttps://doc.rust-lang.org 이 훌륭해서 개인적으로 언어를 익히기엔 충분했고,

패키지 시스템인 Cargo(https://crates.io/)가 마크다운 형식의 문서화 지원하고, Git 연동, 유닛 테스트 환경 그리고 크로스 컴파일 환경, 모듈 레지스터리 서비스 연동등을 제공한다. 물론, 빌드 스크립트도 작성 할 수 있고, 시스템 언어라서 크로스 컴파일도 지원한다. 무엇보다 사용법이 단순해서 학습곡선이 낮다는 점이 좋다.

사용해본 개발툴은 Intellij와 Visual Studio Code 두가지 인데, IntelliJ는 IDE인 만큼 리팩토링 기능이나 호출자(Caller) 보기와 같은 기능이 더 지원 되지만 많이 무거웠지만, 양쪽 모두 코드 정의 가기(Go to Definition)가 …

[Vert.x] Vertlcle, Event Loop 그리고 Thread

Vert.x 실행 할 때 Verticle 개수와 CPU 개수나 Thread 개수 연관 등이 궁금해서 잠시 테스트 해 보았다.

Vert.x의 Verticle은 Event Loop에서 동작한다. 여기서 Event Loop는 Netty의 "io.netty.channel.nio.NioEventLoop"를 말하며, NIO Selector 기반이다.

NIO Selector 설명간단한 EventLoop 구현NioEventLoop.java
Event Loop Pool 개수는 NioEventLoop를 생성하는 개수가 된다. Event Loop Pool 개수를 지정 하지 않으면 아래가 기본 공식이다.

2 * Runtime.getRuntime().availableProcessors()

Vert.x의 Verticle은 Event Loop에서 동작한다 했다. 정확히는 Event Loop는 Single Threaded Executor이고 Verticle은 하나의 실행 Task이다. Verticle이 실행 될 때 Thread 이름과 함께 로그를 남겨 보면 Verticle을 실행한 Event Loop의 Thread를 확인 할 수 있다.

[vert.x-eventloop-thread-0 ] INFO fs.App - !!!Start App!!! [vert.x-eventloop-thread-1 ] INFO fs.redis.Redis - !!!Start Redis!!! [vert.x-eventloop-thread-2 ] INFO fs.redis.Redis - !!!Start Redis!!! [vert.x-eventloop-thread-3 ] INFO fs.web.Http - !!!Start Http!!! [vert.x-eventloop-thread-4 ] INFO fs.web.Http - !!!Start Http!!!
코드로 Event Loop Po…

[코드로 보는 카프카] Producer: BufferPool

카프카 프로듀서는 메시지를 전송할 때 ByteBuffer를 사용한다. ByteBuffer는 생성하는 쓰레드에서 큰 메모리 단위를 생성하거나 여러 버퍼에 할당된 메모리를 해제할 때 쓰레드는 기아가 되거나 데드락이 될 수 있다. 이런 문제 때문에 프로듀서는 BufferPool을 사용한다.  이 BufferPool은 충분한 메모리가 확보 될 때 까지 쓰레드를 기다리게 할 수 있고, 이미 생성된 ByteBuffer를 재사용 할 수 있으며, 제한된 메모리로 동작할 수 있게 한다.

실제 BufferPool.java는 메트릭 관련 코드도 있고 다른 변수들이 있어 예시보다는 쬐~금 복잡하지만 allocate와 deallocate 부분만 간단히 구현해보고 메모리를 제한하는 방법과 쓰레드를 처리하는 동작 방식에 대해 알아 두기로 한다.

코드를 보기 앞서 ByteBuffer.allocate와 ReentrantLock에 대해 먼저 알아보자.
ByteBuffer.allocate  vs ByteBuffer.allocateDirect
BufferPool은 allocate를 사용한다. 왜 allocateDirect를 사용하지 않는지는 여기에 설명 되어 있는데, 간단히 요약하면 아래 정도의 내용이 된다.
생명주기가 짧거나 자주 사용되지 않는 객체에는 다이렉트 버퍼를 사용하지 않아야 한다. 왜냐하면, 다이렉트 버퍼는 OS 종속적인 네이티브 코드를 사용하기 때문에 힙기반 버퍼보다 생성과 메모리 반환 비용이 높고 가비지 컬렉터의 영역 밖이라 메모리 누수가 있을 수 있다. 용량이 큰 다이렉트 버퍼를 빈번하게 할당하면 OutofMemorryError가 생길 수 있다.
그리고, FileChannel 에서 non-direct Buffer와 direct Buffer 속도비교 (FileChannel and non-direct buffer vs. FileChannel and direct buffer, 중국어! 코드와 그림만 보자)
버퍼가 256KB보다 작을땐 non-direct Buffer가 훨씬 빠르…