Halideは画像処理記述用のDSLです。
どういったものかは公式ページをご覧いただくか、福嶋先生がある程度の日本語資料を投稿されていますので、そちらを参照ください。
HalideではJITコンパイルによる動作だけではなく、記述したFuncを静的ライブラリとしてAOTコンパイルし、既存のプログラムに組み込むことも可能です。
基本的な作り方と使い方
# include <Halide.h>
void Gen_StaticLib()
{
using namespace Halide;
ImageParam input(type_of<uint8_t>(), 3); // x,y,ch 3次元
Param<float> mul;
Func brighter;
Var x, y, ch;
brighter(x, y, ch) = cast<uint8_t>(min(input(x, y, ch) * mul, 255));
brighter.parallel(y);
brighter.compile_to_static_library(
"brighter", // .lib のファイル名、brighter.lib になる
{ input, mul }, // 入力パラメータ、関数引数順になる
"brighter" // 関数名
);
}
実行するとbrighter.hとbrighter.libができているので、既存のプログラムにリンクして使うだけです。
入力、出力はHalideBuffer.h中のHalide::Runtime::Bufferを使えばOK。
Halide.dllは必要ありません。
# include <HalideBuffer.h>
# include "brighter.h"
void Use_StaticLib()
{
using namespace Halide;
Runtime::Buffer<uint8_t> src(640, 480, 3);
Runtime::Buffer<uint8_t> dst(640, 480, 3);
/* 画像読み込みコードは省略 */
brighter(src, 1.5f, dst); // 出力は生成時のパラメータの最後につく
}
クロスコンパイル
compile_to_static_libraryには4番目の引数があり、OSやCPUアーキテクチャ、SIMDの有無などを設定できます。
指定しない場合は、AOTコンパイル時の環境が自動的に使用されます。
# include <Halide.h>
void Gen_StaticLib()
{
using namespace Halide;
ImageParam input(type_of<uint8_t>(), 3);
Func brighter;
Var x, y, ch;
brighter(x, y, ch) = cast<uint8_t>(min(input(x, y, ch) * 1.5f, 255));
brighter.parallel(y);
// 動作環境の指定
Target env;
env.os = Target::OS::Windows;
env.arch = Target::Arch::X86;
env.bits = 64;
env.set_feature(Target::Feature::AVX2); // vectorizeしていないからあまり意味がないが…
brighter.compile_to_static_library(
"brighter",
{ input },
"brighter",
env //ここに指定
);
}
生成した複数のスタティックライブラリをリンクするとき
1つの.libファイルに複数のFuncを含めることはできないようです。
処理ごとに.libも複数生成する必要があります。
# include <Halide.h>
void Gen_StaticLib()
{
using namespace Halide;
ImageParam input(type_of<uint8_t>(), 2);
Param<float> val;
Func mul, add;
Var x, y;
mul(x, y) = cast<uint8_t>(min(input(x, y) * val, 255));
add(x, y) = cast<uint8_t>(min(input(x, y) + val, 255));
mul.compile_to_static_library(
"mul", // mul.lib
{ input, val },
"mul"
);
add.compile_to_static_library(
"add", // add.lib
{ input, val },
"add"
);
}
が、これらの.libを同時に使おうとするとリンクエラーになってしまいます。
どうやら指定なしだと.libにはHalideのランタイム関数も同時に含まれるようで、ランタイムの重複定義でコケるっぽいです。
リンクする.libのうちどれか一つだけにランタイムを持たせるように、Targetを設定してやると解決。
# include <Halide.h>
void Gen_StaticLib()
{
using namespace Halide;
/* 略 */
Target env, env_no_runtime;
env.os = Target::OS::Windows;
env.arch = Target::Arch::X86;
env.bits = 64;
env.set_feature(Target::Feature::AVX2);
env_no_runtime = env.with_feature(Target::Feature::NoRuntime); // ランタイム生成を抑制
mul.compile_to_static_library(
"mul", // mul.lib
{ input, val },
"mul",
env // こちらにHalideランタイムが含まれる
);
add.compile_to_static_library(
"add", // add.lib
{ input, val },
"add",
env_no_runtime //こちらはランタイムなし
);
}
あるいは、ファイルが1つ増えますが、実際には呼びださない適当なFuncを作ってランタイムだけの.libを作るほうが管理が楽かもしれません。
# include <Halide.h>
using namespace Halide;
void Gen_Halide_Runtime(Target& env)
{
// 実際には呼ばない関数
Func f;
Var x;
f(x) = x;
f.compile_to_static_library(
"Halide_Runtime",
{},
"DoNotCall",
env.without_feature(Target::Feature::NoRuntime) // ここだけNoRuntimeを外す -> ランタイムあり
);
}
void Gen_Mul(Target& env)
{
ImageParam input(type_of<uint8_t>(), 2);
Param<float> val;
Func mul;
Var x, y;
mul(x, y) = cast<uint8_t>(min(input(x, y) * val, 255));
mul.compile_to_static_library(
"mul", // mul.lib
{ input, val },
"mul",
env
);
}
void Gen_Add(Target& env)
{
ImageParam input(type_of<uint8_t>(), 2);
Param<float> val;
Func add;
Var x, y;
add(x, y) = cast<uint8_t>(min(input(x, y) + val, 255));
add.compile_to_static_library(
"add", // add.lib
{ input, val },
"add",
env
);
}
int main()
{
Target env;
env.os = Target::OS::Windows;
env.arch = Target::Arch::X86;
env.bits = 64;
env.set_features(
{ Target::Feature::AVX2,
Target::Feature::NoRuntime } // 基本はNoRuntimeにしておく
);
Gen_Halide_Runtime(env); // ランタイムあり
Gen_Mul(env); // ランタイムなし
Gen_Add(env); // ランタイムなし
}