🧱

oneTBB をソースファイルからビルドする

2022/09/11に公開

概要

oneAPI Threading Building Blocks(oneTBB) を git リポジトリのソースファイルからビルドする方法をまとめます。
こういうのに慣れておらず苦戦したので残しておきます。
vcpkg でインストールできるらしいのですが、自分の環境ではうまくいかず…。

https://github.com/oneapi-src/oneTBB

基本的には Installation from SourcesREADME for the CMake build system の内容に沿って進めるだけです。

環境

私の環境は以下の通りです。同じ Windows でも Visual Studio の場合や、Linux など OS が異なる場合は手順が違うかもしれません。

  • GCC (Mingw-w64 12.2.0)
  • Windows 10 Home 64bit
  • CMake 3.24.0
  • Ninja 1.11.0
  • hwloc 2.8

ビルド手順

まず、oneTBB の Git リポジトリからクローンしてきます。

> git clone https://github.com/oneapi-src/oneTBB.git
> cd oneTBB

ビルド用のディレクトリを作ってそこに移動します。名前はなんでもよいです。

> mkdir build
> cd build

CMake でビルドルールを生成します。自分は Ninja を使っているのでジェネレータに Ninjaを指定しています。

> cmake \
-GNinja
-DCMAKE_BUILD_TYPE=Release \
-DCMAKE_INSTALL_PREFIX=C:\cbw\tbb \
-DTBB_STRICT=OFF \
-DTBB_TEST=OFF \
-DCMAKE_HWLOC_2_5_LIBRARY_PATH=C:\cbw\hwloc\lib\libhwloc.lib \
-DCMAKE_HWLOC_2_5_INCLUDE_PATH=C:\cbw\hwloc\include \
-DCMAKE_HWLOC_2_5_DLL_PATH=C:\cbw\bin\libhwloc-15.dll \
-DBUILD_SHARED_LIBS=OFF ..

CMake のオプションについて

指定するオプションが多いので、必要そうなものを説明します。
他のオプションについては README for the CMake build system で説明されています。

  • CMAKE_BUILD_TYPE=Release
    • Debug ビルドか Release ビルドかを選びます。指定しなければ両方なのかもしれません(未確認)。
  • CMAKE_INSTALL_PREFIX=<oneTBB ライブラリをインストールするディレクトリパス>
    • include/lib/share/ など、後の過程で ninja install したときに oneTBB ライブラリの中身が配置されるディレクトリパスです。
    • 例えば、C:/tbb など
  • TBB_STRICT=OFF
    • ビルド時にコンパイラの警告をビルドエラーとするかどうかを切り替えます。
    • 自分の環境では、error: value computed is not used [-Werror=unused-value] という警告が発生してビルドがコケてしまうので、このオプションを OFF にして警告を無視しています。無視しても多分大丈夫なはず…。
      • この警告をなんとかしようとして手間取りました
  • CMAKE_HWLOC_2_5_LIBRARY_PATH=<hwloc のライブラリファイル(libhwloc.lib)へのパス>
  • CMAKE_HWLOC_2_5_INCLUDE_PATH=<hwloc のヘッダファイルがあるディレクトリへのパス>
  • CMAKE_HWLOC_2_5_DLL_PATH=<hwloc の動的ライブラリファイル(libhwloc-XX.dll)へのパス>
    • 動的ライブラリファイル名は自分の場合 libhwloc-15.dll でした。
    • hwloc の静的/動的ライブラリファイル(libhwloc.{lib/dll})へのパスというところがポイントです。普段 GCC でリンクするときの指定方法とは異なります(ここでも少し詰まりました)。
    • Windows の場合は CMAKE_HWLOC_2_5_DLL_PATH の指定が必要になります。
    • 例えば、C:/hwloc に hwloc ライブラリが配置されていれば以下のような指定になります。
      • -DCMAKE_HWLOC_2_5_LIBRARY_PATH=C:/hwloc/lib/libhwloc.lib
      • -DCMAKE_HWLOC_2_5_INCLUDE_PATH=C:/hwloc/include
      • -DCMAKE_HWLOC_2_5_DLL_PATH=C:/hwloc/bin/libhwloc-15.dll

そもそも hwloc とは?

