OpenCLここまで整理

最近このサーバが死ぬほど重い。。。リクエストを送っても返ってくるまで5秒以上かかる。キャッシュ使って早くしようとしたけどうまく設定できなく断念。また時間のあるときに設定しようと思う。

さてさて、10月ももう終わろうとしており、卒論を本格的に書き始めなければならない。とりあえず、OpenCLについてここまで整理、

現段階の目標は、複数枚のGPUを動かすこと。いろいろ試行錯誤した結果、当初僕がイメージしていた実装ではできないっぽいことが判明。OpenCLは、まず最初にコンテキストを作成しなければならないが、そのコンテキストに複数のデバイス情報を入れることでそのコンテキスト情報をカーネルに送ってマルチデバイスで計算するものだと思っていた。ただ実際はそうではなく、コンテキストがベンダーごとに作ってあげなければいけないらしい。device_idはベンダーごとのデバイスリストであるが、そのデバイスリストをコンテキストを作成するために必要なpropertiesに含めることができないみたい。
普通はこう書く。
[cpp]
cl_context_properties properties_gpu[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)id[1], 0};
[/cpp]
だけどぼくは
[cpp]
cl_context_properties properties_cpugpu[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)id[0],(cl_context_properties)id[1], 0};[/cpp]
[cpp]
cl_context_properties properties_cpugpu[] = {CL_CONTEXT_PLATFORM, (cl_context_properties)id[0],CL_CONTEXT_PLATFORM, (cl_context_properties)id[1], 0};[/cpp]
こんな感じでpropertiesに複数のデバイスCPUとGPUを含めることができると思ったができないっぽい。残念。

じゃあどのようにすればよいか。おそらく、二つのプラットホームを作らなければならない。CPUとGPUの二つのプラットフォームを作り、そのプラットフォームにそれぞれ処理を割り振って、カーネルプログラムで処理させるという流れになるだろう。そのためにはカーネルプログラム事態を二つに分けなければいけないのかや、カーネルプログラムのなかに二つのプラットホームを含めることができるのかも検証しなければならない。

でもおそらく、カーネルプログラムを実行する段階でデバイスリストを渡しているため、複数のデバイスでに自動的に処理が割り振られているかもしれない。ここもまだ検証中。

ここの問題を解消できれば、あとはライブラリ開発したり、割り振るアルゴリズムを考えるだけなので比較的簡単にできそう。はやく解決して12月中には、卒論の第一稿を提出したい。

トリプルブート

時間がないので超速で書く。
Windows8が発売されたこともあり、自分のノートパソコンのOSを入れ替えた。

windows8とLinuxMintとCentOS。
綺麗なブーストの仕方ではない気がするけど、一応動いているのでここまでの流れの備忘録。

最初にLinuxMintをインストール
次にCentOSインストール
Windows8インストール

基本的にはそのままだが、パーティションだけ分けた。
LinuxMint 30G
CentOS 30G
Windows8 200G
ハードディスク上のパーティションもまさしくこの順番。ブートローダ系の知識が全くなく、途中MintやCentが起動できなくなったけど「grub4dos」を利用するという方法にぶつかった。

普通はWindowsを最初にインストールしてWindowsのブートローダの上にLinuxのブートローダのGRUBに書き換えるとスムーズらしいけど、

Grub4dosを使うとWindowsのブートローダにGrub4dosのブートローダを読みこませることができる。
Grub4dosのブートローダを編集してあげてLinuxを呼ぼうって話。

今の状態
Windows8ブートローダ→「Windows8」「Linux」→「Linux」選択→Grub4dos起動→Linux「LinuxMint」「CentOS」

引っかかったのは、Windows8入れたあとに
●Grub4dos入れる
●Linuxのブートローダの「Menu.lst」編集

●Grub4dosを入れる
Grub4dosの入れ方はググればいろんなとこで出てくる。
1. grub4dosをダウンロードしたら、grldrとgrldr.mbrをC:にコピー。
2. Windowsのブートメニューをいじるためにはコマンドラインからでなければならないので、管理者モードでコマンドプロンプトを開く。
3. 次のコマンドでLinuxを追加。

