行き着いた先はやはりアセンブラ。
---
一昨日の続き
結局アルゴリズムの変化による性能の変化を調べたいので今アセンブラのステップ数調べてもアルゴリズムを変更するたびに紙で追うのか!と言う話になったので、前のエントリのコメントでbona先生が紹介してくれたrdtsc命令について調べることにした。
rdtsc命令...(read-time stamp counter の略らしい)
Wataru's memo 2003-10-01 (Wed)の日記の説明が分かりやすい!
余り知られていないが、Pentium 以降の x86 シリーズは、クロックレベルの解像度を持った64ビットカウンターを内部に有している(MSR: Model Specific Registers のひとつ)。この超高精度カウンターは、電源ONでゼロリセットされ、CPU 稼働中は1クロックサイクル毎に1ずつインクリメントされる。RDTSC 命令は本カウンターの現在値を EDX/EAX レジスターペアに格納するための命令である。
んでこの命令の活用例。yosirin-helloさんのこのページのソースを使ってみた。
LARGE_INTEGER cycles;
__asm {
cpuid
rdtsc
mov cycles.LowPart, eax // (1)
mov cycles.HighPart, edx
}
// 関数の呼び出し
__asm {
cpuid
rdtsc
sub eax, cycles.LowPart // (2)
sub edx, cycles.HighPart
mov cycles.LowPart, eax // (3)
mov cycles.HighPart, edx
}
printf("関数が消費したのクロックサイクル数 : %d [cycles]", cycles.QuadPart );
cpuid命令も出さないとちゃんとした値を返してこないそうな。
MSのvc++とかで開発しているのならwindows.hのincludeが必要。
ただ、自分の環境の場合32bit幅で出力されてしまい、あっという間にオーバーフロー起こして負の数になってしまった。これは後でチェックしておこう...。
ちなみに上のソースをまんま実行してみたところ、Athlon64 3000+(実クロック1.8GHz)で60-70クロックぐらい消費していた。上記以外、何も実行してないのでほとんどオーバーヘッドがない状態だがそれでもクロックは僅かに進んでいるのが確認できた。わずか(平均して)65/1,800,000,000≒36.1ナノ秒の出来事ではあるが...。
とりあえずインラインアセンブラの部分を関数化して(多少コレでオーバーヘッドが生じそうだが...)各GAのオペレーターに挟んでその結果をcsvで出力するところまでして今日は終了。
しかし、おおもとのGAのプログラム、元々激重なnearest insertion(以下NI)の評価がメインなので評価対象としてNIを使わない素のGAを作らなければならないという問題が...。長期戦になりそうです(´・ω・`)
---
話をrdtsc命令に戻す。
家に帰ってから思ったのだが、(当たり前の話だが)AMD64っていうからには64bitではないかと。
上の方で当然のごとく32bitで扱っていたがいいのかと。
アセンブラは大学の授業でちょろっとかじった程度でほぼズブの素人なのでまずはその辺を調べてみた。
とりあえずAMD64アーキテクチャの確認
http://hp.vector.co.jp/authors/VA003988/asm2.htmより
32ビットレジスタ
EAX,ECX,EDX,EBX,ESP,EBP,ESI,EDI,R8D,R9D,…,R15D
EAX,EDXは32bitなまんまなのね。よかったよかった。
更にnetで調べてたら良記事発見!
組み込まれたエンジニアさんの2006-02-15の記事より
64ビットの値を受け渡すのには、IA32ではEAX/EDXのペアでデータを渡すようにしているのであるが、64ビットアーキテクチャのAMD64が普通にC言語から64ビットデータを取得する時には、当然ながら1つのレジスタで読み出せるのである。
その部分が、トラブルの種になる。
RDTSC命令は、IA32との互換性を考えて、AMD64でもEAX/EDXペアにカウント値を読み出すので、64、32ビット共用の読み出し関数はこの点を考慮しないといけない。
なるほど、結局Athlon64使っててもEAX/EDXが32bitだから上の方のソースで問題ないわけですな。ただ同記事で指摘されているように、実行するときにCool'n'Quietはoffにしておかないとなぁ...。クロックの動的変化を思いっきり忘れていました。