2010/10/20

C言語で作成した関数を埋め込み

スクリプト言語はC言語などと比べると10倍近くも処理速度が遅いのが難点です。
10倍しても1ミリ秒足らずで終るような処理でしたらあまり問題はありませんが、C言語なら500ミリ秒で済む処理が5秒もかかるようになってしまうと、実用に耐えません。
画像の全ピクセルに対する処理など、反復処理が何万回にも及ぶ可能性のある処理は、本体部分をC言語で作成しておいて、それを何らかの方法でスクリプトから呼び出すようにするとよいでしょう。
ここでは、NILScriptのImageユニットで各種フィルタ機能の実装に利用している、Cコンパイラで生成したネイティブコードをスクリプト中に埋め込む方法を紹介します。

まず、Microsoft Visual C++(無償のExpress Editionでも可)でプロジェクトを作成し、埋め込みたい関数を記述してコンパイルします。
すると、プロジェクトのディレクトリ内の「Release」ディレクトリにソースコードの拡張子を「.cod」にしたファイルが作られます。
このファイルには、ソースコードから生成されたネイティブコードの16進ダンプや、アセンブラのコードが記述されています。

NILScriptのディレクトリをカレントディレクトリとして「ng tool/codConv ".codファイルのパス"」というコマンドを実行すると、指定された.codファイル中の関数のネイティブコード部分が抽出され、以下のような形式でクリップボードに格納されます。
var negate=(new CFunction(Hex.decode(
 "55 8b ec 53 56 57 8b 75 0c c1 e6 02 8b 5d 14 8b d3 2b de 8b 45 08 8b 7d 10 0f af fa 03 f8 8b d0 03 d6 81 30 ff ff ff 00 83 c0 04 3b c2 7c f3 03 c3 3b c7 7c e9 5f 5e 5b 5d c3"
))).toFunction([],UInt);
これをスクリプト中に貼り付け、「[]」の中に引数の型の定義を記述し、必要なら返り値の型を修正すれば、ネイティブコードの埋め込み定義が出来上がります。
あとは普通の関数と同じように呼び出して利用できます。

なお、Imageユニットのネイティブコード関数の多くは、動作速度を高速化するため、処理のほとんどをインラインアセンブラで記述しています。
何度も使用する値でメモリの読み書きが発生しないようにレジスタに格納したままにするなどの工夫をすることで、C言語からコンパイルしたものより2倍くらい高速化できることもあります。
いきなりアセンブラでプログラムを作るのは大変なので、最初はコンパイラが生成した.codファイルを参考にするとよいでしょう。

0 件のコメント:

コメントを投稿