OpenCLではソースコードを文字列で与え、実行時にコンパイルしています。
こうすることで、Platformが提供する各種デバイスに応じたProgramを用意できるわけですが、いかんせんコンパイル時間がMOTTAINAI。デバイスを決め打ちにしていいならコンパイル済のBinary Kernelを直接ロードするんが手っ取り早いす。
で、Binary Kernelのつくりかた。
KB115 - ATI Stream SDK v2.2 Support for Binary OpenCL™ Kernels
のキモんとこを解説。
0. 前準備
上記KB115ページに clbinary.zip
があります。こいつを落っことして解凍し、clbinarygen.c, clbinaryuse.c を用意します。
1. clbinarygen, clbinaryuse をコンパイルし、exeを作ります。
vc10ならば:
: build.bat
set INCDIR="%ATISTREAMSDKROOT%include"
set LIBDIR="%ATISTREAMSDKROOT%lib\x86"
cl -EHsc -I%INCDIR% %1 %2 %3 %4 %5 OpenCL.lib -link -libpath:%LIBDIR%
なんてなバッチからコンパイルすりゃえぇべ。
2. カーネル・コードをコンパイルし、Kernel Binaryを作ります。
カーネル・コード ほげほげ.cl を kernel.cl にコピーし、clbinarygen を起動すると:
>clbinarygen
Reading in program from kernel.cl
Number of platforms found: 1
Found AMD platform
Number of devices found: 10
DEVICE[0]: CL_DEVICE_TYPE_CPU, Name=AMD Athlon(tm) 64 X2 Dual Core Processor 4400+
DEVICE[1]: CL_DEVICE_TYPE_GPU, Name=Redwood
DEVICE[2]: CL_DEVICE_TYPE_GPU, Name=ATI RV770
DEVICE[3]: CL_DEVICE_TYPE_GPU, Name=ATI RV770
DEVICE[4]: CL_DEVICE_TYPE_GPU, Name=ATI RV710
DEVICE[5]: CL_DEVICE_TYPE_GPU, Name=ATI RV730
DEVICE[6]: CL_DEVICE_TYPE_GPU, Name=Cypress
DEVICE[7]: CL_DEVICE_TYPE_GPU, Name=Juniper
DEVICE[8]: CL_DEVICE_TYPE_GPU, Name=Redwood
DEVICE[9]: CL_DEVICE_TYPE_GPU, Name=Cedar
Writing out binary kernel to kernel.bin.0
Writing out binary kernel to kernel.bin.1
Writing out binary kernel to kernel.bin.2
Writing out binary kernel to kernel.bin.3
Writing out binary kernel to kernel.bin.4
Writing out binary kernel to kernel.bin.5
Writing out binary kernel to kernel.bin.6
Writing out binary kernel to kernel.bin.7
Writing out binary kernel to kernel.bin.8
Writing out binary kernel to kernel.bin.9
こんなん出ました。各Device対応に kernel.bin.xxx が生成されます。僕のRADEON HD5670 のDevice名は Redwood だから kernel.bin.1 か kernel.bin.8 を使えばいいみたいよ。
3. できあがったBinary Kernelはそのまんま使えるみたいだけど、そこそこデカいので不要なデータをさっぴいて小さくします。
Binary Kernelの要らんとこをそぎ落として小さくするにはLinux由来のツール: readelf, objcopy が必要となります。僕はWindowsのヒトなのでCygwinをインストールしておきます。そんときDevelカテゴリのbinutilsパッケージをお忘れなく。この中にreadelf,objcopyがありますでの。
>c:\cygwin\bin\readelf -aW kernel.bin.1 | c:\cygwin\bin\grep Machine
Machine: <unknown>: 0x3f3
こんなん出ました。ケツにある16進: 0x3f3 が大事。
んでは要らん部分をそぎ落とします。
32bit:
objcopy -I elf32-i386 -O elf32-i386 -R .source -R .livmir -R .amdil --alt-machine-code=<さっきの16進コード> <Kernel Binary名> <できあがり>
64bit:
objcopy -I elf64-x86-64 -O elf64-x86-64 -R .source -R .livmir -R .amdil --alt-machine-code=<さっきの16進コード> <Kernel Binary名> <できあがり>
僕んトコだと:
>c:\cygwin\bin\objcopy -I elf32-i386 -O elf32-i386 -R .source -R .livmir -R .amdil --alt-machine-code=0x3f3 kernel.bin.1 stripped_kernel.bin
/usr/bin/objcopy: this target does not support 1011 alternative machine codes
/usr/bin/objcopy: treating that number as an absolute e_machine value instead
なんかゆってるけどひとまず気にしないww
4. できあがったstripped_kernel.binが使えることを確認します。
>clbinaryuse
Number of platforms found: 1
Found AMD platform
Number of devices found: 2
DEVICE[0]: CL_DEVICE_TYPE_CPU, Name=AMD Athlon(tm) 64 X2 Dual Core Processor 4400+
DEVICE[1]: CL_DEVICE_TYPE_GPU, Name=Redwood
Which device do you want to use? [0-1]?
1
Enter filename of corresponding binary kernel file
stripped_kernel.bin
Reading in binary kernel from stripped_kernel.bin
*** REPLACE THIS WITH ACTUAL WORK ***
↑コレが見えたらできあがり。
このバイナリ・ファイルを読み込むには:
cl::Context context;
vector<cl::Device> devices;
...
vector<char> binary;
{
ifstream stream("stripped_kernel.bin", ios::binary);
if ( !stream.is_open() ) { return 0; }
istreambuf_iterator<char> first(stream);
istreambuf_iterator<char> last;
copy(first, last, back_inserter(binary));
}
cl::Program::Binaries binaries;
binaries.push_back(make_pair(static_cast<void*>(&binary[0]),binary.size()));
cl::Program program(context,devices,binaries);
とかなんとか。