憂鬱な午後のひととき
恥ずかしいいくつかのミスを
修正して256usec@Core2Duo 1.8GHz. 2.6GHzだと200usecを切って188usec.
数日で1割速くなるってことはやっぱりまだ余裕があるんだろうなあ.
ただレジスタ足りん!と思ったことが何度もあり, ええ加減重い腰をあげて64bit版に挑んでみるか.
まずはOSのインストールから.
うーん, 思ったより速くならず272usec. 今週は頭痛が激しくてしんどい. 頭が緊張しっぱなしなのか夜1時頃寝ても朝6時頃目が覚めてしまう. まだ耳鳴り直らんしなあ. 困った.
なんか乗算内部でちょっと変更すれば数割ぐらいは速くできそうな気がした. それ以前にペアリングの関数名を間違えていた. 恥ずかしー.
下記のソースでなんとなくmovdqaを全てmovapsに置換したら2%ほど速くなった. この二つの命令は動作は同じ(はず)だが0x66プレフィクスが無い分, 前者は後者よりも1byte命令長が長い.
なら何故使っていたのかというとマニュアルにmovapsはfloat用とあったから.
0x66プレフィクスは
LCPストールを起こすので減らすべきというのは知っていたが,
以前movdqaとmovapsの違いを試したときは差は出なかった(のでマニュアルに従っていた). こんなある程度の規模のプログラムで差が出るのは面白い.
ただ同様にpandをandpsに, porをorpsに変更したら速度は変わらなかったり遅くなったりした. デリケートだなあ.
考えられる原因としてはバイト長が変わったことでクリティカルパスで
16byte命令境界をまたぐようになったのかもしれない. そのうち調べる(かもしれない).
プリデコードユニットは最大6命令の先頭位置を見つけることが出来るのですが,フェッチされた16バイトの中に6個を越える命令がある場合は,最初の6命令を1サイクルで処理し,残りは次のサイクルで処理されると書かれています。つまり,7命令を含む場合は,プリデコードに2サイクルを必要とし,平均速度は3.5命令/サイクルに低下してしまいます。
なお, xorに関してはpxor xm0, xm0とxorps xm0, xm0は影響が異なるので注意する.
処理は同じだが, 前者はxm0のレジスタの一部を変更した後に実行したときの
partial register stallが発生しないが後者は発生する.
---と覚えていたが, 今マニュアルを見直すとそれはPentium4までの話でCore2系ではどちらも同じらしい. ならorpsの方がよさそうだ.
288usec@Core2Duo 1.8GHz.
目標値に達したので
公開した.
参照していた論文の実装が同クロックのCore2Duoで400usec程度だったそうなのでとりあえず現時点では世界最速と言ってよいよね?
本音をいうと209usec@Core2Duo 2.6GHzだったのでこちらで200usecを切りたかったのだがちと疲れたし,
Xbyakを
存分に利用したつもりなのでよしとする.
あ,
なんかしゃべってって頼まれたのでそこで多少解説するかもしれない.
まだまだ甘い部分はいっぱいあるので近いうちに抜かれるだろうなあ.
『Googleを支える技術』(西田圭介)
MapやReduceの話よりも, とにかく桁違いな, というか単位違いのスケールの大きい話に圧倒される.
ちまちまやってるのが馬鹿らしくなるというかなんというか.
ストレスのせいかか疲れのせいか, 金曜日の夜から耳鳴りがする. 作業に集中できずあまり進まない.
赤外線は赤かった.
メールで
ちなみに暗順応した目でも赤外線リモコンの光は見えます。真っ暗闇に1時間くらい
居れば目の感度が最大になりますので、そこでリモコンを直視すると赤黒く光る
LEDが見えます。
と教えていただいたので試してみる. 凄い. 本当に赤く光ってるのが見える. 蛍光灯のリモコンでも, パソコンやエアコンのでも見えた.
エアコンのはボタンがいっぱいあるだけあって, 押す場所によって光る場所も変わった.
やってる最中にうっかり蛍光灯を点灯してしまうとさっぱり見えなくなった.
赤外線はデジカメで見える
本当だ, 知らんかった.
C++ コンパイラにおける値渡しの最適化
gcc 4.1.0 -O2 -fomit-frame-pointerなら
movl 36(%esp), %eax
movl $.LC1, (%esp)
movl %eax, 4(%esp)
call printf
movl 36(%esp), %eax
movl $.LC1, (%esp)
movl %eax, 4(%esp)
call printf
movl 36(%esp), %eax
movl $.LC1, (%esp)
movl %eax, 4(%esp)
call printf
movl 36(%esp), %eax
movl $.LC1, (%esp)
movl %eax, 4(%esp)
call printf
となった. つまりこのレベルならどれも一緒.
VC2008なら
proc createT_dtor
mov eax, DWORD PTR [esp+4]
mov ecx, DWORD PTR [esp+8]
mov DWORD PTR [eax], ecx
ret
main
mov eax, DWORD PTR _v$[esp+16]
push eax
push OFFSET $SG3904
call _printf
mov ecx, DWORD PTR _v$[esp+24]
push ecx
lea edx, DWORD PTR _t$3906[esp+24]
push edx
call createT_dtor
mov eax, DWORD PTR _t$3906[esp+28]
push eax
push OFFSET $SG3907
call _printf
mov ecx, DWORD PTR _v$[esp+40]
push ecx
push OFFSET $SG3910
call _printf
mov edx, DWORD PTR _v$[esp+48]
push edx
push OFFSET $SG3913
call _printf
残念ながらcreateT_dtorの呼び出しは省略されなかったがそれ以外はほぼ同じ.
というわけで細かいこと気にせず速度が必要なときに再確認した方がよいのではという至極まっとうな意見で終わる.
cube()の手数をできるだけ減らそうとしたけど難しい. 安直にテーブル引きに走るか. 論文ではできるだけ3乗根の方を減らそうとしてたけど, あれは前提条件が悪いと思う.
減らさないままの方が多分速い.
久しぶりに有給とって自転車でどたばた. ちょっと疲れた.
うるう年
golfとしてはjmpよりはcmovz edx, eax, xor edx, edxよりはcdq, cmpよりはtestの方がよいかな.
leapじゃなくisNotLeapでよいならseteもなくせる.
proc isNotLeap
mov eax, [esp + 4]
cdq
mov ecx, 100
idiv ecx
test edx, edx
cmovnz eax, edx
and eax, byte 3
ret
第1回 勉強会・読書会
参加したいが大阪かあ. 残念.
今日もcube()以外のところをいじる(面倒な部分は避けたい深層意識が…). で325usec. 既に昨日書いた目標値にせまってるなあ.
文献でもSIMD使ってるので400usec程度が限界かと思っていたが, ちとやみくもに最適化を始めるタイミングが早かったかな. コード整理が大変だ.
明日は用事があるので続きは来週.
タコな実装だったdegree(f)を修正して400usec. cube()は明日しよう. ただSIMD化しにくいんだよなあ. どうすべ.
同一クロックでCore2がOpteronの2倍の性能とする(ありえんとおもうが)と, 文献の最速値からの推測値は479 / 2 * 2.2 / 1.8 = 292clk. だからCore2Duo 1.8GHzで300usecだせば文句無しかな. とりあえず当面の目標値とする.
本社のお手伝い調査が一段落したので暫く高速化に専念する.
乗算でCが残っていた部分のSIMD化完了で448usec@Core2Duo 1.8GHz. うーん400usec前半はいくと思ったのになあ.
荒っぽい部分を詰めて地道に稼ぐのも必要だが, とりあえず次はcube()やcubeRoot()を手がけよう.
gcc 4.3.0インストール. 今回からはgccのコンパイルにmpfrとgmpが必須になってちょっと手間取った.
これらが入ったことによって
#include <stdio.h>
double calcd(int n)
{
double ret = 0;
for (int i = 1; i < n; i++) {
ret += 1 / double(i);
}
return ret;
}
float calcf(int n)
{
float ret = 0;
for (int i = 1; i < n; i++) {
ret += 1 / float(i);
}
return ret;
}
int main()
{
printf("calcf=%.15f\n", calcf(10));
printf("calcd=%.15f\n", calcd(10));
}
というコードを-O3 -Sでコンパイルしたとき
main
...
fldl .LC3
fstpl 4(%esp)
movl $.LC4, (%esp)
call printf
movl $.LC4, (%esp)
flds .LC5
fstpl 4(%esp)
call printf
...
.LC1:
.long 1065353216
.section .rodata.cst8,"aM",@progbits,8
.align 8
.LC3:
.long 463583771
.long 1074176442
とcalcf(10), calcd(10)の値がそれぞれfloat, doubleの即値となる最適化がされるようになったっぽい. これは凄い.
それでは
float型とdouble型にあるVCと同じようにdoubleとfloatを厳密に区別するようになったのかと思ったが
クイズの結果は今までのgccと同じくcalcfとcalcdは同一の値を返した.
要はgccが事前計算できるような小さいループの場合はdouble(64bit)とfloat(32bit)と正しく使い分け, そうじゃないときはそのどちらでもない値になってしまう.
中途半端な仕様だなあ. 何かオプションで挙動を変えられるのだろうか(ドキュメントはまだ読んでない).
ただいまF397上のηペアリング475usec@Core2Duo 1.8GHz. 一昨年の最速が479usec@Opteron 275(2.2GHz)らしい.
同一プロセッサだといくらなんやろか. 目指せ300usec代.
ペアリングはintrinsicを使うのを止めてXbyak使って書き直すことにする. もともとこのために開発したようなもんだし.
intrinsicはVC/gcc, 32/64bitで動作するからお手軽に高速化できて実践では重宝するんやけどね.
『鉄仮面』読み終わる. 1200ページも引っ張って落ちはそれかあ. まあそんなけ読ませるのもたいしたもんや.
・大学生の頃にその存在だけは聞いていた『たんぽぽ娘』(風見潤編)を貸してもらう. 素朴だがよい感じ. 梶尾真治のそれ系に近い(というかこっちが元祖なのかな).
・Pentium DはやっぱSSEが遅いので見捨てたい. SSSE以降にしちゃうと比較ができなくなってしまうけどまあいいか. あ, VC2008だとデバッガもSSSEに対応してるんだ.
NyaRuRuの日記
Visual C++ 2008 (含む Express Edition) で WINVER を設定しないと 0x0600 (Windows Vista 以降専用) になるのは酷いかも
VC2008を使うときは気をつけよう. VC6以降はバージョンアップする毎にプロジェクトファイルが変わってしまうので可能な限りVC6を使うというのは実践的な観点からすると正しいと思う.
VC2008のプロジェクトファイルはVC2005では読めないのだけど何が違うのかと比べたらversion="8.00"が"9.00"になっただけだった. ふざけるなー.
ただ最新コンパイラの最適化能力と, テンプレートも魅力なんだよなあ.
上方演芸ホール特別版
おお~, 文枝の「たちぎれ線香」は聞くの初めてや.
Haskell Hackathonを横目に仕事ちょこっと, ペアリングちょこっと.
参加したかったけどこちらを優先ということで. ペアリングはCore2向けにチューンしよう.
既存のやつはPrescott?のしか数値が見当たらない. Core2でどの程度のパフォーマンス出てるんだろう.
昔
御意見は光成滋生<herumi@nifty.com>までお願いします