スポンサーサイト

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

メモリオブジェクトの確保(続)

先のプログラムでは、確保するサイズを1GBにして、ホストメモリからメモリオブジェクトへ書き込みを行った時点で実行に失敗した。ということは、最初のメモリオブジェクトの生成自体は成功したということか。そこで、clCreateBuffer()で作成、clEnqueueWriteBuffer()で書き込み、としていた部分を、試しに、clCreateBuffer()のフラグにCL_MEM_COPY_HOST_PTRを指定して、作成と内容の転送を同時に行ってみる。
すなわち、

MYCLCREATE(mobj,
clCreateBuffer,
clm.context,
CL_MEM_READ_WRITE,
size * sizeof(char),
NULL);
clFlush(clm.queues[0]);
MYCLCHECK(clEnqueueWriteBuffer,
clm.queues[0],
mobj,
CL_TRUE,
0,
size * sizeof(char),
buf,
0, NULL, NULL);

の部分を、

MYCLCREATE(mobj,
clCreateBuffer,
clm.context,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
size * sizeof(char),
buf);

と変更してみる。この実行結果は、

(最初の部分略)
mobj allocation of 256 Mbytes succeeded.
mobj allocation of 512 Mbytes succeeded.
test8.cpp: 58 in main(): clEnqueueReadBuffer failed due to CL_MEM_OBJECT_ALLOCATION_FAILURE

となり、今度は少なくとも見かけ上は、作成(と同時に行われた書き込み)まで成功しているように見える。ううむそれでは、と、次は読み出しをやめて、作成だけにしてみた。


int main(int argc, char **argv)
{
myCLManager2 clm;
cl_mem mobj;
char *buf;
size_t size;
for(size = 256; size <= LONG_MAX; size<<=1 ){
buf = new char[size];
for(size_t i = 0; i < size; i++){
buf[i] = i % 100;
}
MYCLCREATE(mobj,
clCreateBuffer,
clm.context,
CL_MEM_READ_WRITE|CL_MEM_COPY_HOST_PTR,
size * sizeof(char),
buf);
clFlush(clm.queues[0]);
std::cout << "mobj allocation of " << sizeStr(size) << " succeeded.\n";
MYCLCHECK(clReleaseMemObject, mobj);
delete[] buf;
}
return 0;
}


これで実行してみると、
(最初の部分略)
mobj allocation of 256 Mbytes succeeded.
mobj allocation of 512 Mbytes succeeded.
mobj allocation of 1 Gbytes succeeded.
mobj allocation of 2 Gbytes succeeded.
^C
えええ・・・なんだか完全にGPUの物理的なメモリサイズを越えて作成できているではないか。1GBのあたりからホストメモリの方の確保のためにディスクスワップが発生して耐えがたくなってきたので、2GBの確保に成功した時点でControl+Cで中断した。

しかしここまでくると疑念が沸いてきたので、ホストメモリの確保と書き込みはやめて、最後に単にサイズ指定でのメモリオブジェクト生成だけを試してみることにする。


int main(int argc, char **argv)
{
myCLManager2 clm;
cl_mem mobj;
size_t size;
for(size = 256; size <= LONG_MAX; size<<=1 ){
MYCLCREATE(mobj,
clCreateBuffer,
clm.context,
CL_MEM_READ_WRITE,
size * sizeof(char),
NULL);
clFlush(clm.queues[0]);
std::cout << "mobj allocation of " << sizeStr(size) << " succeeded.\n";
MYCLCHECK(clReleaseMemObject, mobj);
}
return 0;
}


これを実行してみると・・・

mobj allocation of 256 Mbytes succeeded.
mobj allocation of 512 Mbytes succeeded.
mobj allocation of 1 Gbytes succeeded.
mobj allocation of 2 Gbytes succeeded.
mobj allocation of 4 Gbytes succeeded.
mobj allocation of 8 Gbytes succeeded.
mobj allocation of 16 Gbytes succeeded.
mobj allocation of 32 Gbytes succeeded.
mobj allocation of 64 Gbytes succeeded.
mobj allocation of 128 Gbytes succeeded.
mobj allocation of 256 Gbytes succeeded.
mobj allocation of 512 Gbytes succeeded.
mobj allocation of 1024 Gbytes succeeded.
mobj allocation of 2048 Gbytes succeeded.
mobj allocation of 4096 Gbytes succeeded.
mobj allocation of 8192 Gbytes succeeded.
mobj allocation of 16384 Gbytes succeeded.
mobj allocation of 32768 Gbytes succeeded.
mobj allocation of 65536 Gbytes succeeded.
mobj allocation of 131072 Gbytes succeeded.
mobj allocation of 262144 Gbytes succeeded.
mobj allocation of 524288 Gbytes succeeded.
mobj allocation of 1048576 Gbytes succeeded.
mobj allocation of 2097152 Gbytes succeeded.
mobj allocation of 4194304 Gbytes succeeded.
mobj allocation of 8388608 Gbytes succeeded.
mobj allocation of 16777216 Gbytes succeeded.
mobj allocation of 33554432 Gbytes succeeded.
mobj allocation of 67108864 Gbytes succeeded.
mobj allocation of 134217728 Gbytes succeeded.
mobj allocation of 268435456 Gbytes succeeded.
mobj allocation of 536870912 Gbytes succeeded.
mobj allocation of 1073741824 Gbytes succeeded.
mobj allocation of 2147483648 Gbytes succeeded.
mobj allocation of 4294967296 Gbytes succeeded.

