卒論提出まであと少し

一応卒論の提出日が12月15日に決まった。それまでにある程度卒論を完成させなければならない。毎日2ページくらいのペースで書かないと間に合わない。。。

まだ全部実験も終わってない状態で書き始めてる。

これからすること。(ここから思考垂れ流し)
APIまで完成しているので、あとは最適化するだけ。ただこれが一番のメイン。どんな用途向けに最適化するか。
あくまでもだれでも簡単に使えるOpenCLが目的。ちょっと前にクロノスから発表されたCLUの考え方にに非常に近くなってしまうがしょうがない。

プロトタイプ向けに手軽にOpenCLを使えるためライブラリ開発、そして最適化は実行時間予測するために行う。これによってCPUとGPUのリソースどちらをどれだけ使ったほうがそのプログラムは高速化するかの予測ができることは実用性にもつながるはず、、、

今からやらなければならないことを整理すると、どんなプログラムも使えるような汎用的な最適化プログラムを組む。具体的には、CPUとGPUのプロセッサの特徴をブラシュアップして、この処理はCPUに、この処理はGPUというように分ける。それを.clファイルに対して考えなければならない。

ん?.clファイルに対してどのようにプログラミングすれば良いんだ?
ホストプログラムの処理はもちろんCPUが行う。この処理をGPUに回すことはない。となると最適化を考えた場合やはり.clファイルに対してリソース分配を考えなければならない。またマルチGPUについても考えなければならない。CPUとGPUの特徴の違いをまだ全部解析できていないが、例えば、CPUにはif文の処理、GPUにはfor文の処理というようにソースを読み込んだ段階でプログラムオブジェクトを分ける。そんなことをが果たして出来るのか?これは利用者がヘテロジニアスな環境を使う前提での研究だが、とりあえず家中に転がっていたCPUやらGPUを一つのパソコンに突っ込んでヘテロジニアスな環境ができたみたいな状況で、このパソコンでプログラムを動かしたい!みたいな。そのとき、「ほら、ぼくの研究役立つでしょ?」みたいな感じ。

そんでまずぼくのプログラムがするべきことっていうのは、まず環境情報をとってくる。どんなデバイスがあるのか。それがもし、CPUだけだったりしたら問題ない?いやそんなことない。OpenMPみたいにスレッドをたくさん立てて並列化するべきだ。またGPUが刺さっていたらもちろんCPUとGPUの並列化になるんだけど、一番難しいのはおそらくここのリソースの分け方。.clファイルを全部GPUに投げちゃえば楽なんだけどそれじゃ意味が無い。ここをさっき言ってたifとかforの関数ごとに分ける。本当にそんなんできんの?できて、CPUだけとGPUだけ処理してどっちが早いぐらいじゃねとも思う。もちろん変数なんて共有しているわけだからそれを別々のデバイスで計算したらわけわからんことになる気がする。そう考えると.clファイルの処理を分けるなんて無理じゃね?てか意味なくね?うーん。。。そんな複雑なプログラムなんてサンプルを考えるだけでも大変な気がする。んー例えばゲーム開発を想定して、分岐をCPU、グラフィック処理をGPUに振り分けるように、、、いやそれだったらホストプログラムの方で処理すればよいだけだ。そもそもCPUだけだったら.clファイルを作成する意味ってなくね?ホストプログラムで処理すればいいんだもん。。。先輩のCUDAでのやつもホストプログラムで処理させるようにしてるんだっけか????そうすると、ソースプログラムを読み込んだ段階でCPUとGPUで処理させてやって早く終わった方だけ結果返して表示させればいいんじゃね?でもそれってリソース分配になってない。。。

.clファイルが一つだけだと思っているからダメなのかもしれない。複数の.clファイルがあったとしてその.clファイルごとに「この.clファイルはCPUで処理するべきだ!この.clファイルはGPUで処理するべきだ!」ってなれば十分研究の価値はあるはず。.clファイルがいくつもあるプログラムってあるよね?.clファイルって結局計算してホストプログラムに結果返すんだから、.clファイルは十分機能ごとにわける可能性はあるよね。。。

今人に聞いたら単純に並列化すれば良いって話になった。適材適所って言うことを考えるんじゃなくて、少しでも高速化すれば良い。GPUがしてる処理をほんの少しでもCPUが負担してあげればそれはちゃんとした研究になる。そのためには、入力データを単純に分割する。その割合は少し考えなければいけない。ん?でもその割合って計算量によって変わる?そこらへんも考えなくちゃいけない。最初は単純にデータ量をデバイス数で割って割り当てれば良いか。

