emscripten で SIMD.js を使う
SIMD.js が使われるケースは3種類ある。
- clang デフォルトの LLVM autovectorization が働いて自動的に SIMD 化する
- GCC 拡張の SIMD Vector Extensions で明示的に使う
- x86 の Streaming SIMD Extensions 命令を使う。詳細は 大本営 を読むべし
opencv など sse サポートしているものをを asm.js にする場合は 3 が使えそう
SIMD.js が有効なブラウザを用意する
-s SIMD
で JS 出力すると SIMD.js の polyfill が付いてくる。
これをそのまま使うと遅くなる。
2017年上半期現在標準で SIMD が有効なブラウザはない。
chrome の場合起動フラグでV8の起動フラグを渡せば有効になる。
chrome --js-flags="--harmony-simd"
使える V8 起動フラグの一覧を見るには chrome --js-flags="--help"
フラグ指定して起動する方法はリンク参照。
ビルド例(opencv の場合)
1. configure
-s SIMD=1
をつけて cmake する
git clone https://github.com/opencv/opencv.git
cd opencv
mkdir build
cd build
OPTS='-O2 --llvm-lto 1 -s SIMD=1'
emcmake cmake \
-DCMAKE_BUILD_TYPE=RELEASE \
-DBUILD_DOCS=OFF \
-DBUILD_EXAMPLES=OFF \
-DBUILD_PACKAGE=OFF \
-DBUILD_WITH_DEBUG_INFO=OFF \
-DBUILD_opencv_cuda=OFF \
-DBUILD_opencv_cudaarithm=OFF \
-DBUILD_opencv_cudabgsegm=OFF \
-DBUILD_opencv_cudacodec=OFF \
-DBUILD_opencv_cudafeatures2d=OFF \
-DBUILD_opencv_cudafilters=OFF \
-DBUILD_opencv_cudaimgproc=OFF \
-DBUILD_opencv_cudaoptflow=OFF \
-DBUILD_opencv_cudastereo=OFF \
-DBUILD_opencv_cudawarping=OFF \
-DENABLE_PRECOMPILED_HEADERS=OFF \
-DWITH_1394=OFF \
-DWITH_CUDA=OFF \
-DWITH_CUFFT=OFF \
-DWITH_EIGEN=OFF \
-DWITH_FFMPEG=OFF \
-DWITH_GIGEAPI=OFF \
-DWITH_GSTREAMER=OFF \
-DWITH_GTK=OFF \
-DWITH_JASPER=OFF \
-DWITH_JPEG=OFF \
-DWITH_OPENCL=OFF \
-DWITH_OPENCLAMDBLAS=OFF \
-DWITH_OPENCLAMDFFT=OFF \
-DWITH_OPENEXR=OFF \
-DWITH_PNG=OFF \
-DWITH_PVAPI=OFF \
-DWITH_TIFF=OFF \
-DWITH_LIBV4L=OFF \
-DWITH_WEBP=OFF \
-DWITH_PTHREADS_PF=OFF \
-DBUILD_opencv_apps=OFF \
-DBUILD_PERF_TESTS=OFF \
-DBUILD_TESTS=OFF \
-DBUILD_SHARED_LIBS=OFF \
-DENABLE_SSE=OFF \
-DENABLE_SSE2=OFF \
-DENABLE_SSE3=ON \
-DENABLE_SSE41=OFF \
-DENABLE_SSE42=OFF \
-DENABLE_AVX=OFF \
-DENABLE_AVX2=OFF \
-DCMAKE_CXX_FLAGS=$OPTS \
-DCMAKE_EXE_LINKER_FLAGS=$OPTS \
-DCMAKE_CXX_FLAGS_DEBUG=$OPTS \
-DCMAKE_CXX_FLAGS_RELWITHDEBINFO=$OPTS \
-DCMAKE_C_FLAGS_RELWITHDEBINFO=$OPTS \
-DCMAKE_EXE_LINKER_FLAGS_RELWITHDEBINFO=$OPTS \
-DCMAKE_MODULE_LINKER_FLAGS_RELEASE=$OPTS \
-DCMAKE_MODULE_LINKER_FLAGS_DEBUG=$OPTS \
-DCMAKE_MODULE_LINKER_FLAGS_RELWITHDEBINFO=$OPTS \
-DCMAKE_SHARED_LINKER_FLAGS_RELEASE=$OPTS \
-DCMAKE_SHARED_LINKER_FLAGS_RELWITHDEBINFO=$OPTS \
-DCMAKE_SHARED_LINKER_FLAGS_DEBUG=$OPTS \
../
-DENABLE_SSE3=ON
にする
2. make
emmake make -j 4
これで llvm bitcode な libopencv_*.a
ができる
3. リンク
あらかじめ用意しておいた opencv を使用するプログラム my_program.bc
とリンクする
emcc \
./my_program.bc \
./lib/libopencv_video.a \
./lib/libopencv_imgproc.a \
./lib/libopencv_core.a \
./3rdparty/lib/libzlib.a \
-O2 --llvm-lto 1 -s SIMD=1 \
-o ./my_program.bc
llvm-nm my_program.bc
でシンボルが見える
基礎的な注意点として 依存するライブラリは順番を後ろにすること。
例えば libopencv_imgproc.a は libzlib.a に依存し、
libopencv_video.a は libopencv_core.a に依存している。
4. asm.js へコンパイル
emcc \
my_program.bc \
-O2 --llvm-lto 3 -s SIMD=1 \
-s TOTAL_MEMORY=33554432 \
-s ASSERTIONS=0 \
-o ./cv.js
これで SIMD が有効なブラウザだと例えば cv::calcOpticalFlowFarneback
が 4 倍ほど速くなる