最終更新 : 2009/06/25
CTRUMP(しーとらんぷ)は現代のマルチコアプログラミングが抱える問題を解決するためのひとつの方法として提案する、C言語からC言語へのトランスレータ・ユーティリティ群です。全自動による並列化ではなく、プログラマとのインタラクションを通じて目的とする性能に到達することを支援する半自動並列化支援ツールおよび、ライブラリです。
CTRUMPを利用して何ができるのか、を駆け足で簡単に紹介します。ここでの記述は2009年3月23日時点でのCTRUMPについての内容です。プロジェクトの目標、ロードマップなどについてはそれぞれの項目を参照してください。
CTRUMPはC言語で記述されたプログラムコードをより高速なプログラムコードに変換する手助けをします。CTRUMPが出力するのはC言語のプログラムコードです。これはプログラマが読んで理解するのが苦痛にならないくらいの可読性を維持するように配慮されています。直接オブジェクトコードに変換されてしまって実際にどんな最適化が行われたのかわからなくなってしまうようなことはありません。
int function(int *a, int *b, int *c, int n)
{
int i;
int sum = 0;
for (i=0; i<n; i++) {
int v = b[i];
a[i] = v + c[i];
}
}
#include <ctrump/runtime/ctrump-libspe2.h>
int function(int *a, int *b, int *c, int n)
{
int i;
int sum=0;
{
i=0;
struct spe_param {
unsigned int sync_buffer_ea;
int start;
int loop_count;
int all_loop_count;
int spe_id;
int is_last_spe;
int n;
unsigned int b;
unsigned int a;
int v;
unsigned int c;
} __attribute__((aligned(128))) ;
typedef struct spe_param spe_param_t;
spe_param_t *spe_param;
unsigned char spe_param_buffer[896];
unsigned int param_ea_mod=(unsigned int)spe_param_buffer&127;
spe_param=(spe_param_t *)(spe_param_buffer+128-param_ea_mod);
unsigned char sync_buffer[256];
unsigned char *sync_buffer_ea_mod=(unsigned char *)((unsigned int)sync_buffer&127);
unsigned char *sync_buffer_ptr;
sync_buffer_ptr=(unsigned char *)(sync_buffer+128-sync_buffer_ea_mod);
*((unsigned int *)sync_buffer_ptr)=0;
int spe_num=(ctrump_spe_num);
int all_units=(n+32-1)/32;
if (all_units<spe_num) {
spe_num=all_units;
}
int per_spe_units=all_units/spe_num;
int per_spe_loop_count=per_spe_units*32;
int spe_id;
extern spe_program_handle_t spe_main;
for (spe_id=0; spe_id<spe_num; spe_id++) {
spe_context_ptr_t cur_spe;
cur_spe=(ctrump_runtime_spes[spe_id].spe_context);
ctrump_runtime_spe_program_load(cur_spe, &spe_main);
spe_param[spe_id].n=n;
spe_param[spe_id].b=(unsigned int)b;
spe_param[spe_id].a=(unsigned int)a;
spe_param[spe_id].v=v;
spe_param[spe_id].c=(unsigned int)c;
spe_param[spe_id].start=spe_id*per_spe_loop_count;
spe_param[spe_id].loop_count=per_spe_loop_count;
spe_param[spe_id].is_last_spe=(spe_id==spe_num-1);
spe_param[spe_id].spe_id=spe_id;
spe_param[spe_id].all_loop_count=n;
spe_param[spe_id].sync_buffer_ea=(unsigned int)sync_buffer_ptr;
ctrump_runtime_spe_start(cur_spe, &spe_param[spe_id]);
}
ctrump_runtime_spe_wait((ctrump_runtime_spes[spe_num-1].spe_context));
}
}
#include <spu_mfcio.h>
#include <spu_intrinsics.h>
#include <ctrump.h>
#include <ctrump-libspe2.h>
struct spe_param {
unsigned int sync_buffer_ea;
int start;
int loop_count;
int all_loop_count;
int spe_id;
int is_last_spe;
int n;
unsigned int b;
unsigned int a;
int v;
unsigned int c;
} __attribute__((aligned(128))) ;
typedef struct spe_param spe_param_t;
static __attribute__((aligned(128))) spe_param_t spe_param;
static __attribute__((aligned(128))) unsigned char spe_buffer[16384];
int main(unsigned long long spe, unsigned long long argp, unsigned long long envp)
{
int i;
unsigned char *b_i_base[2];
b_i_base[0]=(unsigned char *)(spe_buffer+128);
b_i_base[1]=(unsigned char *)(spe_buffer+128)+8192;
int b_i_mod[2];
int b_i_len[2];
int *b_i;
unsigned char *c_i_base[2];
c_i_base[0]=(unsigned char *)(spe_buffer+2688);
c_i_base[1]=(unsigned char *)(spe_buffer+2688)+8192;
int c_i_mod[2];
int c_i_len[2];
int *c_i;
unsigned char *a_i_base[2];
a_i_base[0]=(unsigned char *)(spe_buffer+5248);
a_i_base[1]=(unsigned char *)(spe_buffer+5248)+8192;
int a_i_mod[2];
int a_i_len[2];
int *a_i;
mfc_get(&spe_param, argp, sizeof(spe_param), 0, 0, 0);
mfc_write_tag_mask(1);
mfc_read_tag_status_all();
int n=spe_param.n;
unsigned int ea_b=spe_param.b;
unsigned int ea_a=spe_param.a;
int v=spe_param.v;
unsigned int ea_c=spe_param.c;
int per_spe_loop_count=spe_param.loop_count;
int spe_id=spe_param.spe_id;
int is_last_spe=spe_param.is_last_spe;
unsigned int sync_buffer_ea=spe_param.sync_buffer_ea;
if (is_last_spe) {
per_spe_loop_count=spe_param.all_loop_count-per_spe_loop_count*spe_id;
}
int start_offset=spe_param.start;
i=0;
int block_i=0;
int block_count_i=(per_spe_loop_count+576-1)/576;
int loop_num_i=576;
int loop_num_i_next=0;
int bufside_i=0;
b_i_mod[0]=(unsigned int)(ea_b+4*start_offset)&127;
b_i_len[0]=(b_i_mod[0]+loop_num_i*4+127)&(~127);
mfc_get(b_i_base[0], ea_b+4*start_offset-b_i_mod[0], b_i_len[0], 0, 0, 0);
c_i_mod[0]=(unsigned int)(ea_c+4*start_offset)&127;
c_i_len[0]=(c_i_mod[0]+loop_num_i*4+127)&(~127);
mfc_get(c_i_base[0], ea_c+4*start_offset-c_i_mod[0], c_i_len[0], 0, 0, 0);
a_i_mod[0]=(unsigned int)(ea_a+4*start_offset)&127;
a_i_len[0]=(a_i_mod[0]+loop_num_i*4+127)&(~127);
for (block_i=0; block_i<block_count_i; ) {
loop_num_i=576;
if (block_i==block_count_i-1) {
loop_num_i=(per_spe_loop_count-1)%576+1;
} else {
loop_num_i_next=576;
if (block_i==block_count_i-2) {
loop_num_i_next=(per_spe_loop_count-1)%576+1;
}
b_i_mod[bufside_i^1]=(unsigned int)(ea_b+4*((block_i+1)*576+start_offset))&127;
b_i_len[bufside_i^1]=(b_i_mod[bufside_i^1]+loop_num_i_next*4+127)&(~127);
mfc_get(b_i_base[bufside_i^1], ea_b+4*((block_i+1)*576+start_offset)-b_i_mod[bufside_i^1], b_i_len[bufside_i^1], (bufside_i^1)+0, 0, 0);
c_i_mod[bufside_i^1]=(unsigned int)(ea_c+4*((block_i+1)*576+start_offset))&127;
c_i_len[bufside_i^1]=(c_i_mod[bufside_i^1]+loop_num_i_next*4+127)&(~127);
mfc_get(c_i_base[bufside_i^1], ea_c+4*((block_i+1)*576+start_offset)-c_i_mod[bufside_i^1], c_i_len[bufside_i^1], (bufside_i^1)+0, 0, 0);
a_i_mod[bufside_i^1]=(unsigned int)(ea_a+4*((block_i+1)*576+start_offset))&127;
a_i_len[bufside_i^1]=(a_i_mod[bufside_i^1]+loop_num_i_next*4+127)&(~127);
}
mfc_write_tag_mask(1<<(bufside_i));
mfc_read_tag_status_all();
b_i=(int *)((unsigned char *)b_i_base[bufside_i]+b_i_mod[bufside_i]);
c_i=(int *)((unsigned char *)c_i_base[bufside_i]+c_i_mod[bufside_i]);
a_i=(int *)((unsigned char *)a_i_base[bufside_i]+a_i_mod[bufside_i]);
for (i=0; i<loop_num_i; i++) {
{
int v=b_i[i];
a_i[i]=v+c_i[i];
}
}
ctrump_put_unaligned((unsigned char *)a_i, ea_a+4*(block_i*576+start_offset), loop_num_i*4, bufside_i+0, 0, 0);
block_i++;
bufside_i=bufside_i^1;
}
mfc_write_tag_mask(3);
mfc_read_tag_status_all();
mfc_write_tag_mask(~0);
mfc_read_tag_status_all();
return ctrump_spe_fini(spe_id, is_last_spe, sync_buffer_ea);
}
CTRUMPの動作環境をセットアップする手順を説明します。 ここでは例として標準的な構成でインストールされているi386版のUbuntu 8.04.2デスクトップエディションにCTRUMPをセットアップします。
TIPS: 対象のUbuntu 8.04.2 をソフトウェア開発に用いたことがない場合、以下に述べるものに加えて autoconf, automake, g++, libtool, python-dev, sun-java6-jdk, subversion... などのパッケージをインストールする必要があるかも知れません。
コマンドラインからQt4をインストールします。
$ sudo apt-get install qt4-dev-tools qt4-qtconfig qt4-doc libqt4-dev
コマンドラインからPyQt4をインストールします。
$ sudo apt-get install python-qt4 python-qt4-common python-sip4 pyqt4-dev-tools
ANTLRをインストールします。現在のCTRUMPが対応しているのはANTLR v2になりますので注意してください。
ディストリビューションが提供しているAntlrバイナリパッケージは、PICとしてビルドされていないことがあり、リンクに失敗することがあります。 ソースからインストールすることをお勧めします。
$ tar zxvf antlr-2.7.7.tar.gz -C ~/ $ cd ~/antlr-2.7.7/ $ ./configure $ CXXFLAGS='-O2 -fPIC' make $ sudo make install
$ svn co https://ctrump.svn.sourceforge.net/svnroot/ctrump/trunk ctrump
$ cd ctrump $ sh autogen.sh
$ ./configure --with-antlr=/usr/local $ make $ sudo make install
$ export LD_LIBRARY_PATH=/usr/local/lib:${LD_LIBRARY_PATH}
$ ctrump_gui &
$ ./configure CC='gcc -mno-cygwin' CXX='g++ -mno-cygwin' \ --with-mingw-python=/cygdrive/c/Python25 \ --with-python=/cygdrive/c/Python25/python --host=i686-pc-mingw32 --build=i686-pc-mingw32
CTRUMPの開発は、経済産業省 委託研究事業 「平成20年度IT投資効率向上のための共通基盤開発プロジェクト」のテーマ 「マルチコアプラットフォーム上での開発環境整備」として行われました。
CTRUMPの開発は、独立行政法人産業技術総合研究所と株式会社フィックスターズの共同で行っており、 GUI、Cパーサ部分を産総研が、 並列性解析器、SPE最適化エンジン、C構文プリンタ、Pythonバインディング、実行時ランタイムを 株式会社フィックスターズが担当しています。