憂鬱な午後のひととき


3月31日_
微妙に手順ミスが続いてなんやよう分からんうちに一日が終わってしまい疲労感だけが残る. JRの新幹線の切符の購入システムのちぐはぐさはなんとかならんもんかな. はよ寝よ.
3月28日_
『ゴルディウスの結び目』(E・ケストナー 高橋健二訳)
だれひとり解いたものがいなかったゴルディウスの結び目を, 剣で断ち切ることによって名声を得たアレクサンダーの逸話. 筆者は誉め称えるだけでええんかいと待ったをかける.
至る所でできる新しい結び目(=困難な事態)にたいして,
サーベル理論の信奉者が, (中略)「ぶった切るのが一ばん筋が通っている!」とつぶやきます.(中略) そういう提言をする連中をこそ, 一刀両断にするように, 実際徐々に転換していくべきだ
と著者は思う. 自分もよく傲慢な判断をしがちなので反省.
3月26日_
またいつもの眠たい季節. 今のところ花粉症でないだけましなんやけど.
3月21日_
・確かANSI的にはstdoutはマクロでなければならないはずだったので stdout = stderr; はエラーになるんとちゃうのと思った. で, gccのstdio.hを見ると
#define stdout stdout
その上にある「C89/C99 say they're macros. Make them happy.」というコメントがなんとも.
・プログラマーの三大美徳は不精, 短気, 傲慢(itmedia Hena Hena Nikki経由)
うひゃ, (プログラマーじゃない単なる性格として)当てはまる. 必要条件にしか過ぎないのが悲しい.
3月20日_
夜が高カロリーな食事だったので, 前回よりゆっくりめでジョギング. 去年引っ越したから場所は違うけど, まあ似たような距離だ.
3月19日_
鼻水は止まったけど仕事中頭が痛い. 実に痛い. しかし, 帰社してから知人と暗号の議論をしてたら治ってしまった. 頭を使うといいらしい(そういう問題か?).
3月18日_
その後何人かの友人に会う. 先月から過酷なスケジュールが目白押しだった(not 過去形)ので, その一時がとても心地よい. が, 気が抜けたか東京に戻り掛けた頃から風邪気味でくしゃみの連発(花粉症ではない). 帰宅して薬飲んで6km走って寝る.
3月17日_
知人の結婚式のため大阪へ. 豪華で楽しい披露宴だった.
3月15日_
夜は短し歩けよ乙女』(森見登美彦)
にやにやしながら読んでしまった. 大学生の頃に読んだら間違いなく畳の上をのたうち回ってたな. 四つ目のエピソードのぬいぐるみに八つ当たりする彼女がまたいい.
3月13日_
『トカトントン』(太宰治)
ずっと昔どこかで高橋源一郎が触れていてタイトルだけは知ってたけど読むのは初めて. すっごくやる気が無いときの気持ちにどんぴしゃ. 他の終戦直前から直後にかけてのいくつかの短編の主人公の意地汚さというか心の弱さが自分のことみたいで恥ずかしい.
3月12日_
↓しつこく続き. 仮説を立てたら検証してみよう.
    ; (A)
    fld     tword [eax]       ; read 80bit
    fstp    st0
の代わりに
    ; (B)
    fld        qword [eax]    ; read 64bit
    mov        bx, [eax+8]    ; read 16bit
    fstp       st0
    ; (C)
    movq       mm0, [eax]     ; read 64bit
    mov        bx, [eax+8]    ; read 16bit
    ; (D)
    mov        bx, [eax]      ; read 16bit
    fld        qword [eax+2]  ; read 64bit
    fstp       st0
で試してみる. 最後のは, 80bitを2回に分けて読むならの最初の仮数部16bit, 残りの指数部64bitかもと思ったパターン.
読み方の違い(小さいほど速い)
offset0123456789101112131415
A1.701.691.701.761.701.701.705.811.705.155.145.155.155.165.155.14
B0.850.850.870.850.850.860.905.500.905.075.085.075.055.095.085.04
C0.890.900.890.900.900.890.905.350.914.734.714.734.724.734.734.73
D0.910.910.920.920.920.920.925.195.205.195.205.205.195.190.915.40