$ bcdesit
$ bcdedit /create /d "Linux" /application bootsector
エントリ {xxxx} は正常に作成されました。
$ bcdedit /set {xxxx} device partition=C:
$ bcdedit /set {xxxx} path grldr.mbr
$ bcdedit /displayorder {xxxx} /addlast

こんな感じ。{xxxx}はめちゃ長いけど写す。

●Linuxのブートローダの「Menu.lst」編集
ぼくの場合、CentOSをあとから入れたのでLinuxのブートローダはCentOSにある。Grub4dosでLinuxに入ったら、CentOSに入り、Menu.lstを編集する。(Mint入れたあとにCent入れたら自動的にデュアルになると思ってたんだけど結局なってなくて、ここでその設定をした。ここではまだMintはでてなかった。)
ここで入れないと少しめんどくさい。

<Grub4dosからLinuxに入れなかった場合>
Windows8のブートローダでGrub4dosに入り、そこからLinuxを選択したいわけだが、入れなかった場合、Grub4dos起動したあとすぐに矢印キー連打してCommandモード的な奴に入り、直接MintかCentを起動する。
よく分かんないけどこんな感じでLinuxMintを起動できた。

grub> root (hd0,0)
Filesystem type is ext2fs, partition type 0x83
 
grub> kernel /boot/vmlinuz-3.2.0-23-generic root=/dev/sda1
[Linux-bzImage, Setup=0x4200, size=0x4b81d0]

grub> initrd /boot/initrd.img-3.2.0-23-generic
[Linux-inited @ 0x7f279000, 0xd85e6b bytes]

grub> boot

変えなくちゃいけないのは、最初のroot(hd0,0)と、kernelって売った行の最後のsda1ってとこ。hd0って言うのはハードディスクの構成で一枚目だからhd0、二枚目ならhd1的な。そんで(hd0,0)の二個目の0はパーティションの順番を指している。ぼくの場合一番最初にMintを入れたから0、次に入れたCentを指定するときには(hd0,1)を指定する。
次にsda1っていうのはよくわからんけど、一番最初は1始まりだから1らしい。
ぼくの場合、Mintはsda1, Centはsda2っぽい。
こんな感じでLinuxどっちでもいいけど起動する。ここまでがLinuxに入れなかった場合。

そんで次にやっと「Menu.lst」を編集する作業に入る。
LinuxのブートローダのMenuを編集するんだが、まずそのファイルがどこにあるかを探す。
ぼくの場合、CentOSの「/boot/grub/menu.lst」にあった。これをvimで編集した奴がこれ。

[root@s-kotake]# cat menu.lst
# grub.conf generated by anaconda
#
# Note that you do not have to rerun grub after making changes to this file
# NOTICE:  You do not have a /boot partition.  This means that
#          all kernel and initrd paths are relative to /, eg.
#          root (hd0,1)
#          kernel /boot/vmlinuz-version ro root=/dev/sda2
#          initrd /boot/initrd-[generic-]version.img
#boot=/dev/sda
default=0
timeout=5
splashimage=(hd0,1)/boot/grub/splash.xpm.gz
hiddenmenu
title Linux Mint
        root (hd0,0)
        kernel /boot/vmlinuz-3.2.0-23-generic ro root=/dev/sda1
        initrd /boot/initrd.img-3.2.0-23-generic

title CentOS (2.6.32-279.11.1.el6.x86_64)
        root (hd0,1)
        kernel /boot/vmlinuz-2.6.32-279.11.1.el6.x86_64 ro root=UUID=79c217e1-c0f2-4d85-b942-561fd6cef379 rd_NO_LUKS  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_MD crashkernel=128M LANG=ja_JP.UTF-8 rd_NO_LVM rd_NO_DM rhgb
        initrd /boot/initramfs-2.6.32-279.11.1.el6.x86_64.img

