Easy::jit は、C++ コードの単純なジャストインタイム コード生成を可能にするコンパイラ支援ライブラリです。
まず、clang と LLVM をインストールします。
apt install llvm-6.0-dev llvm-6.0-tools clang-6.0
次に、プロジェクトを構成してコンパイルします。
cmake -DLLVM_DIR=/usr/lib/llvm-6.0/cmake < path_to_easy_jit_src >
cmake --build .
サンプルをビルドするには、opencv ライブラリをインストールし、フラグ-DEASY_JIT_EXAMPLE=1
を cmake コマンドに追加します。
ベンチマークを有効にするには、Google ベンチマーク フレームワークをインストールし、フラグ-DEASY_JIT_BENCHMARK=1 -DBENCHMARK_DIR=<path_to_google_benchmark_install>
を cmake コマンドに追加します。
すべて準備完了です!
プロジェクトに簡単なテストのみを実行したい場合は、docker で使用するためのすべてが提供されています。これを行うには、 <path_to_easy_jit_src>/misc/docker
内のスクリプトを使用して現在のディレクトリから Dockerfile を生成し、Docker インスタンスを生成します。
python3 < path_to_easy_jit_src > /misc/docker/GenDockerfile.py < path_to_easy_jit_src > /.travis.yml > Dockerfile
docker build -t easy/test -f Dockerfile
docker run -ti easy/test /bin/bash
Easy::Jit ライブラリはコンパイラの支援に依存しているため、それを使用するにはコンパイラ プラグインをロードすることが必須です。フラグ-Xclang -load -Xclang <path_to_easy_jit_build>/bin/EasyJitPass.so
プラグインをロードします。
インクルードされたヘッダーには C++14 サポートが必要です。インクルード ディレクトリを忘れずに追加してください。 --std=c++14 -I<path_to_easy_jit_src>/cpplib/include
使用します。
最後に、 -L<path_to_easy_jit_build>/bin -lEasyJitRuntime
を使用して、バイナリを Easy::Jit ランタイム ライブラリに対してリンクする必要があります。
すべてをまとめると、次のコマンドが得られます。
clang++-6.0 --std=c++14 < my_file.cpp >
-Xclang -load -Xclang /path/to/easy/jit/build/bin/bin/EasyJitPass.so
-I < path_to_easy_jit_src > /cpplib/include
-L < path_to_easy_jit_build > /bin -lEasyJitRuntime
ビデオ ストリームに画像フィルターを適用するソフトウェアの以下のコードを考えてみましょう。次のセクションでは、Easy::jit ライブラリを使用するようにこれを調整します。最適化する関数はkernel
で、画像全体にマスクを適用します。
マスク、その寸法、面積は頻繁には変更されないため、これらのパラメーターに関数を特化するのが合理的と思われます。さらに、画像の寸法とチャネル数は通常、実行全体を通じて一定のままです。ただし、その値はストリームに依存するため、知ることはできません。
static void kernel ( const char * mask, unsigned mask_size, unsigned mask_area,
const unsigned char * in, unsigned char * out,
unsigned rows, unsigned cols, unsigned channels) {
unsigned mask_middle = (mask_size/ 2 + 1 );
unsigned middle = (cols+ 1 )*mask_middle;
for ( unsigned i = 0 ; i != rows-mask_size; ++i) {
for ( unsigned j = 0 ; j != cols-mask_size; ++j) {
for ( unsigned ch = 0 ; ch != channels; ++ch) {
long out_val = 0 ;
for ( unsigned ii = 0 ; ii != mask_size; ++ii) {
for ( unsigned jj = 0 ; jj != mask_size; ++jj) {
out_val += mask[ii*mask_size+jj] * in[((i+ii)*cols+j+jj)*channels+ch];
}
}
out[(i*cols+j+middle)*channels+ch] = out_val / mask_area;
}
}
}
}
static void apply_filter ( const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) {
kernel (mask, mask_size, mask_area, image. ptr ( 0 , 0 ), out-> ptr ( 0 , 0 ), image. rows , image. cols , image. channels ());
}
ライブラリのメインヘッダーはeasy/jit.h
で、ライブラリの唯一のコア関数がエクスポートされます。この関数はどのように呼ばれるでしょうか? -- easy::jit
。対応する include ディレクティブをファイルの先頭に追加します。
# include < easy/jit.h >
easy::jit
を呼び出すことで、関数を特殊化し、2 つのパラメータ (入力フレームと出力フレーム) だけを受け取る新しい関数を取得します。
static void apply_filter ( const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) {
using namespace std ::placeholders ;
auto kernel_opt = easy::jit (kernel, mask, mask_size, mask_area, _1, _2, image. rows , image. cols , image. channels ());
kernel_opt (image. ptr ( 0 , 0 ), out-> ptr ( 0 , 0 ));
}
Easy::jit は、実行時にバイナリ コードに特化する関数の LLVM ビットコード表現を埋め込みます。これを実行するには、ライブラリがこれらの関数の実装にアクセスする必要があります。 Easy::jit は実行時にどの関数が特殊化されているかを推測しようとしますが、多くの場合これはまだ不可能です。
この場合、次のコードに示すように、 EASY_JIT_EXPOSE
マクロを使用できます。
void EASY_JIT_EXPOSE kernel () { /* ... */ }
またはコンパイル時に正規表現を使用します。以下のコマンドは、名前が「^kernel」で始まるすべての関数をエクスポートします。
clang++ ... -mllvm -easy-export= " ^kernel.* " ...
easy/jit.h
ヘッダーと並行して、すでに生成された関数の再コンパイルを避けるためのコード キャッシュを提供するeasy/code_cache.h
があります。
以下に、前のセクションのコードを示しますが、コード キャッシュを使用するように調整されています。
# include < easy/code_cache.h >
static void apply_filter ( const char *mask, unsigned mask_size, unsigned mask_area, cv::Mat &image, cv::Mat *&out) {
using namespace std ::placeholders ;
static easy::Cache<> cache;
auto const &kernel_opt = cache. jit (kernel, mask, mask_size, mask_area, _1, _2, image. rows , image. cols , image. channels ());
kernel_opt (image. ptr ( 0 , 0 ), out-> ptr ( 0 , 0 ));
}
このプロジェクトの最上位ディレクトリにあるファイルLICENSE
参照してください。
個人プロジェクトでの作業をサポートしてくださった Quarkslab に心より感謝いたします。
セルジュ・グエルトン (serge_sans_paille)
フアン・マヌエル・マルティネス・カーマーニョ (jmmartinez)
Kavon Farvardin (kavon) atJIT の著者