Diary
twitterもはじめました
[356]2008/5/22(Thu)
[tag: 旧日記システム
]
今日はC++の話。
学校の研究で仮想マシンもどきを作っている俺だけども, 最近やっとこさコードを実行できるようになったので性能評価をしてみているんだけど,すばらしい遅さ。
とりあえずテストしたのは,O(n2)の計算量のいわゆるバカソート。 配列サイズ1000のソートを,最適化オプション-O3でコンパイルしたVMで実行しても6秒かかる。 配列サイズを10倍して,実行時間を100倍にして実機でやると0.15秒くらい。 単純に比較して仮想マシンは実機の5000倍くらい遅くなるようだ。
もうちょっと正確にみてみると,実行した命令数が1000万命令強。 かかった時間が6秒弱。 計算してみると1.8MIPS…いつの時代のマシンだ…
まぁ,もとから速度を考えて設計した仮想マシンじゃないし, 実行することだけが目的じゃないからいいような気がするんだけど, なんか「実行を速くしてみたい」という衝動にかられて色々やってみた。
とりあえず,アセンブリ言語レベルのマシンなので,
で,こんなふうに苦労したのに
性能がちっとも変わらないとはどういうことだキバヤシ!!!
-O3では辛うじて速くなったものの(誤差程度だが),-O2ではむしろ遅くなるという… もしかしてswitch-caseって俺が思ってるより賢くコンパイルされるんだろうか… caseラベルをいちいち比較してるんだと思っていたんだがなぁ… ちょっと時間ができたら出力されるアセンブリでも眺めてみようかな…
苦労して関数テーブルを使っても速くならなかったよ。死ねよ。
Boostの関数型言語風味にする拡張ではどうだろうね。気になるね。
学校の研究で仮想マシンもどきを作っている俺だけども, 最近やっとこさコードを実行できるようになったので性能評価をしてみているんだけど,すばらしい遅さ。
とりあえずテストしたのは,O(n2)の計算量のいわゆるバカソート。 配列サイズ1000のソートを,最適化オプション-O3でコンパイルしたVMで実行しても6秒かかる。 配列サイズを10倍して,実行時間を100倍にして実機でやると0.15秒くらい。 単純に比較して仮想マシンは実機の5000倍くらい遅くなるようだ。
もうちょっと正確にみてみると,実行した命令数が1000万命令強。 かかった時間が6秒弱。 計算してみると1.8MIPS…いつの時代のマシンだ…
まぁ,もとから速度を考えて設計した仮想マシンじゃないし, 実行することだけが目的じゃないからいいような気がするんだけど, なんか「実行を速くしてみたい」という衝動にかられて色々やってみた。
とりあえず,アセンブリ言語レベルのマシンなので,
void VM::execute(Instruction *i)
{
switch(i->getType())
{
case ADD:
execADD(i);
break;
case SUB:
execSUB(i);
break;
.
.
.
}
}
のような実行エンジンになってる。
で,命令が来るたびにSwitch-Caseで比較してると遅いと思って,
関数テーブルを用意してみた…んだけど,
C++で関数テーブル作ろうとすると文法の気持ち悪さに死にそうになるね…
{
switch(i->getType())
{
case ADD:
execADD(i);
break;
case SUB:
execSUB(i);
break;
.
.
.
}
}
//どこかでtypedef定義
//これ自体が既にキモい
//じつはInstructionAddressという型を定義しているのだけど
//この文法は無いだろ…と
typedef void (VM::*InstructionAddress)(Instruction*);
void VM::makeInstructionTable()
{
//どこかで定義した InstructionAddress *instruction_table;
instruction_table = new InstructionAddress[INSTRUCTION_NUM];
//命令のタイプ情報はenumなんだが,enumのfor_eachがC++には無い…
//とりあえず,代入の例
instruction_table[ADD] = &VM::execADD;
.
.
.
}
void VM::execute(Instruction *i)
{
InstructionAddress func;
func = instruction_table[i->getType()];
//なにこの文法 これで目的の関数が実行できる
(this->*func)(i);
.
.
.
}
とか,まぁ実際のコードはもうちょっとウマいこと書いてるけど,こんな感じでがんばった。
可読性もクソもあったもんじゃないね!
ちょっと関数型言語に憧れてみる。//これ自体が既にキモい
//じつはInstructionAddressという型を定義しているのだけど
//この文法は無いだろ…と
typedef void (VM::*InstructionAddress)(Instruction*);
void VM::makeInstructionTable()
{
//どこかで定義した InstructionAddress *instruction_table;
instruction_table = new InstructionAddress[INSTRUCTION_NUM];
//命令のタイプ情報はenumなんだが,enumのfor_eachがC++には無い…
//とりあえず,代入の例
instruction_table[ADD] = &VM::execADD;
.
.
.
}
void VM::execute(Instruction *i)
{
InstructionAddress func;
func = instruction_table[i->getType()];
//なにこの文法 これで目的の関数が実行できる
(this->*func)(i);
.
.
.
}
で,こんなふうに苦労したのに
性能がちっとも変わらないとはどういうことだキバヤシ!!!
-O3では辛うじて速くなったものの(誤差程度だが),-O2ではむしろ遅くなるという… もしかしてswitch-caseって俺が思ってるより賢くコンパイルされるんだろうか… caseラベルをいちいち比較してるんだと思っていたんだがなぁ… ちょっと時間ができたら出力されるアセンブリでも眺めてみようかな…
きょうのまとめ
C++で関数ポインタを扱うとかなりキモいよ。Cで関数ポインタ扱うよりキモさ++。苦労して関数テーブルを使っても速くならなかったよ。死ねよ。
Boostの関数型言語風味にする拡張ではどうだろうね。気になるね。