title CentOS (2.6.32-279.el6.x86_64)
        root (hd0,1)
        kernel /boot/vmlinuz-2.6.32-279.el6.x86_64 ro root=UUID=79c217e1-c0f2-4d85-b942-561fd6cef379 rd_NO_LUKS  KEYBOARDTYPE=pc KEYTABLE=us rd_NO_MD crashkernel=128M LANG=ja_JP.UTF-8 rd_NO_LVM rd_NO_DM rhgb
        initrd /boot/initramfs-2.6.32-279.el6.x86_64.img

って感じ。今のところこれで何とかトリプルブートができてる良い感じ良い感じ。

Windows8のブートのところがおしゃれ。

卒論の方向性について

おおよそ論文の方向性がやっと明確になってきた。

 最終的な目的は「環境を考慮したOpenCLの負荷分散手法」である。ただこれもどの計算を対象にするか、それともとりあえず汎用的にどのプログラムでもそれなりの負荷分散できるような方法を提案するかを考える必要がある。
 そしてその過程として、「OpenCLの環境の情報をとってきて誰もが簡単に使えるライブラリ開発」を一つの目的にする。これはGPUが複数枚刺さっている環境、またCPUとGPUを開発者がOpenMPのようにコメント一つで簡単に使えるライブラリを開発するということ。
 今自分がやることは、複数枚GPUが刺さっている環境でOpenCLを動かすこと。そして動かせたら、それを引数を与えるだけで使える関数を作る。次にライブラリ化を考える。

今月中には4枚のGPUで動かせるようにしたい。そこまでいけばおそらくライブラリ作成はさぼど難しくはないず。
できるだけ12月中には論文を完成させたい。

OpenCLのマンデルブロ集合(1次元配列版)

[cpp]
#include<iostream>
#include<cstdio>
#include<cstdlib>
#include<CL/cl.h>
#include<string>
#include<time.h>

#define MAX_SOURCE_SIZE (0x100000)
#define MEM_SIZE (CL_DEVICE_MAX_WORK_ITEM_SIZES)

using namespace std;

