CTRUMPチュートリアル

最終更新 2009/06/25

CTRUMPは、拡張可能、かつ、使いやすいCプログラムのトランスレータフレームワークを目指しており、 これを実現するために、ユーザのレベルにあわせたインターフェースを用意しています。

現在は、以下のインターフェースが用意されています。

このページでは、それぞれのインターフェースの簡単な使いかたを説明しています

GUIからCTRUMPを使う

まずは、使ってみましょう。 ここでは、CTRUMPは、直列に書かれたCプログラムのループをCell/B.E.上のSPEに自動並列化するツールです。

CTRUMPのインストールが終わっていれば、コマンドラインからctrump_guiを起動すると、ツールを起動することができます。

 $ ctrump_gui

(書きかけ… index.html を参考にしてください)

PythonからCTRUMPを使う

CTRUMPでは何ができるのか全体像を確認したところで、次のステップへ進みましょう。

上のGUIで行った最適化操作は、全てプログラム可能なPythonインターフェースとして用意されています。

(実際、上のGUIプログラムは、このCTRUMP最適化操作をPythonスクリプトによって制御することによって実現しています)

このインターフェースを使うことで、必要な操作を自動化することができるようになっています。

たとえば、GUIではインタラクティブな操作が可能ですが、特定の部分を何度も最適化したい場合は、同じ操作を繰り返す必要があり、面倒な場合があります。 このような場合には、以下で説明するCTRUMP APIを使い、操作を自動化しましょう。

処理概要

CTRUMPインターフェースを使う場合の一般的な手順は以下のようになります

  1. プログラムを解析する
  2. コードの変換を行う場所を選択する
  3. 変換を実行する

ctrumpモジュールのインポート

正しくインストールされていれば、以下のように、ctrumpをimportすればctrumpモジュールを使うことが可能になります

import ctrump
print ctrump.version

上のPythonスクリプトを実行し、'0.0.1'と表示されればモジュールのインポートに成功しています (それ以外の数字が表示された場合、このドキュメントが追従できていません)

Cプログラムのパース

続いて、C プログラムをパースします。 なお、Cプリプロセッサは現在のところ対応していません。必要な箇所でプリプロセスを行ったプログラムを入力するようにしてください。

以下のプログラムでプログラムがパースできます。

import ctrump
fname = sys.argv[1]
env = ctrump.EnvType()
parser_option = ctrump.ParserOptionType()
(tree,errors) = env.parse(fname, fname, parser_option)
print tree

parseに渡す引数は、parse(ファイル名, プリプロセスする前のファイル名, パーサオプション)のみっつで、返り値は、 (構文木のオブジェクト, 警告のリスト)のタプルです。コンパイルエラーがあった場合、(None, エラー・警告のリスト)が返ります。

ParserOptionTypeはタブ幅や言語拡張などの設定をする予定がありますが、プリプロセッサへの対応がまだのため、特に設定するものはありません。

プログラムの解析

最適化可能な箇所を探します。これには、ctrump.optimizer.OptimizeEngine の analyze メソッドを使います

# Cコードから最適化可能な箇所を探す
import sys
import ctrump
import ctrump.optimizer

fname = sys.argv[1]
env = ctrump.EnvType()
parser_option = ctrump.ParserOptionType()
(tree,errors) = env.cfront_parse(fname, fname, parser_option) # パースする

engine = ctrump.optimizer.OptimizeEngine() # 解析エンジンオブジェクトを作る

results = engine.analyze(tree) # 解析を行う

analyze の戻り値は AnalysisResult オブジェクトのリストになっています。 このリストの要素の AnalysisResult オブジェクトにアクセスすることで解析結果を取得することができます。

プログラムに問題がある場合は、get_errors()やget_warnings()でその問題の発生箇所とメッセージが、 最適化可能な場合はget_enabled_transoatlrs()で、どのような変換が可能かを示すオブジェクト、トランスレータを取得できます

例は以下のようになります

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import ctrump
import ctrump.optimizer

prog='''
int func(int *data, int n)
{
    int sum = 0;
    int i;
    for (i=0; i<n; i++) {
        sum += data[i];
    }

    for (i=0; i<n; i++) {
        sum += data[i/3];
    }

    for (i=0; i<n; i++) {
        sum += data[sum];
    }
    return sum;
}'''

f=open('testfunc.c', 'w')
f.write(prog)
f.close()

fname = 'testfunc.c'
env = ctrump.EnvType()
parser_option = ctrump.ParserOptionType()
(tree,errors) = env.cfront_parse(fname, fname, parser_option) # パースする

engine = ctrump.optimizer.OptimizeEngine() # 解析エンジンオブジェクトを作る

results = engine.analyze(tree) # 解析を行う

print '== errors =='
for i in results: # 全ての解析結果について
    region = i.get_region()
    for e in i.get_errors(): # 全てのエラーを表示する
        print region, e

print '== 最適化可能なループ =='
for i in results: # 全ての解析結果について
    region = i.get_region()
    for t in i.get_enabled_translator_names(): # 有効なトランスレータの名前を全て表示する
        print region, t

以下のような出力がされます

$ python view-error.py
== errors ==
('testfunc.c', 10, 'testfunc.c', 12) testfunc.c:11:`i/3':添字式が解析できませんでした。

('testfunc.c', 14, 'testfunc.c', 16) testfunc.c:15:`sum':添字式の中で使われる変数がループ中で計算されています。