もはや笑うしかない。
表示の単位をGBまでしか用意しなかったが、事実上天井知らずである。
とはいっても、明らかにこうして確保したメモリオブジェクトが実際に使用できるわけではない。結論としては、この(NVIDIA CUDA Toolkit 4.1 on Ubuntu)実行環境では、API上でのメモリオブジェクトの作成はあくまで「予約」扱いであり、実際のGPUメモリの確保や書き込みは、その結果が実行時に実際に必要になるまで遅延されているらしい、ということだ。
スポンサーサイト

Ubuntu + NVIDIA CUDA Toolkit 4.1 上でのメモリオブジェクトの確保

私の環境は、ハードウェア的にはホストメモリ3.2GB、GPUメモリ1GBである。この環境ではたしてメモリオブジェクトをどれだけ使用できるものか。

APIとしては、clGetDeviceInfo()の引数に、CL_DEVICE_MAX_MEM_ALLOC_SIZEを指定して問い合わせを行うことでチェックできる。OpenCLの仕様によると、最低でも128MBか、あるいはGPUメモリサイズの1/4のどちらか大きい方の値ということである。ただしGPUメモリサイズというのは、正確には、clGetDeviceInfo()の引数にCL_DEVICE_GLOBAL_MEM_SIZEを指定して得られる値のことで。若干端数がハードウェア構成上の値と違う可能性はある。私の環境の場合、この値は1023MBであった。そしてCL_DEVICE_MAX_MEM_ALLOC_SIZEの返り値は255MB。

では255MBを越えるメモリオブジェクトはまったく確保できないのかというと、実際に試してみると必ずしもそうとは限らないようである。

以下のようなテストプログラムを実行してみた。



#include "myCLManager2.h"
#include "myCLUtils.h"
#include <iostream>
#include <string>
#include <sstream>
#include <values.h>

string sizeStr(size_t size)
{
size_t rsize;
const char *unit;
if(size >= 1024 * 1024 * 1024){
rsize = size / (1024 * 1024 * 1024);
unit = "Gbytes";
}else if(size >= 1024 * 1024){
rsize = size / (1024 * 1024);
unit = "Mbytes";
}else if(size >= 1024){
rsize = size / 1024;
unit = "Kbytes";
}else{
rsize = size;
unit = "bytes";
}
stringstream s;
s << rsize << " " << unit;
return s.str();
}

int main(int argc, char **argv)
{
myCLManager2 clm;
cl_mem mobj;
char *buf;
size_t size;
for(size = 256; size <= LONG_MAX; size<<=1 ){
buf = new char[size];
for(size_t i = 0; i < size; i++){
buf[i] = i % 100;
}
MYCLCREATE(mobj,
clCreateBuffer,
clm.context,
CL_MEM_READ_WRITE,
size * sizeof(char),
NULL);
clFlush(clm.queues[0]);
MYCLCHECK(clEnqueueWriteBuffer,
clm.queues[0],
mobj,
CL_TRUE,
0,
size * sizeof(char),
buf,
0, NULL, NULL);
clFlush(clm.queues[0]);
for(size_t i = 0; i < size; i++){
buf[i] = 0;
}
MYCLCHECK(clEnqueueReadBuffer,
clm.queues[0],
mobj,
CL_TRUE,
0,
size * sizeof(char),
buf,
0, NULL, NULL);
for(size_t i = 0; i < size; i++){
if(buf[i] != i % 100){
std::cout << "buffer contents don't match for"
<< sizeStr(size) << ".\n";
}
}
clFlush(clm.queues[0]);
std::cout << "mobj allocation of " << sizeStr(size) << " succeeded.\n";
MYCLCHECK(clReleaseMemObject, mobj);
delete[] buf;
}
return 0;
}

やっていることは極めて単純である。myCLManager2は自分で作成した、OpenCLのコンテクストやキュー、プログラムオブジェクトの管理を行うヘルパークラスであるが、本質的ではない。単に普通にコンテクスト、キューの生成を行っていると思っていただければいい。

MYCLCHECK、MYCLCREATEも以前のエントリに書いたが、単にエラー処理を組み込んだマクロである。OpenCLのAPIが正常終了しなかったときはexit()するようになっている。

流れとしては、サイズを倍々にしながら、
そのサイズ分のホストメモリを確保
適当な内容で初期化
同じサイズでメモリオブジェクトを作成
clEnqueueWriteBufferでホストメモリからメモリオブジェクトに書き込み
ホストメモリの内容をクリア
メモリオブジェクトから先ほど書き込んだ内容をホストメモリに読み出し
最初の内容と一致するかチェック