//test
int main()
{
//char string[MEM_SIZE];

FILE *fp;
char fileName[] = "./calc.cl";
char *source_str;
size_t source_size;

fp = fopen(fileName, "r");
if(!fp)
{
cout << "file failed" <<endl;
exit(1);
}
source_str = (char *)malloc(MAX_SOURCE_SIZE);
source_size = fread(source_str,1,MAX_SOURCE_SIZE,fp);
fclose(fp);

cl_int status;
cl_platform_id platforms[2];
cl_uint num_platforms;
status = clGetPlatformIDs(2, platforms, &num_platforms);
//cout << status << endl;

cl_device_id devices;
cl_uint num_devices;
cl_context context;
status = clGetDeviceIDs(platforms[1], CL_DEVICE_TYPE_GPU, 4, &devices, &num_devices);
//cout << status << endl;

context = clCreateContext(NULL, 4, &devices, NULL, NULL, &status);
//cout << status << endl;

cl_command_queue queue;
queue = clCreateCommandQueue(context, devices, 0, &status);
//cout <<"queue:"<<status << endl;

cl_program program;
program = clCreateProgramWithSource(context, 1, (const char**)&source_str, &source_size, &status);
//cout << status << endl;

status = clBuildProgram(program, num_devices, &devices, NULL, NULL, NULL);
cout << status << endl;

//*////////////////////////

size_t logsize;
status = clGetProgramBuildInfo(program,devices,CL_PROGRAM_BUILD_LOG,0,NULL,&logsize);
if(status == CL_SUCCESS)
{
char *logbuffer;
logbuffer = new char[logsize + 1];
if(logbuffer == NULL)
{
cout << "memory allocation failed."<<endl;
//return;
}

status = clGetProgramBuildInfo(program,devices,CL_PROGRAM_BUILD_LOG,logsize,logbuffer,NULL);
if(status == CL_SUCCESS)
{
logbuffer[logsize]=’\0’;
cout << "build log" << endl;
cout << logbuffer << endl;
}
delete [] logbuffer;
}

//*////////////////////////

cl_kernel kernel;
kernel = clCreateKernel(program, "calc", &status);
//cout << status << endl;

float Outx[MEM_SIZE];
float Outy[MEM_SIZE];
//bool OutCheck[MEM_SIZE][MEM_SIZE];
bool *OutCheck = new bool[MEM_SIZE*MEM_SIZE];
Outx[0] = -2;
Outy[0] = -2;
for(int i= 0; i < MEM_SIZE-1 ;i++)
{
Outx[i+1] = Outx[i] + (4.0 / MEM_SIZE);
Outy[i+1] = Outy[i] + (4.0 / MEM_SIZE);

for(int j = 0; j < MEM_SIZE ;j++)
{
OutCheck[i*MEM_SIZE+j] = true;
}
}

cl_mem memoutx, memouty, memoutcheck;
memoutx = clCreateBuffer(context, CL_MEM_READ_WRITE, MEM_SIZE * sizeof(float), NULL, &status);
//cout <<status<<endl;
memouty = clCreateBuffer(context, CL_MEM_READ_WRITE, MEM_SIZE * sizeof(float), NULL, &status);
//cout <<status<<endl;
memoutcheck = clCreateBuffer(context, CL_MEM_READ_WRITE, MEM_SIZE * MEM_SIZE * sizeof(bool), NULL, &status);
//cout <<status<<endl;

status = clEnqueueWriteBuffer( queue, memoutx, CL_TRUE, 0, MEM_SIZE * sizeof( float ), Outx, 0, NULL, NULL );
//cout << status << endl;
status = clEnqueueWriteBuffer( queue, memouty, CL_TRUE, 0, MEM_SIZE * sizeof( float ), Outy, 0, NULL, NULL );
//cout << status << endl;
status = clEnqueueWriteBuffer( queue, memoutcheck, CL_TRUE, 0, MEM_SIZE * MEM_SIZE * sizeof( bool ), OutCheck, 0, NULL, NULL );
//cout << status << endl;

status = clSetKernelArg(kernel, 0,sizeof(cl_mem),(void *)&memoutx);
status = clSetKernelArg(kernel, 1,sizeof(cl_mem),(void *)&memouty);
status = clSetKernelArg(kernel, 2,sizeof(cl_mem),(void *)&memoutcheck);
//cout <<"カーネルに渡す引数セット:"<<status<<endl;

//status = clEnqueueTask(queue, kernel, 0, NULL, NULL);
size_t global_work_size[] = {MEM_SIZE * MEM_SIZE};
size_t local_work_size[] = {MEM_SIZE};

clock_t t1, t2;
t1 = clock();
status = clEnqueueNDRangeKernel(queue, kernel, 1, local_work_size, global_work_size, NULL, 0, NULL, NULL);
t2 = clock();
if( status < 0){
cout <<"実行:"<<status<<endl;
cout << CL_DEVICE_MAX_WORK_GROUP_SIZE << endl;
cout << CL_DEVICE_MAX_WORK_ITEM_SIZES << endl;
exit(0);
}

//status = clEnqueueReadBuffer(queue, memoutx, CL_TRUE, 0, MEM_SIZE * sizeof(float), Out, 0, NULL, NULL);
//status = clEnqueueReadBuffer(queue, memouty, CL_TRUE, 0, MEM_SIZE * sizeof(float), Out, 0, NULL, NULL);
status = clEnqueueReadBuffer(queue, memoutcheck, CL_TRUE, 0, MEM_SIZE * MEM_SIZE * sizeof(bool), OutCheck, 0, NULL, NULL);
//cout <<"結果取り出し:"<<status<<endl;

for(int i = 0;i < MEM_SIZE; i++)
{
for(int j = 0;j < MEM_SIZE; j++)
{
if(OutCheck[i*MEM_SIZE+j] == true){
cout << Outx[i]<< " " << Outy[j] << endl;
}
}
}

//cout <<"time = "<< (double)(t2 – t1) / CLOCKS_PER_SEC << endl;

delete OutCheck;

clReleaseMemObject(memoutx);
clReleaseMemObject(memouty);
clReleaseMemObject(memoutcheck);
clReleaseKernel(kernel);
clReleaseProgram(program);
clReleaseCommandQueue(queue);
clReleaseContext(context);
}
[/cpp]