== 最適化可能なループ ==
('testfunc.c', 6, 'testfunc.c', 8) SPE Offloading

10-12行目のループは添字式が11行目で解析できないために、14-16行目のループは15行目で'sum'を添字式に使っているため、最適化できず、 6-8行目のループは SPE Offloading が適用可能であることを示しています。

最適化を実行する

トランスレータが適用可能な場合には、そのAnalysisResultオブジェクトのdo_translateメソッドを呼び出すとトランスレーションが実行できます

引数はそれぞれ、

となっています。

とりあえず、グローバルオプション、トランスレートオプションは、あとで説明します。とりあえず初期値を渡しておきましょう。 グローバルオプションの初期値は、OptimizeEngine.get_global_optioon_initial_value()で、 トランスレートオプションの初期値は、トランスレータのget_translate_option().initial_value()で取得できます。

例は以下のようになります

#!/usr/bin/python
# -*- coding: utf-8 -*-
import sys
import ctrump
import ctrump.optimizer

prog='''
int func(int *data, int n)
{
    int sum = 0;
    int i;
    for (i=0; i<n; i++) {
        sum += data[i];
    }

    for (i=0; i<n; i++) {
        sum += data[i/3];
    }

    for (i=0; i<n; i++) {
        sum += data[sum];
    }
    return sum;
}'''

f=open('testfunc.c', 'w')
f.write(prog)
f.close()

fname = 'testfunc.c'
env = ctrump.EnvType()
parser_option = ctrump.ParserOptionType()
(tree,errors) = env.cfront_parse(fname, fname, parser_option) # パースする

engine = ctrump.optimizer.OptimizeEngine() # 解析エンジンオブジェクトを作る

results = engine.analyze(tree) # 解析を行う

done = False
for i in results: # 全ての解析結果について
    region = i.get_region()
    for t in i.get_enabled_translators(): # 有効なトランスレータ
        print "// optimize %s"%str(region)

        translate_option = t.get_translate_option().initial_value() # トランスレートオプションの初期値を取得
        global_option = engine.get_global_option_initial_value() # グローバルオプションの初期値を取得

        new_progs = i.do_translate(t, global_option, translate_option)

        done = True
        break


    if done:
        break

for i in new_progs:
    print i

最初に見つかったループを有効なトランスレータのうちひとつで最適化します。

以上のプログラムを修正して、「特定の行のプログラムを最適化する」なども可能です。必要にあわせてカスタマイズしてください。

オプション

CTRUMPでは、変換実行時に、いくつかのパラメータを指定することができます。

上で示した、translate_option、global_optionは実際にはただの辞書オブジェクトであり、キーが指す値を変更すると、 パラメータを変更できます。

グローバルオプションとトランスレータオプションの違いは、 全体で設定するか、変換ごとに設定するか、の違いになります。 あくまで実装者がそういう区別が便利だと判断して設定されているもので、オプションの設定の方法に違いはありません。

# -*- coding: utf-8 -*-
import sys
import ctrump
import ctrump.optimizer

prog='''
int func(int *data, int n)
{
    int sum = 0;
    int i;
    for (i=0; i<n; i++) {
        sum += data[i];
    }

    for (i=0; i<n; i++) {
        sum += data[i/3];
    }

    for (i=0; i<n; i++) {
        sum += data[sum];
    }
    return sum;
}'''

f=open('testfunc.c', 'w')
f.write(prog)
f.close()

fname = 'testfunc.c'
env = ctrump.EnvType()
parser_option = ctrump.ParserOptionType()
(tree,errors) = env.cfront_parse(fname, fname, parser_option) # パースする

engine = ctrump.optimizer.OptimizeEngine() # 解析エンジンオブジェクトを作る

results = engine.analyze(tree) # 解析を行う

done = False
for i in results: # 全ての解析結果について
    region = i.get_region()
    for t in i.get_enabled_translators(): # 有効なトランスレータ
        print "// optimize %s"%str(region)

        translate_option = t.get_translate_option().initial_value() # トランスレートオプションの初期値を取得
        global_option = engine.get_global_option_initial_value() # グローバルオプションの初期値を取得
        translate_option['doublebuffering'] = False # ダブルバッファリングしない

        new_progs = i.do_translate(t, global_option, translate_option)

        done = True
        break


    if done:
        break

for i in new_progs:
    print i

上の赤字で示すコードを追加して、SPEオフローディング時にダブルバッファリングされないように変更しています。

オプションの値は、トランスレータごとに変わり、どのトランスレータのどのオプションがどのような影響を与えるかは、 事前に把握しておく必要があります。

オプションがきちんとドキュメンテーションされていれば、以下のようにすることで、説明を読むことができます。

#!/usr/bin/python
# -*- coding: utf-8 -*-
import ctrump.optimizer

engine = ctrump.optimizer.OptimizeEngine() # 解析エンジンオブジェクトを作る

translators = engine.get_all_translators()

for i in translators:
    for j in i.get_global_option():
        print '%s:%s:%s'%(j[0], j[2], j[3])

    for j in i.get_translate_option():
        print '%s:%s:%s'%(j[0], j[2], j[3])

get_global_option, get_translate_optionでそれぞれ、グローバルオプションとトランスレータオプションを取得でき、 (オプション名, オプションの型, 一行説明, 長い説明, 初期値)のタプルになっています

PythonでCTRUMPを拡張する

(書きかけ)

CでCTRUMPを拡張する

(書きかけ)


戻る