A~Cが似た傾向を示し, Dだけ異なっていることからおそらく仮説は正しいのだろう.
3月9日_
2 よー分からんので基本に戻ってchar/short/int/float/doubleで測定してみる. 数値は多少ぶれがある. 一部抜けがあるのはメモが手元に無いからで概ね傾向は変わらなかった. こういう仮説はどうだろう. 10byteのlong doubleは内部では(A)8byte + (B)2byteの2回に分けてアクセスされている. offset = +7では(B)が16byteをまたぐので遅い. offset = +8ではどちらもまたがないので速い. offset = +9~+15では(A)がまたがるので遅い. あとは(A)と(B)のまたぎかたのペナルティが違うのだろうと推測するしか.
3月8日_
このやる気の無い不動産屋は連絡網も駄目駄目だ. 「○○するにはどうすればよいですか」と尋ねたら, 「明日また担当のものに電話してください」って, 用件をメモって向こうから回答するぐらいできんのか. 前回も電話の取り次ぎの度に繰り返し故障部分を説明させられた. そのときも対応のまずさに文句を言ったのに, 客の手間を減らそうという発想がないらしい. 繰り返し厳禁はプログラムだけの話じゃないんだが.
たとえば近所のお米屋さんは, 初めて購入したときに登録した住所をほぼ覚えていた(名前も言ってないのに…. えっ, 私の挙動が不信で監視されていた?). 半年前に一度会っただけの某営業の人は全く別のところで偶然会ったときに名前を思い出した. また, SONYのカスタマーセンターは電話を掛けるだけで, 私が購入したマシンの履歴などがオペレーターの手元に現れるようだ.
小さいところは, そういう個人の努力で, 大きいところはシステムを導入してよりよい対応を目指している. 中規模のところはいかにうまく情報共有をするかが生命線だと思われるのだが, 今だに殿様商売でいて淘汰されないのかね.
3月7日_
↓size = 16の結果からsize = 12などが予想できないかとか, ごたごた計算してたので, 一番の疑問点「sizeo = 16, offset = 8で何故速い?」(つっこみby BBS 21)という疑問を書き忘れていた. 本当に何故なんだろう. メモリへのアクセスと内部のなんらかのバッファとの複合作用なんだろうか. ちなみに下の結果は__align(32)にしてoffsetを[16, 32)で回してもmod 16と同じだった.
offset = 7のときはmod 8で3つの区間に跨がるから一番遅いのかな.
3月6日_
3のアドレスの位置による速度劣化をもう少し調べてみた. calc<size>(buf + offset, N);としてoffsetをいろいろ変えてみる. P4 1.7GHzとP4 2.8GHzで傾向はほとんど同じだった. 他のプロセッサでは調べていない.
size, offsetを変えたときの処理時間比(小さいほど速い)
offset(先頭アドレス mod 16)
sizeof(long double)0123456789101112131415
1611111112.911.81.81.81.81.81.81.8
121.11.31.31.71.11.31.31.71.11.31.31.71.11.31.31.7
101.181.51.181.51.181.51.181.51.181.51.181.51.181.51.181.5

目を引くのはsize = 16, offset = 7のときの劣化の度合い. 7 + 10(80bit)が16バイト境界をまたぐので一番効率が悪くなるのだろう. あと, メモリ領域よりも速度を優先するためにsize = 16を利用するならアドレスは最低8バイト境界に合わせないと意味がない(でないと逆に遅くなることがある). gccのmallocの返り値は普通そうなってるから大丈夫なはずだけど, char配列を利用する場合などは注意すべきだろう.
size = 12なら4バイト境界で十分. ただそのときはsize = 10のときと殆ど変わらないのでメモリ領域を優先することもありえる. てことを考えると何故-m80bit-long-doubleオプションがないんかな.
3月5日_
先月作ったクライアント, サーバ間に差し込むアプリの耐久試験をしてたら, アプリより先にサーバが落ちた(^^; 単なるファイル操作だけだったのになあ.
3月2日_
椎名林檎の新聞広告から
「(心配すべきことは)今おつきあいしている方が, 『5年後の成長した自分に必要な相手であり続けるだろうか?』」
そういう考え方もあるのか.
3月1日_
浮動小数点演算ではまった話」にからんで, 「gccでlong doubleは80bit浮動小数なのにsizeof(long double)は何故10ではなく12?」という話.
多分, 配列のパディングと同じで遅くなるからというのが理由じゃないかと.
#include <stdio.h>
#include <time.h>

template<int size>
static inline double calc(char *buf, int n)
{
    for (int i = 0; i < n; i++) {
        *reinterpret_cast<long double*>(&buf[i * size]) = i;
    }
    long double sum = 0;
    for (int i = 0; i < n; i++) {
        sum += *reinterpret_cast<long double*>(&buf[i * size]);
    }
    return sum;
}

template<int size>
void test()
{
    int begin;
    const int N = 1000;
    const int COUNT = 500000;
    double ret;
    static __attribute__(aligned(16)) char buf[N * size];
    printf("size=%d\n", size);
    begin = clock();
    for (int i = 0; i < COUNT; i++) {
        ret = calc<size>(buf, N);
    }
    printf("ret %f, time=%f\n", ret,
        (clock() - begin) / (double)CLOCKS_PER_SEC);
}

int main()
{
    test<10>();
    test<12>();
    test<16>();
}
P4 2.4GHz + gcc -O3 -fomit-frame-pointerにて
size=10
ret 499500.000000, time=5.130000
size=12
ret 499500.000000, time=4.690000
size=16
ret 499500.000000, time=4.310000
となった. size=10が正しく動作していることからlong doubleは12byteのうち10byteしか使っていないことが分かる. ちなみにgccでは下二つのどちらを選ぶかのオプション-m96bit-long-double, -m128bit-long-doubleがある.

御意見は光成滋生<herumi@nifty.com>までお願いします