とりあえず今やらなければならないこと。CPUとGPUの並列化をできるようにする。非同期コマンドキュー。タスク並列化かデータ並列どっちが良いか考え、データの割り方を考える。色々試してみて、簡単でもいいから早いアルゴリズムを構築する。以上。

ヾ(*`Д´*)ノ”

本当にその人のことを思うならば理論

どうもぼくです。

なんか最近地味にいろんな人にコタシンドットコムが見られている気がする。これはそもそも勉強の一環で立てたサーバーで、脆弱なのであまりアクセスはしないでください、、、(・ω・;A)フキフキ

今日の話題は、本当にその人のことを想うならば~理論は正しかった。
久しくそんな自己啓発本も読んでるわけでもないし就活してるわけでもないので成長とはなんぞやと考えていなかったんですが、たまたま感動した言葉を見つけてしまったので、書きます。

=====================================================
『掃除道』鍵山 秀三郎

一つ目の幸せは、してもらう幸せです。

二つ目の幸せは、自分でできるようになった幸せです。

三つ目の幸せは、人にしてあげる幸せです。

人がしてほしいことをしてあげれば喜ばれます。そんな人は頼りにされます。人にものを差し上げる、自分の身体と時間を使って何かをして差し上げる、相手の喜びをわが喜びとする。この幸せを大事にしていただきたいのです。

「してあげる幸せ」は三つの幸せの中でも最高の幸せです。
=====================================================

らしい。昔見たことあるような言葉だけど久々に見て『確かに、』と思った。

世の中にはそんな悪い人はいないし、大抵のことは自分のためにやってもらったことはなんでも嬉しい。でも、そっから自分でできたらもっと嬉しい。でさらにそれを人にやってあげたらもっと嬉しいよねって話。

好奇心を持つってことともリンクしてると思うんだけど、その最終地点はやっぱり人のためなんだって思った。

そんで本当にその人のことを思うならば~理論はやっぱり正しかったよねって話に繋がるんだけど

これは、ぼくがよく言う言葉で『本当にその人のことを思うならば、やっぱり正直に言うべきなんだって』みたいな。主に恋愛系でいう。

なにかアクションを起こそうと思っても、いろいろ考えるとなにもかも不安要素に思えて結局現状維持って結論に達してしまいがちになる。
でもその時に、自分がしなければならないことは不安になることではなく、本当にその人のためになることを考えてアクションすることが重要。

ぼくがよくする話で『恋愛ゲーム理論』って言うのがあるんだけど、現実の恋愛も結局ゲームと同じように動けってやつ。

例えば、ラブプラスで寧々さんを落としたいと思ったら放課後は寧々さんがアルバイトしてるファーストフード店に通うだろって。寧々さんいないと思っても少しでも好感度あげるために毎日毎日ファーストフード店の選択し選ぶでしょって。
でも現実は、そんなことしない。毎日行ったらストーカーだと思われるとかキモいとかしつこいとかマイナスのことばかり考えて結局行動しない。でも本当にその人のことが好きで、自分が世界で一番その人のことを大切にできる自信があるならば、玉砕覚悟で何回もアタックするべきなんだって。そこは動いてから考える理論だろって。

つまり、やっぱり自分がしてもらって嬉しいことはしてあげるべき。それが相手のためにできる自分にとっての最良の手段。
それで嫌われたらその時に考えれば良い。

きっとこれは恋愛以外でも、人間関係を築く上で大切。もっと言うと人間のコミュニティ存在し続ける限り自分にできる最良の関わり方なんじゃないかと思った。

ビジネスだってきっとそうで、人のためになることを率先してやってあげる。まず自分からギブすることが大切。

本当に自分の身の回りの人全員に対して『本当にこの人をことを想ったら~』って出来たら最高に幸せになれるのかも。

世の中の真理はこのひとつだけだ!みたいになってすんごい胡散臭いブログになってしまった気がするけど、純粋に今そう思ったので良い。

途中死ぬほどたばこ吸いたくなったけど、まだ禁煙続いてるので我慢してもうねます。

割れなかったことに意味がある。

大切なものほど使わずにとっておく。使わなければ傷もつかずいつまでも綺麗なままだ。
でもそれでは愛着がわかない。大切なものは大切であるがゆえに壊れないように丁寧に扱ってしまう。
そして適当に扱っているものほど意外と長く持ったりする。
大切なものを大切じゃないなんて思うことはできないし、大切なものを適当に扱うこともできない。いわゆるジレンマってやつ。

本当に本当に大切なものならば身近において長く使い続けることが大切なんだろう。自分はそれが苦手。

大切なものを全く傷つけないように使うことなんてできやしない。ある程度傷つくことは割りきって付き合うしかないんだろう。
毎日丁寧に手入れするだけのマメさが自分にあるんだろうか。

『割れなかったことに意味がある。』(『ツレがうつになりまして』より引用)
もしかしたら壊してしまうのは自分かもしれない。そのために自分をコントロールしないといけない。

小学生にありがちな好きだからいたずらしちゃうって言うのは意外とより良い人間関係を築く上で良いのかもしれない。
バランスをとらなければ!!!

OpenCLの行列和のC++化

OpenCLのライブラリを作成する前段階として、昔書いたOpenCLの行列和のプログラムのC++化を行った。まだ完全ではないがとりあえず動くのであげておく。

[cpp]
/*
* main.h
*
*/

#ifndef MAIN_H_
#define MAIN_H_

#include<iostream>
#include<cstdlib>
#include<string>
#include<cstdio>
#include"clapi.h"

#ifdef __APPLE__
#include<OpenCL/opencl.h>
#else
#include<CL/cl.h>
#endif //__APPLE

#define MAX_SOURCE_SIZE (0x100000)

#endif /* MAIN_H_ */
[/cpp]

[cpp]
/*
* main.cpp
*
*/
#include"main.h"

using namespace std;
int main(){
int fvar=4096, fwid=4096, lvar=4096, lwid=4096;
double *mtrx1,*mtrx2,*Out;
FILE *fp;
if ((fp = fopen("inputD.txt","r")) == NULL)
{
printf("file open error!!n");
exit(EXIT_FAILURE);
}
int size;
fscanf(fp, "%d", &size);

mtrx1 = (double*) malloc(size * size * sizeof(double));
mtrx2 = (double*) malloc(size * size * sizeof(double));
Out = (double*) malloc(size * size * sizeof(double));

if(mtrx1==NULL) return 1;
if(mtrx2==NULL) return 1;
if(Out==NULL) return 1;

int i, j;
for(i = 0; i < size; i++)
{
for(j = 0; j < size; j++)
{
Out[i * size + j] = 0;
fscanf(fp, "%lf", &mtrx1[i * size + j]);
}
}

for(i = 0; i < size; i++)
{
for(j = 0; j < size; j++)
{
fscanf(fp,"%lf", &mtrx2[i * size + j]);
}
}

fclose(fp);

//kotake
//1.カーネルプログラム指定
string filename="test.cl";
//2.オブジェクト生成???
clapi cl(filename);
//3.メンバ関数実行
//cl.auto(入力数, データ1のdouble型配列の個数, データ1の配列のアドレス, データ2の配列の個数, データ2の配列のアドレス, ….)

if(!cl.clauto(2, size*size, mtrx1, size*size, mtrx2))return -1;
//cl.doOpenCL();
// iif(cl.hikisu != true || doOpenCL != true) return 0;

Out = cl.getOut();

//結果表示
cout<<"加算結果"<<endl;
for(int i = 0 ; i < fvar ; i++){
for(int j = 0 ; j < fwid ; j++){
cout<< Out[i*fwid+j] << " " ;
}
cout << endl;
}

free(mtrx1);
free(mtrx2);
free(Out);
}
[/cpp]

[cpp]
/*
* clapi.h
*
*/

#ifndef CLAPI_H_
#define CLAPI_H_

#ifdef __APPLE__
#include<OpenCL/opencl.h>
#else
#include<CL/cl.h>
#endif //__APPLE

#include<stdarg.h>
#include<string>
#include<cstdio>
#include<iostream>

#define MAX_SOURCE_SIZE (0x100000)

using namespace std;

class clapi {
public:
clapi();
clapi(string);
~clapi();
bool clauto(int , …);
bool doOpenCL();
double* getOut();
cl_device_id device_list[4];
private:
void builderr();
cl_int status;
cl_platform_id platforms[2];
cl_uint num_platforms;
cl_context context;
//cl_device_id device_list[4]; //なぜか2じゃできない
cl_uint num_device;
cl_context_properties properties[3];
cl_command_queue queue;
cl_program program;
cl_uint pro_info;
cl_kernel kernel;
cl_mem memIn[10];
cl_mem memOut;
string filename;
double* s[10];
int size[10];
int num_hikisu;
double* Out;
};

#endif /* CLAPI_H_ */
[/cpp]

[cpp]
/*
* clapi.cpp
*
*/

#include "clapi.h"

using namespace std;

clapi::clapi() {

}

clapi::clapi(string tmp){
filename = tmp;
}

clapi::~clapi(){
clReleaseMemObject(memOut);
for(int i = 0; i < num_hikisu; i++) clReleaseMemObject(memIn[i]);
clReleaseKernel(kernel);
clReleaseProgram(program);
clReleaseCommandQueue(queue);
clReleaseContext(context);
}

bool clapi::clauto(int n, …) {
num_hikisu = n;
va_list args;
va_start(args, n);

for (int t = 0; t < n ; t++) { //forでループさせ渡された引数1個ずつについて処理する。すべてsに+=していく。
size[t] = va_arg(args,int);//double型配列の個数
s[t] = va_arg(args,double*); //可変長引数を取り出す. 第一引数はva_list型の変数。第二引数には取り出す型。
}
va_end(args);

doOpenCL();

return true;
}

bool clapi::doOpenCL() {
status = clGetPlatformIDs(2, platforms, &num_platforms);
if (status != CL_SUCCESS || num_platforms <= 0) {
fprintf(stdout, "clGetPlatformIDs failed.n");
printf("%dn", status);
return false;
}
// 最初の要素として返されたプラットフォームIDを、プロパティにセットする
properties[0] = CL_CONTEXT_PLATFORM;
properties[1] = (cl_context_properties)platforms[1];
properties[2] = 0;

//1.デバイスの取得
status = clGetDeviceIDs(platforms[1], CL_DEVICE_TYPE_GPU, 4, &device_list[0], &num_device);
if (status != CL_SUCCESS || num_device <= 0) {
fprintf(stdout, "clGetDeviceIDs failed.n");
printf("%dn", status);
return false;
}

context = clCreateContext(properties, num_device, &device_list[0], NULL,NULL, &status);
if (status != CL_SUCCESS) {
cout << "clCreateContext failednError Code: " << status << endl;
return false;
}

//3.コマンドキューの作成
queue = clCreateCommandQueue(context, device_list[0], 0, &status);
if (status != CL_SUCCESS) {
cout << "clCreateCommandQueue failednError Code: " << status << endl;
return false;
}
//4.プログラムオブジェクトの作成
FILE *fp;
size_t source_size;
char *source_str;

fp = fopen(filename.c_str(), "r");
if (!fp) {
fprintf(stderr, "Failed to leas kernel.n");
return false;
}
source_str = (char *) malloc(MAX_SOURCE_SIZE);
source_size = fread(source_str, 1, MAX_SOURCE_SIZE, fp);
fclose(fp);

program = clCreateProgramWithSource(context, 1, (const char**) &(source_str), &source_size, &status);
if (status != CL_SUCCESS) {
cout << "clCreateProgramWithSource failednError Code: " << status << endl;
return false;
}

//5.プログラムのビルド
status = clBuildProgram(program, num_device, &device_list[0], NULL, NULL, NULL);
if (status != CL_SUCCESS) {
cout << "clBuildProgram failed nError Code: "<< status << endl;
builderr();
return false;
}

//6.カーネルの作成
kernel = clCreateKernel(program, "calc", &status);
if (status != CL_SUCCESS) {
cout << "clCreateKernel failednError Code: " << status << endl;
return false;
}

//7メモリオブジェクトの作成
for(int i = 0 ; i<num_hikisu ; i++)
{
memIn[i] = clCreateBuffer(context, CL_MEM_READ_ONLY | CL_MEM_COPY_HOST_PTR, sizeof(double)*size[i], (void*) s[i], &status);
if (status != CL_SUCCESS) {
cout << "clCreateBuffer failednError Code: " << status << endl;
return false;
}

}
memOut = clCreateBuffer(context, CL_MEM_WRITE_ONLY, sizeof(double)*size[1] , NULL, &status);
if (status != CL_SUCCESS) {
cout << "clCreateBuffer failednError Code: " << status << endl;
return false;
}

//8.カーネルに渡す引数の設定
status = clSetKernelArg(kernel, 2, sizeof(cl_mem), (void *) &memOut);
if (status != CL_SUCCESS) {
cout << "clSetKernelArg failednError Code: " << status << endl;
return false;
}

for(int i = 0; i< num_hikisu; i++)
{
status = clSetKernelArg(kernel,i,sizeof(cl_mem),(void *) &memIn[i]);
if (status != CL_SUCCESS) {
cout << "clSetKernelArg failednError Code: " << status << endl;
return false;
}
}

//9.カーネルの実行
size_t globalsize[] = { size[0] };
status = clEnqueueNDRangeKernel(queue, kernel, 1, NULL, globalsize, NULL, 0, NULL, NULL);
if (status != CL_SUCCESS) {
cout << "clEnqueueNDRangeKernel failednError Code: " << status << endl;
return false;
}

//10.結果の取得
Out = (double*) malloc(size[0] * sizeof(double));
status = clEnqueueReadBuffer(queue, memOut, CL_TRUE, 0, sizeof(double)*size[1], Out, 0, NULL, NULL);
if (status != CL_SUCCESS) {
cout << "clEnqueueReadBuffer failednError Code: " << status << endl;
return false;
}

return true;
}

double* clapi::getOut(){
return Out;
}

void clapi::builderr() {
size_t logsize;
status = clGetProgramBuildInfo(program, device_list[0], CL_PROGRAM_BUILD_LOG, 0, NULL, &logsize);
if (status == CL_SUCCESS) {
//ログを格納するためのバッファをアロケートする
char *logbuffer;
logbuffer = new char[logsize + 1];
if (logbuffer == NULL) {
printf("memory allocation failed.n");
return;
}

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

[cpp]
/*
* test.cl
*
*/

#pragma OPENCL EXTENSION cl_khr_fp64: enable

__kernel void
calc(
__global const double *in1,
__global const double *in2,
__global double *out)

{
int index = get_global_id(0);
out[index] = in1[index]+in2[index];
}
[/cpp]

ライブラリ作成

ライブラリ作成途中経過。とりあえずやり方だけ。

$ g++ -g -Wall -fPIC -c test.c
$ g++ -shared -o lib.so test.o
$ g++ -o main main.c ./lib.so
$ ./main

以下オプションの説明。

-Wall
すべての警告を出す

-fPIC
このオプションがターゲットマシンでサポートされていれば、位置独立なコードを出力します。このオプションはダイナミックリンクに適しており、分岐において大きなディスプレースメントを要求する場合にも適応します

-shared
他のオブジェクトとリンクして実行可能プログラムを形成し得る共有オブジェクトを生成します。ごく少数のシステムでのみ、このオプションはサポートされています。

libxxx.so(xxxがライブラリ名)
共有ファイルとは、プログラムが実行するときに呼び出す。そのため、静的ライブラリに比べ実行速度が遅くなる。しかし、その反面、以下のようなメリット、特長がある。

OpenCLで使い時は、1行目と3行目に-lOpenCLのオプションを追加する。

なんか色々書いたけど上のやり方じゃなくても、コンちゃんいわくそんな面倒いことしなくても1行でいけるらしい。。。

$ g++ test.c main.c -o main -lOpenCL
$ ./main

~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
そんで、Makefileも少し勉強して簡単なやつだけど作ってみた。

main: main.cpp lib.so
      g++ -o main main.cpp ./lib.so -lOpenCL
lib.so: api.o
        g++ -shared -o lib.so api.o
api.o: api.cpp
       g++ -g -Wall -fPIC -c api.cpp -lOpenCL
clean:; rm main *.o *.lib

参考URL
http://blog.livedoor.jp/ha_yshr/archives/51793675.html
http://blog.livedoor.jp/vavacco/archives/665039.html
http://www.am.sanken.osaka-u.ac.jp/~mukaigaw/misc/Makefile.html

禁煙っすよ、禁煙。

禁煙はじめました。今日から。あんだけ禁煙しないと神に誓っていたのになぜこの卒論に追われてストレスが溜まる時期に禁煙か。
ようは彼女ができたって話なんですが。

話が変わりますが、今日は何年ぶりかって言うくらい久々に説教を受けた。しかも年下に。本当にトンカチで頭を強くぶたれたような感覚だった。何も言えねぇっす。なんかこのブログでもそうだけど自分語ってんなと。何語ってんだと。そんな自分が恥ずかしくなった。自分なんてまだまだどころか、最下層だった。自分の甘さ、弱さ、決意が足りなさを強く感じた。てかこんなこと言える立場でも何でもない。世の中の人間は当たり前のことを当たり前にやっているのに、自分は何かと理由を付けて文句いっているクソガキだって。わかっているようでわかってない。そこは常に考えなくてはいけない。傲慢だった。深く反省した。

そして本当に自分は彼女に不釣り合いだと感じる。人間として完成してる訳がない。こんなしょーもないことをぐだぐだぐだぐだ書いてしまうしょーもない人間。今回は自信がなくなっているというよりここ数年でわずかに感じていた自分のだめな部分を本当に突かれてただただショック。憂鬱死するレベル。

まぁそんなこともありつつ、自分自身に対する戒めとタバコなんて自分は吸えるほどの人間じゃないっていうか、本当に愛する人が出来たならその人が自分に抱く不安要素はなくすように努力することが普通だろって思う訳で、かっこつけてるでも何でもなく純粋にそう思って禁煙。

決意は固い。