Qt WebAssemblyをマルチスレッドに対応させる


はじめに

こちらの記事で構築した環境を元にQtのWebAssemblyをマルチスレッド対応にしていきます。

QtのマルチスレッドWebAssembly

オンラインインストール版など配布版のQt5に付属しているのはシングルスレッド版のWebAssemblyです。本家Qtサイトの説明を読むと「なるべく多くの種類のブラウザに対応するためにシンプルな造りにしている」そうです。マルチスレッド動作するプロジェクトを動作させるためには、マルチスレッド版のWebAssemblyが必要になります。そのためには配布版のQtは使用できず、Qt5をソースファイルからマルチスレッド・オプションを付加してビルドして利用しなければなりません。

Qtソース取得

mkdir ~/qt5-build
cd ~/qt5-build
git clone git://code.qt.io/qt/qt5.git
cd qt5
git checkout 5.15
git submodule update --init –recursive

ビルド

私の環境ではmakeに3時間ほどかかりました。

mkdir build
cd build
touch .qmake.stash
touch .qmake.super
source $EMSDK/emsdk_env.sh
../qt5/configure -xplatform wasm-emscripten -feature-thread -developer-build -opensource -static -nomake examples -nomake tests -no-dbus -no-headersclean -system-libpng -no-ssl -no-warnings-are-errors -skip qtlocation -skip qt3d -skip qtwebengine -prefix $PWD/qtbase
make

ファイルのコピー

シングルスレッド版Qtと並べてマルチスレッド版Qt一式を配置します。
(私の環境ではシングルスレッド版は/opt/Qt/5.15.5にあります)

sudo mkdir /opt/Qt/5.15.3/wasm_multithread
sudo cp -r ~/qt5-build/build/qtbase/* /opt/Qt/5.15.3/wasm_multithread/

Kitの作成

Qt Creatorの「ツール」→「オプション」メニューを開き、「Kits」ページの「Qtバージョン」タブを表示。
「追加」ボタンを押して、/opt/Qt/5.15.3/wasm_multithread/qbase/bin/qmakeを選択します。

「Kits」タブを表示して「Add」ボタンを押し、下記のようにフィールドを設定。
「名前:」フィールドを「Qt %{Qt:Version} WebAssembly MultiThread」
「Qt version:」フィールドを「Qt5.15.3」

Qtプロジェクトの設定

Qt Creatorプロジェクト設定画面で、マルチスレッド用のKitを使ってBuild&Runを設定します。

プロジェクトファイル(.pro)の設定

マルチスレッドプロジェクトのEmscriptenビルドオプションとして次の2つがあります。

QMAKE_WASM_PTHREAD_POOL_SIZE = 4
QMAKE_WASM_TOTAL_MEMORY = 1GB


QMAKE_WASM_PTHREAD_POOL_SIZEはブラウザVMに準備させるスレッド数(WebWorker)で、EmscriptenのPTHREAD_POOL_SIZEオプションにマップされます。
また、マルチスレッドでは実行時にヒープ領域の拡大が許されていないので、アプリのビルド時にヒープメモリサイズを指定する必要があります。その値がQMAKE_WASM_TOTAL_MEMORYで、EmscriptenのTOTAL_MEMORYオプションにマップされます。

これらの設定値が大きすぎるとビルドやランができなくなります。
私の環境ではPTHREAD_POOL_SIZE=15にするとChromeでのアプリ起動ができなくなりました。main関数にブレークポイントを設置しても、その前にアボートするのでWebWorkerを生成する段階でリソースが枯渇しているのかもしれません。PTHREAD_POOL_SIZE=14に変更するとmain関数に到達しました。
またQMAKE_WASM_TOTAL_MEMORY=4GBでビルドエラーになり、3GBではOKでした。
これらはOSやブラウザの設定で変動するのかもしれませんが、詳しい情報は得られていません。

さらに詳しくはQt WebAssembly本家サイトやEmscriptenサイトの情報を調べてみてください。

マルチスレッド実行

マルチスレッドアプリをChromeで実行すると、PTHREAD_POOL_SIZEで指定した数だけスレッドが準備されていることが分かります(MultiThreadSample.worker.js)。

ブラウザのマルチスレッド設定

私が使っているChromeは下記バージョンですが、マルチスレッドWebAssemblyに対応しているので設定変更は不要です。そのままで実行できます。

Google Chrome バージョン: 91.0.4472.114(Official Build) (64 ビット)

Firefoxは79からサポートされているようです。それ以前のSharedArrayBufferを無効化されているFirefoxではマルチスレッドが動きませんが、アドレスバーに about:configと入力して、javascript.options.shared_memoryの値を有効にすれば動作するらしいです。

参考サイト