ということを繰り返している。もし自分の環境で似たようなことをやりたい人がいたら、メモリを食いつぶしてシステムに影響が出ないとも限らないので、先に重要なファイルはセーブするなり何なりした上で十分心してやってもらいたい。

でまあ、これをとりあえず実行してみるとこんなになった。

mobj allocation of 256 bytes succeeded.
mobj allocation of 512 bytes succeeded.
mobj allocation of 1 Kbytes succeeded.
mobj allocation of 2 Kbytes succeeded.
(途中略)
mobj allocation of 128 Mbytes succeeded.
mobj allocation of 256 Mbytes succeeded.
mobj allocation of 512 Mbytes succeeded.
test7.cpp: 55 in main(): clEnqueueWriteBuffer failed due to CL_MEM_OBJECT_ALLOCATION_FAILURE

というわけで、512MBまでは実際にメモリオブジェクトを確保、書き込み、読み出しを問題なく行うことができた。あくまで前述の255MBという値は、最低限保証されている値というわけである。この後もう少し細かくテストしてみるとちょっと思いがけないことも分かったのだが、それは別エントリに後で書こう。


追記:
遅ればせながらプログラムソースを見やすくするためにSyntaxHighlighterを導入させていただいた。導入にあたっては、Linux愛好者の独り言 fc2ブログでソースコードを綺麗に表示する。(SyntaxHighLighter 3.0)を参考にさせていただいた。ありがとうありがとう。

Ubuntu上でのEPSON PX-404Aのスキャナ設定

今まで数年間プリンタなしで済ませてきたのだが、お役所に書類を提出する必要上、久々にプリンタを購入することになってしまった。特に機能に対するこだわりもないので、廉価帯でインクが各色独立タイプのEPSON PX-404Aを購入した。

プリンタとしては、つないで電源を入れた瞬間に自動で認識され、ドライバも自動的にインストールされ、これといって何もする必要がなかった。一方で、スキャナ機能を利用するには若干の作業が必要だったので、例によって忘れてしまう前にメモしておくこととする。

まず、EPSONのサイトLINUX(TM)情報で紹介されている、avasys社のページをチェックしてみた。すると、2012年からはセイコーエプソンのダウンロードページを参照するようにとのことである。

ややたらい回しにされている感はあったが、このページから機種名にPX-404Aを指定してみると、プリンタドライバーが2件、スキャナードライバが1件表示される。プリンタの方はUbuntuで自動的にインストールが済んでいるので、スキャナードライバの方を選択する。すると、図のようなファイル一覧が表示された。

downloads1.png


私の使用しているのはAMDのCPUを使用した64bit環境なので、
iscan-data_1.14.0-1_all.deb iscan_2.28.1-3_amd64.deb userg_revP_j.pdf
の三つのファイルをダウンロードする。userg_revP_j.pdfがマニュアルなので、基本的にはこれにしたがってインストールすればいいのであるが、一点問題があった。コアパッケージ iscan_2.28.1-3_amd64.deb のインストールに当たって、"libltdl3"パッケージを要求されるのであるが、実際にUbuntu 11.04にインストールされているのはlibltdl7である。ここをごまかすために、まず

sudo dpkg --install iscan-data_1.14.0-1_all.deb (これは問題なく行える)
の次に、コアパッケージのインストールは次のように依存性を無視して強行する。
sudo dpkg --force-depends --install iscan_2.28.1-3_amd64.deb

しかるのちに、
cd /usr/lib
sudo ln -s libltdl.so.7 libltdl.so.3

とする。要するに、強引にlibltdl7をlibltdl3に見せかけてやるのである。ライブラリのAPIがバックワードコンパチブルでなくなっているとまずい可能性があるが、深く考えずにとりあえずやってみたところ、問題なくiscanユーティリティを起動することができるようになった。

iscan-ss1.png


また、同様にxsaneをgimp内から起動しての取り込みも問題なく行えるようになった。

sane-from-gimp1.png

強引にシンボリックリンクを張る代わりに、パッケージデータベースに対してlibltld7はlibltdl3を兼ねるということを教えてやればいい気がするが、それはまた後で調べることにしよう。

追記:
上のダウンロード画面のスクリーンショットをよく見てもらえば分かるが、実はlibltdl7対応のパッケージがすでに存在しているのであった。こちらをインストールすればそもそも上記のような問題は発生しない。あと、その後intel CPUを搭載した新しいPCにもインストールを行ったが、intel CPUの64ビット環境(アーキテクチャx86_64)でも、amd64用のパッケージで問題なくインストール、使用できることを確認した。i386パッケージは32ビット環境用であり、こちらは64ビットでは正常にインストールできず、強制的にインストールしても動作しない。
プロフィール

GM3D

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

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

この人とブロともになる

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