[cpp]
//#define MEM_SIZE (2000)

__kernel void
calc(__global float *outx,
__global float *outy,
__global bool *outcheck )
{

#define MaxCalcNum 10000

//int width = 2, height = 2, j = 0;
//int out[MEM_SIZE*MEM_SIZE];

#pragma OPENCL EXTENSION cl_khr_fp64:enable
//int index_x = get_global_id(0);
//int index_y = get_global_id(1);
int index_x = get_group_id(0);
int index_y = get_local_id(0);

//if(index_x > MEM_SIZE)index_x = 1;
//if(index_y > MEM_SIZE)index_y = 1;
double prevX = 0.0f;
double prevY = 0.0f;

for(int i = 0; i < MaxCalcNum ; i++)
{
double newX = (prevX * prevX) – (prevY * prevY) + outx[index_x];
double newY = 2.0f * prevX * prevY + outy[index_y];

if ((newX * newX) + (newY * newY) > 4.0f)
{
outcheck[index_x * get_local_size(0) + index_y] = false;
break;
}
else
{
prevX = newX;
prevY = newY;
}
}
}
[/cpp]

考える倫理観

人はなぜ考えるのか。
成長するため?
自分の身を守るため?
幸せになるため?
何かを守るため?

人はなぜ自分の意見を主張したがるのか。
共感して欲しいから。

自分の考えを通すということは、良いことか否か。

その考えがすべての事象を包括的に考え、すべての人幸せになる意見だとしても、押し通すことは良くないかもしれない。
その考えのすべてを理解してくれる人なんてごく僅かなわけで、必ずしも正しいという保証はない。

多くの人はそれを「わがまま」だと思うだろう。

ならば、自分の意見を主張しないほうが良いのかもしれない。

この世の中の意見はきっとどれが正しくてどれが間違っているっていう明確な基準なんてない。しいて言うならば法律ぐらい。でもやはり基準っていうのは倫理観というものである程度定まっていて、多くの人は持っているわけでその倫理観という見方の中で物事を「これは正しい、これは間違っている」という判断を下している。

そしてもし他人が間違っていると思えば自分の倫理観に従い、注意なり叱ったりしてその行為を正すものなんだろう。

多くの人が共通して持っている倫理観に、逆らうならばそれはそれは多くの非難を浴びる。もし仮に自分が正しかったとしても全世界の人が持っている共通の価値観を変えることは当たり前だができない。

そんなことは起こり得ないというだろう。大抵の場合、多くの人が思っていることは正しいと。

ただ仮に、万が一そんな状況になったら自分はどうするか。
どう考えてみても、自分は間違っているとは思えない。でも多くの人が思うのならそうなんかな。自分がおかしいんだ。「当たり前のことを当たり前と思うことが正義だと思うことにしよう」って本気でそう思うことができるんだろうか。

そんな自分の中の疑問を表に出さず、気づかないふりをして生活する。そのうちそんな疑問を感じていたことなんて感じなくなっちゃって、人に流されてなんとなく楽しい人生って思って死んでゆくんじゃないかと思うとなんかすごく切ない。

正解なんてないと割りきって生きるのが正解?
正解なんてあるのかよと言いつつも探していく人生のほうが良かったんじゃないのか。

生きる意味っていうのは難しい。生きる意味っていうのは、裏を返すと死なない意味になると自分は思うのだが、どうなんだろうか。迷惑をかけないならば死んでもいいのか。

最近、善悪の判断ができてない。

何が正しくて、何が悪いことなのか。非常に危ない。
もう少し冷静になれ。