憂鬱な午後のひととき
微妙に手順ミスが続いてなんやよう分からんうちに一日が終わってしまい疲労感だけが残る. JRの新幹線の切符の購入システムのちぐはぐさはなんとかならんもんかな.
はよ寝よ.
『ゴルディウスの結び目』(E・ケストナー 高橋健二訳)
だれひとり解いたものがいなかったゴルディウスの結び目を, 剣で断ち切ることによって名声を得たアレクサンダーの逸話.
筆者は誉め称えるだけでええんかいと待ったをかける.
至る所でできる新しい結び目(=困難な事態)にたいして,
サーベル理論の信奉者が, (中略)「ぶった切るのが一ばん筋が通っている!」とつぶやきます.(中略)
そういう提言をする連中をこそ, 一刀両断にするように, 実際徐々に転換していくべきだ
と著者は思う. 自分もよく傲慢な判断をしがちなので反省.
またいつもの眠たい季節. 今のところ花粉症でないだけましなんやけど.
・確かANSI的にはstdoutはマクロでなければならないはずだったので stdout = stderr; はエラーになるんとちゃうのと思った.
で, gccのstdio.hを見ると
#define stdout stdout
その上にある「C89/C99 say they're macros. Make them happy.」というコメントがなんとも.
・プログラマーの三大美徳は不精, 短気, 傲慢(
itmedia Hena Hena Nikki経由)
うひゃ, (プログラマーじゃない単なる性格として)当てはまる. 必要条件にしか過ぎないのが悲しい.
夜が高カロリーな食事だったので,
前回よりゆっくりめでジョギング. 去年引っ越したから場所は違うけど, まあ似たような距離だ.
鼻水は止まったけど仕事中頭が痛い. 実に痛い. しかし, 帰社してから知人と暗号の議論をしてたら治ってしまった.
頭を使うといいらしい(そういう問題か?).
その後何人かの友人に会う. 先月から過酷なスケジュールが目白押しだった(not 過去形)ので, その一時がとても心地よい.
が, 気が抜けたか東京に戻り掛けた頃から風邪気味でくしゃみの連発(花粉症ではない). 帰宅して薬飲んで6km走って寝る.
知人の結婚式のため大阪へ. 豪華で楽しい披露宴だった.
『
夜は短し歩けよ乙女』(森見登美彦)
にやにやしながら読んでしまった. 大学生の頃に読んだら間違いなく畳の上をのたうち回ってたな.
四つ目のエピソードのぬいぐるみに八つ当たりする彼女がまたいい.
『トカトントン』(太宰治)
ずっと昔どこかで高橋源一郎が触れていてタイトルだけは知ってたけど読むのは初めて.
すっごくやる気が無いときの気持ちにどんぴしゃ.
他の終戦直前から直後にかけてのいくつかの短編の主人公の意地汚さというか心の弱さが自分のことみたいで恥ずかしい.
↓しつこく続き. 仮説を立てたら検証してみよう.
; (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かもと思ったパターン.
読み方の違い(小さいほど速い)
offset | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
A | 1.70 | 1.69 | 1.70 | 1.76 | 1.70 | 1.70 | 1.70 | 5.81 | 1.70 | 5.15 | 5.14 | 5.15 | 5.15 | 5.16 | 5.15 | 5.14 |
B | 0.85 | 0.85 | 0.87 | 0.85 | 0.85 | 0.86 | 0.90 | 5.50 | 0.90 | 5.07 | 5.08 | 5.07 | 5.05 | 5.09 | 5.08 | 5.04 |
C | 0.89 | 0.90 | 0.89 | 0.90 | 0.90 | 0.89 | 0.90 | 5.35 | 0.91 | 4.73 | 4.71 | 4.73 | 4.72 | 4.73 | 4.73 | 4.73 |
D | 0.91 | 0.91 | 0.92 | 0.92 | 0.92 | 0.92 | 0.92 | 5.19 | 5.20 | 5.19 | 5.20 | 5.20 | 5.19 | 5.19 | 0.91 | 5.40 |
A~Cが似た傾向を示し, Dだけ異なっていることからおそらく仮説は正しいのだろう.
↓
2 よー分からんので基本に戻ってchar/short/int/float/doubleで
測定してみる.
数値は多少ぶれがある. 一部抜けがあるのはメモが手元に無いからで概ね傾向は変わらなかった.
- T = char~doubleをsize = sizeof(T)の繰り返しで読み書きするときはalignmentされていないと数倍遅い(well-known).
- T = char~doubleをsizeof(T)より大きい2の倍数のsizeで読み書きするときは, sizeをまたぐ位置にあるときのみ遅い(へぇ).
- long doubleを16byteごとに読み書きするときは, offset = +7が一番遅く, あとはoffset = +9~+15で遅い. +8は遅くない.
こういう仮説はどうだろう. 10byteのlong doubleは内部では(A)8byte + (B)2byteの2回に分けてアクセスされている.
offset = +7では(B)が16byteをまたぐので遅い. offset = +8ではどちらもまたがないので速い. offset = +9~+15では(A)がまたがるので遅い.
あとは(A)と(B)のまたぎかたのペナルティが違うのだろうと推測するしか.
このやる気の無い
不動産屋は連絡網も駄目駄目だ.
「○○するにはどうすればよいですか」と尋ねたら, 「明日また担当のものに電話してください」って,
用件をメモって向こうから回答するぐらいできんのか. 前回も電話の取り次ぎの度に繰り返し故障部分を説明させられた.
そのときも対応のまずさに文句を言ったのに, 客の手間を減らそうという発想がないらしい. 繰り返し厳禁はプログラムだけの話じゃないんだが.
たとえば近所のお米屋さんは, 初めて購入したときに登録した住所をほぼ覚えていた(名前も言ってないのに…. えっ, 私の挙動が不信で監視されていた?).
半年前に一度会っただけの某営業の人は全く別のところで偶然会ったときに名前を思い出した.
また, SONYのカスタマーセンターは電話を掛けるだけで, 私が購入したマシンの履歴などがオペレーターの手元に現れるようだ.
小さいところは, そういう個人の努力で, 大きいところはシステムを導入してよりよい対応を目指している.
中規模のところはいかにうまく情報共有をするかが生命線だと思われるのだが, 今だに殿様商売でいて淘汰されないのかね.
↓size = 16の結果からsize = 12などが予想できないかとか, ごたごた計算してたので,
一番の疑問点「sizeo = 16, offset = 8で何故速い?」(つっこみby
BBS 21)という疑問を書き忘れていた.
本当に何故なんだろう. メモリへのアクセスと内部のなんらかのバッファとの複合作用なんだろうか.
ちなみに下の結果は__align(32)にしてoffsetを[16, 32)で回してもmod 16と同じだった.
offset = 7のときはmod 8で3つの区間に跨がるから一番遅いのかな.
↓
3のアドレスの位置による速度劣化をもう少し調べてみた.
calc<size>(buf + offset, N);としてoffsetをいろいろ変えてみる.
P4 1.7GHzとP4 2.8GHzで傾向はほとんど同じだった. 他のプロセッサでは調べていない.
size, offsetを変えたときの処理時間比(小さいほど速い)
offset(先頭アドレス mod 16) |
sizeof(long double) | 0 | 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | 13 | 14 | 15 |
16 | 1 | 1 | 1 | 1 | 1 | 1 | 1 | 2.9 | 1 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 | 1.8 |
12 | 1.1 | 1.3 | 1.3 | 1.7 | 1.1 | 1.3 | 1.3 | 1.7 | 1.1 | 1.3 | 1.3 | 1.7 | 1.1 | 1.3 | 1.3 | 1.7 |
10 | 1.18 | 1.5 | 1.18 | 1.5 | 1.18 | 1.5 | 1.18 | 1.5 | 1.18 | 1.5 | 1.18 | 1.5 | 1.18 | 1.5 | 1.18 | 1.5 |
目を引くのはsize = 16, offset = 7のときの劣化の度合い. 7 + 10(80bit)が16バイト境界をまたぐので一番効率が悪くなるのだろう.
あと, メモリ領域よりも速度を優先するためにsize = 16を利用するならアドレスは最低8バイト境界に合わせないと意味がない(でないと逆に遅くなることがある).
gccのmallocの返り値は普通そうなってるから大丈夫なはずだけど, char配列を利用する場合などは注意すべきだろう.
size = 12なら4バイト境界で十分. ただそのときはsize = 10のときと殆ど変わらないのでメモリ領域を優先することもありえる.
てことを考えると何故-m80bit-long-doubleオプションがないんかな.
先月作ったクライアント, サーバ間に差し込むアプリの耐久試験をしてたら, アプリより先にサーバが落ちた(^^;
単なるファイル操作だけだったのになあ.
椎名林檎の新聞広告から
「(心配すべきことは)今おつきあいしている方が, 『5年後の成長した自分に必要な相手であり続けるだろうか?』」
そういう考え方もあるのか.
「
浮動小数点演算ではまった話」にからんで,
「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>までお願いします