スポンサーサイト

上記の広告は1ヶ月以上更新のないブログに表示されています。
新しい記事を書く事で広告が消せます。

結構間違っておった+エラー処理を変更

前掲のソース(main.cpp)、2箇所ほど間違っていた。まず、

#define N (33 * 1024)  は #define N (33 * 1024 * 1024) が正しい。33*1024ではいかにも小さすぎて並列処理の意味もない。

そして、トータルのスレッド数を規定するグローバルワークサイズの指定が間違っていた。

const size_t globalWorkSize[1] = {N}

としていたが、これだと入力データ配列の一要素に対して1スレッドを起動することになる。このサンプルでは、256スレッドからなるブロックを32個(あるいは、それでは多すぎる場合はN/256の切り上げ)起動し、一つのスレッドが、例えばa[i] * b[i]の演算のあるi(例えば3としよう)を担当するならば、同じスレッドの内部でループして、

i = 3
i = 3 + 256 * 32
i = 3 + 2 * 256 * 32
....
と i < N であるかぎりループしていくというアルゴリズムになっているので、スレッドをN個起動するのはやりすぎである。ということで、

const size_t globalWorkSize[1] = {threadsPerBlock * blocksPerGrid};

と修正。これで実行結果は

$ ./multiqueue1
Blocks per Grid = 32
Threads per Block = 256
GPU sum  = 2.76217e+22
Expected = 2.76217e+22

となる。

さて、間違いというのではないが、前掲のソースではいちいちエラー処理のためにOpenCLのAPI呼び出しに続いて毎回report_errorという自前の関数を呼び出していた。これだとソースが長くなってかなわないので、ちょっと考えてもう少しコンパクトに記述することにした。

OpenCLのAPIは、エラーコードの返し方に注目すると二種類に分類できる。一つは、新しくなにがしかのOpenCLで使用するオブジェクトを新規に作成するためのAPIである。これは例えば、OpenCLコンテキストやコマンドキュー、カーネル、プログラムオブジェクト、メモリオブジェクトなどの作成が含まれる。このタイプのAPIは、使用する場合は

cl_some_object_type obj;
cl_int error;
obj = clCreateSomeObject(args...、&error);
if(error != CL_SUCCESS) {エラー処理}

といった形になる。必ず最後の引数がエラーコードへのポインタという形式で統一されているようだ。一方、既に作成済みのOpenCLオブジェクトに対して何がしかの問い合わせや操作を行う場合は、返り値がcl_int型で、エラーコードを兼ねている。

cl_int return_val;
return_val = clDoSomething(args...);
if(return_val != CL_SUCCESS){エラー処理}

という形になる。そこで、この両者に対してそれぞれラッパーマクロを作ることにした。まず前回のreport_errorをもう少し拡張して

void report_error(const char *modulename, const int linenum, const char *fname, const char *targetFunc, cl_int error);

という形にした。やっていることは引数をすべて適当に整形してcerrに出力するだけである。そして、

#define MYCLCHECK(fname, ...) ({cl_int ret; ret = fname(__VA_ARGS__); myCLUtils::report_error(__FILE__, __LINE__, __FUNCTION__, #fname, ret);})

#define MYCLCREATE(retval, fname, ...) ({cl_int ret; retval = fname(__VA_ARGS__, &ret); myCLUtils::report_error(__FILE__, __LINE__, __FUNCTION__, #fname, ret);})

という2種類のマクロを作成した。MYCLCREATEの方が新規オブジェクト作成API用で、MYCLCHECKの方はその他のAPI用である。使い方は、MYCLCREATEの方は

  MYCLCREATE(dev_a, clCreateBuffer, clm.context,
         CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR,
         N * sizeof(float), a);

のようにする。これは、(見やすいように中途半端に展開すると)

({cl_int ret;
   dev_a = clCreateBuffer(clm.context,
  CL_MEM_READ_ONLY|CL_MEM_COPY_HOST_PTR,
  N * sizeof(float), a, &ret);
  myCLUtils::report_error(__FILE__, __LINE__  , __FUNCTION__, "clCreateBuffer", ret);
}) ;

のように展開される。注意点としては、本来最後の引数として渡すエラーコードへのポインタは、マクロが勝手にブロック内変数へのポインタとして付加してくれるので、記述してはいけないという点である。

第二の形式のAPIに対しては、MYCLCHECKマクロを、

  MYCLCHECK(clSetKernelArg, dotKernel, i++, sizeof(dev_a), &dev_a);

のように使う。これは、展開されると
({
  cl_int ret;
  ret = clSetKernelArg(dotKernel, i++, sizeof(dev_a), &dev_a);
  myCLUtils::report_error("main.cpp", 49, __FUNCTION__, "clSetKernelArg", ret);
});

のようになる。これらの定義はwww.opaquelight.com/MultiQueue1.tgz内のmyCLUtils.hで行っている。なお、MultiQueueといっているがキューはまだ一つだけである。そのうち複数キューにしてテストしてみることにする。
とりあえずは、このマクロを使うことでエラー処理をしながらもソースの行数が倍加することは避けられる。

もっときちんとエラー処理をしたければ、例外を発生させるべきなのだろうが、とりあえずはこれでよかろう。


スポンサーサイト

テーマ : プログラミング
ジャンル : title="コンピュータ">コンピュータ

コメントの投稿

非公開コメント

プロフィール

GM3D

Author:GM3D
FC2ブログへようこそ!

最新記事
最新コメント
最新トラックバック
月別アーカイブ
カテゴリ
FC2カウンター
検索フォーム
RSSリンクの表示
リンク
ブロとも申請フォーム

この人とブロともになる

QRコード
QR
上記広告は1ヶ月以上更新のないブログに表示されています。新しい記事を書くことで広告を消せます。