Potable Hardware Locality(hwloc)ライブラリとは、コンピュータの OS やアーキテクチャなどの違いを抽象化して移植性を高めるためのライブラリ、のようです(多分)。並列でコアを動かす oneTBB としては、確かに必要となりそうです。
自分ははじめこのライブラリを用意しておらず、これ関連でビルドがコケてようやく必要なのを知りました(ドキュメントをよく読め)。
これも Git リポジトリ からビルドしてもよかったのですが、公式ページ からビルド済みのものをダウンロードできるのでこちらが楽です。

https://www.open-mpi.org/projects/hwloc/

左の「Sub Projects > Hardware Locality」から最新のバージョンをダウンロードして任意のディレクトリに展開します。
私がダウンロードした時点でバージョンは 2.8 だったので、CMake のオプションでは『バージョン 2.5 以上の場合』として設定すれば問題なさそうです(CMAKE_HWLOC_2_5_* を使う)。

静的ライブラリとしてのビルド

oneTBB は普通にビルドすると共有(動的)ライブラリとしてビルドされます。しかし、共有ライブラリをリンクする場合、実行時のライブラリファイルの配置が重要になるので、管理が少し面倒です(環境変数を汚したくないし"共有ライブラリ置き場"に全部突っ込んみもしたくない)。個人的にはバイナリサイズは大きくなっていいので静的ライブラリとしてリンクしたいです。
--static オプションをリンク時に指定すれば、共有ライブラリも静的ライブラリのようにリンクできるのですが、「このライブラリは共有ライブラリか静的ライブラリか」を意識してビルドする必要があるので、これもまた面倒。

調べてみると、-DBUILD_SHARED_LIBS=OFF とオプションを指定すると静的ライブラリとしてビルドできるようです。→GitHubのイシュー
今回はこのオプションを使って静的ライブラリとしてビルドしています。

さて、手順に戻ります。
CMake をお望みのオプション付きで実行すると、同じディレクトリ内に build.ninja が生成されるので、Ninja でビルドします。

> ninja

これで、同じディレクトリに gnu_12.2_cxx11_64_release というフォルダが作成され、その中にビルド生成物が格納されています。
そして、これを先ほど指定したディレクトリへインストールして完了です。

> ninja install

oneTBB の動作確認

実際に oneTBB を呼び出してみて動作確認してみます。

今回は concurrent_hash_map を使います。ネットに転がっていたコードそのままですが…。→参考
※ビルドが成功し正常に実行できることだけを確認していて、実際の並列処理がうまくいくかまでは試していません。ビルド通ればいけるやろ。

onetbb_test.cpp
#include <iostream>
#include <string>
#include <tbb/concurrent_hash_map.h>

int main(int argc, char* arv[]) {
    using Accessor = tbb::concurrent_hash_map<int, std::string>::accessor;
    tbb::concurrent_hash_map<int, std::string> hmap;
    hmap.insert({1, "hello"});

    hmap.emplace(2, "world");
    {
        Accessor accessor;
        auto found = hmap.find(accessor, 2);
        if(found) {
            std::cout << "key 2 => " << accessor->second << std::endl;
        }
    }
    {
        Accessor accessor;
        auto found = hmap.find(accessor, 1);
        if(found) {
            accessor->second += ", world!";
            std::cout << "key 1 is updated : " << accessor->second << std::endl;
        }
    }
	
    return 0;
}

コンパイルするときにライブラリを指定します(oneTBB の配置場所を C:/tbb とします)。

> g++ -IC:/tbb/include -o ./onetbb_test ./onetbb_test.cpp -LC:/tbb/lib -ltbb12

実行すると、問題なく動作しました。

> ./onetbb_test.exe
key 2 => world
key 1 is updated : hello, world!

concurrent_hash_map のリファレンスはこちら

https://oneapi-src.github.io/oneTBB/main/tbb_userguide/concurrent_hash_map.html

oneTBB のリファレンスは こちら です。

https://oneapi-src.github.io/oneTBB/

おわりに

自分は色々な前提知識が足りておらず悪戦苦闘しましたが、みなさんはこの記事が助けとなり導入が楽になっていれば幸いです。

oneTBB で並列プログラミング頑張るぞ~!(使いこなせるとは言ってない)

Discussion