11/29 (水)
11/26 (日)
[Food] Jaipur@元住吉
最近出来たブレーメン通りのゆうき亭の向かいにインド料理店。 いつ行っても行列ができて入れなかったのだが、今日は11時半頃に前を通ったら空いていたので GO !!
お味の方は値段なりというか「普通」です。
夜のメニューにはジャガイモとカリフラワーカレー(アルゴビ)があるようなので、次回はこっちに期待します。
11/25 (土)
[Food] ボジョレー・ヌーボー会@情報技研
今年も研主催のボジョレー・ヌーボー会が開催される。 ヌーボーその他のアルコール類を持ち寄って賞味する会だが、アルコールは飲めない nminoru も参加させてもらう。
ヌーボーよりもリッチなデザート。 各人が買い込んだケーキ・お菓子類です。
11/24 (金)
符号/ゼロ拡張変換と volatile
C 言語を使っていると暗黙の型変換があちこちに顔をのぞかせるのだが、C コンパイラの性質を掴まないと整数型から整数型への型変換であってもみょ〜なアセンブラを吐かれてしまうよというお話。
汎用レジスタのレジスタ長は、i386 のような 32 ビット CPU だと 32 ビットで、x86-64 のような 64 ビット CPU だと 64 ビットだ。 そのため 32 ビットCPUだと int8_t・uint8_t・int16_t・uint16_t・int32_t・uint32_t の 6 つの整数型は直接レジスタ上に乗せられる。 64 ビット CPU あと int64_t、uint64_t までレジスタに乗せられる。 問題はレジスタ長よりも短い整数型をレジスタに乗せるときに、その上位ビットに何が入るかが問題だ?
まず正規化されたフォーマットとして signed 型なら符号拡張された結果が unsigned 型ならゼロ拡張された結果が入っているとすっきりする。 関数の引数レジスタや戻り値レジスタや右シフト命令の入力なども正規化されたレジスタを必要とするので好都合だ。
ただし、レジスタの正規化はシンプルだけど高コストな方法だ。 CPU の演算命令が 1,2,4,8 バイトの各演算を持ち結果がゼロ拡張されるか符号拡張されるかを選択できればいいのだが、実際の命令は完備されていないので演算後のゼロ・符号拡張命令が入る。
例えば以下のようなプログラムを i386 でコンパイルすると、ゼロ拡張が入る。
#include <stdint.h> uint8_t add(int8_t a, int8_t b) { return a + b; }
add:
pushl %ebp
movl %esp,%ebp
movb 12(%ebp),%al
addb 8(%ebp),%al
andl $255,%eax ;; これはゼロ拡張
leave
ret
ゼロ・符号拡張は、うまいパターンでは1命令で、最悪2命令(左シフトしたあとで符号付き右シフト or 符号なし右シフト)かかる。 数サイクルとはいえ余分なコストがかかり、これが積み重なると速度が低下する。
レジスタの上位ビットを常に正規化するのは高コストなので正規化するパターンは絞って、それ以外では関数の内部ではレジスタの上位にゴミが入っている状態を認めるというやり方もある。 しかしこれだと好機を逸するパターンがある。 例えばロード・ストア命令は必要なデータ幅で呼んだ後で適当なゼロ・符号拡張を行ってくれるし、演算命令は結果をゼロ・符号拡張している場合がある。 上位ビットが常にゴミだと考えると、すでに正規化されているレジスタを過剰に正規化するコストを払うことになる。
理想的にはコンパイラ内部の最適化で仮想レジスタを引き回すときに、データ幅と一緒に上位ビットがどうなっているか属性(ゼロ拡張・符号拡張・不定値)を一緒に取り扱えば、必要な箇所で必要なだけのゼロ・符号拡張命令が展開できる。 現実的にはコンパイラはそんなことをしていなくて、もっとアドホックでナイーブなやり方をしているのです。
以下はコンパイラのナイーブさの一例。 ia64 用の GCC (3.4.4, 20050721) でビルドした結果、volatile をつけると、なぜか全然必要のないゼロ拡張命令を挟み込むことに…
#include <stdint.h> int32_t convert_uint16_to_int32_nonvolatile (uint16_t* p) { return *p; } int32_t convert_uint16_to_int32_volatile (volatile uint16_t* p) { return *p; }
convert_uint16_to_int32_nonvolatile:
.prologue
.body
.mbb
ld2 r8 = [r32]
nop 0
br.ret.sptk.many b0
convert_uint16_to_int32_volatile:
.prologue
.body
.mmb
nop 0
ld2.acq r8 = [r32]
nop 0;;
.mib
nop 0
zxt2 r8 = r8
br.ret.sptk.many b0
11/22 (水)
[Book] Linuxカーネル2.6解読室
高橋浩和氏がオープンソースマガジンで連載されていた、Linuxカーネル2.6解読室が書籍化された20日に発売された。 同書を購入しようと探していたのだが、新横浜の文教堂には数冊おいてあったようだが 私が行った時には一冊も発見できず。 元住吉では置いてあった形跡すらなし。
結局、日吉の天一書房で3冊ぐらい平積みされているのを発見。
11/21 (火)
11/19 (日)
[Work] 大量にコードを書く。いや書かせる。
土曜日と日曜日で某ソフトの命令インタプリータ部分をヘコヘコとコーディングする。
二日で2万ステップほどコードを開発。
我ながら恐ろしい生産性だ。
…って嘘です。コードは書いていません。 インタプリータ対象命令の命令表を EXCEL で作っておいて、Perl スクリプトに食わせて C 言語のソースコードを出力させただけです。 Perl スクリプトは1,000行弱。
動的に生成した C 言語のソースプログラムは、生成的マクロプログラムの類なので全部 .h ファイル行き。 最終的にほとんどのコードが .h ファイルに入っていて、.c ファイルの部分がほとんどないプログラムになる予感。
P.S.
この手のソースコードジェネレータはどうしても汚いコードになりがちだが、今回の Perl スクリプトもかなりボロボロだ。 出来上がった C 言語のソースファイルを見ながら、XML で命令表を作っておいて XSLT で書けば綺麗なジェネレータが書けたのにと気づく。 後の祭りだが。
11/15 (水)
[Prog] POSIX Timer
Linux でスレッドを指定してタイマーシグナルを受け取る手段はないかと、POSIX タイマー(timer_create) 実装の調査をする。
POSIX タイマーはタイマー生成時の引数 sigevent の
SIGEV_SIGNAL
を指定すると、 タイマー条件成立時にsigev_signo
で指定した番号にシグナルを送信できる。SIGEV_NONE
を指定すると、 タイマーは進むが条件が成立しても何のアクションも起こさない。SIGEV_THREAD
を指定すると、 notification としてsival_ptr
の先にある関数が実行される。
SIGEV_SIGNAL
の場合、送信シグナルの番号は選べるがシグナルを受信するのはプロセスとなり特定のスレッドで受けることはできない。
SIGEV_THREAD
の場合が問題なのだが、glibc のコードを読むと新しいスレッドを生成し時刻がくるとそのスレッドにシグナルを投げてその中で notification となる関数を実行しているようだ。
内部では SIGEV_THREAD_ID
と設定することで、シグナルをスレッドに投げるようになる。
ただしSIGEV_THREAD_ID
は非標準で、あくまでもライブラリ実装用の機能なので使用はお控えくださいとお断りされている。
そもそもスレッドを指定してタイマー処理を割り込ませるとバッドマナーなのかしら?
Java の処理系でもタイマーは専用タイマースレッドがある状態だしなぁ(java.util.Timer)。
一度、専用タイマースレッドで受け取っておいて、sigqueue
でシグナルの再配送をすべきかしら?
11/13 (月)
[Work] 普段良く使うマクロの「名前」って、
今回のプロジェクトで使いそうな(C言語の)データ型・マクロをまとめている。
int8_t のようなデータサイズ定義型、アトミック操作・ビット演算系のマクロを用意するのだが、自分が普段使っているけど一般的に何って呼ぶのか分からないマクロがあって困っている。
offsetof
のように ANSI/ISO に取り込まれてしまうと名前も決め内できるのだけど、微妙なマクロの名前って世間ではどうしているんだろう?
例えば 0b110101 のように2進数を記述することができるマクロとか…
#define BINARY16(X)\ ((int)((((0x ## X ## UL) & (1UL << 60)) >> (60 - 15))\ | (((0x ## X ## UL) & (1UL << 56)) >> (56 - 14))\ | (((0x ## X ## UL) & (1UL << 52)) >> (52 - 13))\ | (((0x ## X ## UL) & (1UL << 48)) >> (48 - 12))\ | (((0x ## X ## UL) & (1UL << 44)) >> (44 - 11))\ | (((0x ## X ## UL) & (1UL << 40)) >> (40 - 10))\ | (((0x ## X ## UL) & (1UL << 36)) >> (36 - 9))\ | (((0x ## X ## UL) & (1UL << 32)) >> (32 - 8))\ | (((0x ## X ## UL) & (1UL << 28)) >> (28 - 7))\ | (((0x ## X ## UL) & (1UL << 24)) >> (24 - 6))\ | (((0x ## X ## UL) & (1UL << 20)) >> (20 - 5))\ | (((0x ## X ## UL) & (1UL << 16)) >> (16 - 4))\ | (((0x ## X ## UL) & (1UL << 12)) >> (12 - 3))\ | (((0x ## X ## UL) & (1UL << 8)) >> (8 -2))\ | (((0x ## X ## UL) & (1UL << 4)) >> (4 -1))\ | (((0x ## X ## UL) & (1UL << 0)) >> (0))))
11/10 (金)
[Prog] 非同期シグナルをハンドラ外で処理する
非同期シグナルをシグナルハンドラではなく、元のコンテキストで処理したい場合の処理方法の関する覚え書き。
ある非同期シグナル(SIGTARGET、リアルタイムシグナルを想定)を受信したいがその処理を同期的に行いたい場合、シグナルマスクを閉じてしまって sigtimedwait
でポーリングするという方法がある。
プログラムとしては下のようになる。
siginfo_t save_siginfo; sigset_t target_sigset; // 事前準備 sigemptyset(&target_sigset); sigaddset(&target_sigset, SIGTARGET); // メインの処理 void main_work() { sigprocmask(SIG_BLOCK, SIGTARGET); while (true) { work(); // この中でシグナルが発生する while(sigtimedwait(&target_sigset, &save_siginfo, no-wait) > 0) { // save_siginfo に入った1個分のシグナルを処理 } } }
ただし work()
の 1回の処理が短い場合 sigtimedwait
の実行コストが問題になる。
sigtimedwait
はシステムコールなので 0.1〜1マイクロ秒程度の遅延が発生する。
この遅延を無視できない場合、全体の性能が低下することになる。
そこで sigtimedwait
とシグナル受信を組み合わせる方法を考えてみた。
SIGTARGET に対してマスクを空けておきシグナルを受信する。
ただしハンドラではシグナルをメモリに保存し、フラグを立ててから元のコンテキストにすぐ戻ってくる。
元のコンテキストではフラグのチェックでシグナル受信を検知できるので、sigtimedwait
を毎回呼び出すよりはずっと高速だ。
肝はシグナルハンドラの終わりで ucontext_t
を弄って元のコンテキストが SIGTARGET をマスクする形で再開させている点。
1回シグナルを受け取った後は、シグナルの実処理が済むまで SIGTARGET シグナルはブロックされることになる。
volatile sig_atomic_t flag = FALSE; siginfo_t save_siginfo; sigset_t target_sigset; // シグナルハンドラのセット void set_signal_handler() { struct sigaction newAct; newAct.sa_sigaction = signal_handler; newAct.sa_flags = SA_INFO; sigfillset(&signewAct.sa_mask); // SIGTARGET 中は不要なシグナルは受信しない sigaction(SIGTARGET, &newAct, NULL); sigemptyset(&target_sigset); sigaddset(&target_sigset, SIGTARGET); } // メインの処理 void main_work() { while (true) { work(); // この中でシグナルが発生する if (flag) { do { // save_siginfo に入った1個分のシグナルを処理 } while(sigtimedwait(&target_sigset, &save_siginfo, no-wait) > 0); // シグナルマスクを解除 sigprocmask(SIG_UNBLOCK, SIGTARGET); } } } // SIGTARGET シグナルハンドラ void signal_handler(int signum, siginfo_t* info, ucontext_t* ucp) { save_siginfo = *info; flag = TRUE; sigaddset(&ucp->uc_sigmask, SIGTARGET); }
ただしこのプログラムは、インターバルの間に複数の非リアルタイムシグナルが届く場合に問題がある。 リアルタイムシグナルはキューイングされるが、通常のシグナルはシグナル番号毎にたかだか1個しか保存されないからだ。 受け取ったシグナルのキューイングを自前で行えば、下のプログラムのようにもできる。
// メインの処理 void main_work() { while (true) { work(); // この中でシグナルが発生する if (flag) { // シグナルマスクを設定 sigprocmask(SIG_BLOCK, SIGTARGET); do { // 受信済みのシグナルを一つづつ処理してゆく。 } while(sigtimedwait(&target_sigset, &save_siginfo, no-wait) > 0); // シグナルマスクを解除 sigprocmask(SIG_UNBLOCK, SIGTARGET); } } } // SIGTARGET シグナルハンドラ void signal_handler(int signum, siginfo_t* info, ucontext_t* ucp) { save_siginfo_list,push(*info); flag = TRUE; }
11/9 (木)
[MyWeb] スパムに負けた…
度重なるスパムコメントやスパムトラックバックに根をあげました。\(;_;/
あまりやりたくはなかったのだが、日本語を含まないコメントやトラックバックは弾くように改造。 なんか負けた気がする…
11/7 (火)
11/6 (月)
[Prog] IA-64 コンパイラで分岐予測ヒント
分岐ミスペナルティの大きい IA-64 は、分岐命令のミスを防ぐのが重要。
だが IA-64/Linux 用の gcc は __builtin_expect
ビルトイン関数を使用しても、分岐予測ヒントを混ぜてくれないようだ。
本来であれば IA-64 の分岐命令 br.cond.completor は、コンプリータ部を指定することで分岐ヒントが入る。 C 言語の if 文は br.cond.dpnt 命令か br.cond.dptk 命令になることが多いようだが、プロフィルを採ってみないと分岐確率は分からないので半分は間違った指定になっていると思われる。
Completor | 動的予測ハードウェア | 操作 |
---|---|---|
spnt | 使用しない | 分岐しないと予測(?) |
sptk | 使用しない | 常に分岐すると予測 |
dpnt | 使用する | 動的履歴情報がない場合、分岐しないと予測 |
dptk | 使用する | 動的履歴情報がない場合、分岐すると予測 |
ICC v9.0 も C 言語の if 文に分岐ヒントを入れることができない。
とほほと思っていたのだが、ICC v9.1 になって __builtin_expect
ビルド関数を if の条件に挟むと、異なるコードがでるようになった。
分岐命令の分岐予測方向は変わらないのだが、分岐の制御構造を弄ってプログラマーが与えたヒントに沿うようにコードを最適化している。
この場合でも spnt/sptk は使ってくれない。
追記:11/7
GCC で __builtin_expect
が効かなかったのは、サンプルのコードパターンのせいだった。
GCC ではコードパターンによっては dpnt/dptk の替わりに spnt/sptk が出ている。
11/5 (日)
11/4 (土)
ThinkPad T40 を修理に出した
出張のしょっぱなに壊れた ThinkPad T40 の修理依頼を出すために、引き取りに来てもらう(10/15の日記)。
出張先で電源を入れた時はまだピーッピッピッピ、ピーッピッピッピとさえずっていたのだが、今は完全に沈黙している。 マザーボード交換になりそうな予感。
フライトの前にニッセイ同和損害保険というのに入っていたので、物損は10万円まで補償してくれる契約になっている。 問題は保険が降りるかどうか…
追記:11/7
サポートセンターの調査では、メモリの破損が原因のようだ。 メモリを入れ替えると BIOS 画面まで動くようになったそうだ。
追記:11/9
マザーボード側のパスワードが分からなくて右往左往。
ThinkPad はパワーオンパスワードとは別にアドミニストレーターパスワードがあり、それは CMOS のバッテリーを抜いてもクリアできずマザーボードを交換しないとダメのようだ。
あわやメモリ + マザボード交換になりそうだったが、なんとかパスを思い出すことが出来た。
iTunes
オリコンが PC ベースの音楽配信事業から手を引くと言うニュースを見て思い出し、久しぶりに iTunes を立ち上げてみる。
半年ぶりぐらいに起動して見ると、自分の触手が動く範囲では来世たかおのアルバムとファルコムの音源が追加されているのに気づく。 イースの音源とか大変懐かしゅうございます。
とりあえずフリー曲をダウンロードしてみようとするが、iTunes 4.9.0.17 では楽曲の購入ができなくなっていた。 バージョンアップすると JHymn が動かなくなりそうだが、さてどうしたものか…
11/3 (金)
オズ通りの散策
久しぶりに元住吉の駅からオズ通りの方を歩いてみる。 チャオチャオ餃子が潰れて、その場所に「肉屋の正直食堂」という店になっていた。 生の状態のステーキをカウンターのIHヒーターで客が勝手に加熱して食べろという店らしい。
綱島街道に出たところにあるパチンコ屋にインターネット・漫画・カラオケ喫茶ができていた。 元住吉初めての漫画喫茶だ。 とりあえず入ってみるが漫画の蔵書数は甚だ少なし。 客の入りをみるとカラオケがメインのようだ。
11/1 (水)
[MyWeb] ドメイン更新の季節
愉快堂出版の DOMAIN 21 から NMINORU.JP ドメインの更新時期のお知らせメールが届く。 2006年8月15日に届いたメールでは愉快堂出版は JPRS から指定事業者を解除され(jprs)、JPドメインレジストラから手を引いたと思っていたがそうではないらしい。 ヒューメイアレジストリを介して登録しているのか?
とりあえず契約を更新。 次回の更新は2011年11月30日。