OpenCLでマンデルブロ集合を書いてみた

元ネタ http://codezine.jp/article/detail/5439
この記事ではRadeonだが、僕のノートについているのはGeForceM8400GSなので、nVidiaの開発者ページから、
GPU Computing SDK code samples and more
を落としてきてインストール
ドライバはCUDA対応のものが必要だが、最近は標準でCUDA対応になっているようなので割愛。
VisualC++2010Expressをインストール。
includeパスと、ライブラリパスを加えて、以下のプログラムをコンパイルした。

#include 
#include 
#include 

#define __CL_ENABLE_EXCEPTIONS
#include 

using namespace std;

int main(){
  try{
    vector platforms;
    cl::Platform::get(&platforms);
    cl::Platform platform = platforms[0];
    cl_context_properties prop[3] = {CL_CONTEXT_PLATFORM,reinterpret_cast(platform()),0 };
    cl::Context  context(CL_DEVICE_TYPE_GPU, prop);
    vector devices = 
      context.getInfo();
    cl::Device device = devices[0];
    cl::CommandQueue queue(context,device);
    cl::Buffer buffer(context,CL_MEM_READ_ONLY, (cl_int)(sizeof(int)*128*128));

    static string src_str = 
"int calculate(const float x, const float y, const int limit) {     \n"
"    int   count = 0;                                               \n"
"    float prevX = 0.0f;                                            \n"
"    float prevY = 0.0f;                                            \n"
"    for (int i = 0; i < limit; i++) {                              \n"
"        count = i + 1;                                             \n"
"        float newX = (prevX * prevX) - (prevY * prevY) + x;        \n"
"        float newY = (2.0f * prevX * prevY) + y;                   \n"
"        if (((newX * newX) + (newY * newY)) > 4.0f) {              \n"
"            break;                                                 \n"
"        } else {                                                   \n"
"            prevX = newX;                                          \n"
"            prevY = newY;                                          \n"
"        }                                                          \n"
"    }                                                              \n"
"    return count;                                                  \n"
"}                                                                  \n"
"                                                                   \n"
"__kernel void mandelbrot(                                          \n"
"                const int max_calc,                                \n"
"                const float start_x,                               \n"
"                const float start_y,                               \n"
"                const float step_x,                                \n"
"                const float step_y,                                \n"
"               __global int *out) {                                \n"
"    size_t x     = get_global_id(0);                               \n"
"    size_t y     = get_global_id(1);                               \n"
"    size_t width = get_global_size(0);                             \n"
"                                                                   \n"
"    int index = (width * y) + x;                                   \n"
"                                                                   \n"
"    float px = mad(step_x, (float)x, start_x);                     \n"
"    float py = mad(step_y, (float)y, start_y);                     \n"
"                                                                   \n"
"    out[index] = calculate(px, py, max_calc);                      \n"
"}                                                                  \n";
    cl::Program::Sources sources;
    sources.push_back(make_pair(src_str.c_str(),src_str.size()));
    cl::Program program(context,sources);
    program.build(devices);

    cl::Kernel kernel(program,"mandelbrot");
    kernel.setArg(0,10000);
    kernel.setArg(1,-2.0f);
    kernel.setArg(2,1.5f);
    kernel.setArg(3,0.0234375f);
    kernel.setArg(4,-0.0234375f);
    kernel.setArg(5,buffer);
    int* result = new int[128*128];
    queue.enqueueNDRangeKernel(kernel,cl::NullRange,cl::NDRange(128,128),cl::NullRange);
    queue.enqueueReadBuffer(buffer,CL_TRUE,0,sizeof(int)*128*128,result);
    
    for(int i = 0;i < 128;i++){
      for(int j = 0;j < 128;j++){
        cout << result[j+i*128] << ",";
      }
      cout << endl;
    }
 
  }catch(const cl::Error& ex){
  }
}

汚いプログラムだけどご勘弁。結果はコンソールにCSVで出力されるので、それをファイルに書き出してExcelでグラフにしてみた。

怪しいなとは思っていたけれど

Tokyo Cabinetのデータベースファイルが壊れてました。Tweetが反映されない状態が続いていました。とりあえず初期化して新規Tweetが反映されるようになっています。また、これまでのログがあるので、今週の土日に再度登録し直します。
あと、HeatHashの次の目標はMashupAwardです。10月いっぱいかけてブラッシュアップして応募しようと思います。

karesansuiのインストール

アイコンがかわいかったのとシステムのステータス(負荷とか)がグラフで表示されて便利と思ったので仮想環境管理ツールkaresansuiを、VMWareでCentOS5.5を入れてその上にインストールしてみた。
CentOS標準の仮想マシンマネージャーでXen仮想マシンが作って動かせるのに、karesansuiから作った仮想マシンがうまく動かなかった。よく分からないので、保留。
OpenVZが管理できるようになったら相当うれしい。注目プロジェクト。

Tokyo Dystopia インストール

全文検索ライブラリのTokyo Dystopiaをインストールしてみた。参考にしたのは、本家
http://fallabs.com/

http://blog.livedoor.jp/ixcy/archives/50040801.html
こちら。
基本的には上記サイトそのままで完了。Tokyo Cabinetはインストール済みなのでとばして、Tokyo Dystopiaのバージョンが0.9.15になっているので、最新版をダウンロードする。
# wget http://fallabs.com/tokyodystopia/tokyodystopia-0.9.15.tar.gz
解凍
# tar xvzf tokyodystopia-0.9.15.tar.gz
makeとインストール
# cd tokyodystopia-0.9.15
# ./configure
# make
# make install
以上で終わり。実際にdystmgrでテストしてみて、動作を確認した。

HeatHashの過去のTweetからキーワードを検索する部分に使いたいと考えている。

wkhtmltopdf

WebページをPDFに変換するプログラムを探していたが、このwkhtmltopdfは試した中で最も良い感じ。リンクも有効、日本語も問題なし、さらにJavaScriptも実行してくれる。ページは、
http://code.google.com/p/wkhtmltopdf/
今回はOSXで試してみた。上記サイトのDownloadから、wkhtmltopdf-0.10.0_beta4_OS-X.i386をダウンロードして、適当なフォルダに置く。さらにパーミションで実行可能属性をつけて、
./wkhtmltopdf-0.10.0_beta4_OS-X.i386 URL PDFFileName
で実行。見事にPDFファイルが作成された。WindowsLinuxでも動くようだ。

http://d.hatena.ne.jp/yuum3/20091204/1259941811
こちらには、ソースからのビルドの方法が載っている。

以前Web→PDFのプログラムを探していてmozilla2pdfというのを見つけたが、wkhtmltopdfの方がよさげ。

iPhoneアプリ版HeatHashについて

昨日と今日でiPhoneアプリ版HeatHashが少し形になってきた。
といっても、
http://github.com/mtigas/iOS-MapLayerDemo
ここの、ソースの画像のアドレスを変えて、ちゃんとマップが表示されることを確認しただけなんだけど。
あまりにも簡単にいきすぎて怖いくらいだけど、
http://www.youtube.com/watch?v=eoHK2DfSIno
こんな感じで動いています。

iPhoneアプリ版のロードマップ
1、GPSで自分の位置を取得、中心をあわせる
2、キーワードヒートマップの選択、表示
3、ホールドするとその地点のツイートが表示されるようにする
4、その地点からツイートできるようにする
5、AR表示を作る
そして、AppStoreで公開する。

がんばります。

HeatHashのアーキテクチャについて

HeatHashのアーキテクチャについての解説を書こうと思う。

入力から出力まで順番に書いていこうと思う。

まず、Twitterの収集はTwitterStreamingAPIをRubyで読んでいる。そして、位置情報付きのTweetがあったら、RubySTOMPを使ってHornetQのキューにそのTweetの情報を投げる。なお、このときにキーワードデータベースから検索用キーワードを取り出して、TwitterStreamingAPIに渡している。また、もうひとつのRubyプログラムで、sampleストリームから位置情報付きのTweetを同様に収集している。

次に、キューから取り出したTweetをTokyo Cabinetに格納する部分をJavaで作った。ここで、独自に考えた空間インデックス構造をキーとして、TCのB-TreeDBに格納している。また、格納時にキーワードをAC法で検索・分類し、キーワードごとと、ズームレベルごとに、あらかじめ分割してTCに格納している。この部分は後ほど別記事でまとめる。

最後にヒートマップの表示は、サーバーライブラリとしてnettyを用いたJPEG画像送出に特化したHTTPサーバープログラムとなっている。ここで、以前書いたJPEG直接生成を使っている。TCからデータを取り出し、二次元配列にしてJPEG画像を生成している。ここも、nettyの使い方のまとめも含めて別記事でまとめたい。

また、Webのキーワード入力の裏で動くのが、Rubyで作ったCGIで、新しいキーワードが入力されるとTCのTableDBに格納し、DBにあるキーワードならカウントアップする仕組みになっている。また、入力されたキーワードのヒートマップ表示URLにリダイレクトする。

以上、大まかに分けると4つのパートがHornetQとTCを介して協調して動作する、結構複雑な仕組みとなっている。

以後の記事で、各部分について述べていきたい。

追記:図があるとわかりやすいと思うので、時間のあるときに描きます。