或曰

日曜日, 7月 29, 2007

もてぎロード東コース スポーツ走行

バイク屋さんで車検を通してもらった ZX-6R をトランポに積み込み、そのまま下ろすことなくツインリンクもてぎ行き。午後から天気が崩れそうなので、午前中に30分x2本走って帰宅。

久しぶりにバイクで走った割には、東コースの自己ベスト更新して 1'48.312。写真見ると、頭をインに入れすぎ。頭をセンターに残して、腰だけ落とす感じにしないと。

5コーナーとヘアピンカーブでは、ニースライダーを安定して接地させられるようになった。左は右と比べるとバンク角が少し小さめで、意識して突き出さないとニースライダーは路面に触れない。無理に膝擦る必要はないので、先にフォーム見直す方向で。

しばしばアクセルをワイドオープンしたときにエンジンが一瞬失火する。帰り際にバイク屋さんに寄って相談したら、おそらくタンク水抜きならびに排水系洗浄の際に使った洗浄剤もしくはレギュラーガソリンがまだ完全に抜けきっていないためだろうとのこと。しばらく様子見。

ラベル:

土曜日, 7月 28, 2007

お買いもの


gccの例外処理について調べてたところ、目次に記載があったので購入。MinGW の GCC は SjLj 使ってるけど、これは try - catch があるだけで(実行時に例外が発生しなくても)コストかかるのか。ちょっと気をつけて書かないと性能に影響出るな。

まだ目次をざっと眺めた程度だけど、ハードや実装よりの面白そうな項目がちらほら。

ラベル: ,

ZX-6R 復活

バイク屋さんに預けてあった ZX-6R, 無事に修理が終わって車検通しましたとの連絡。長かった。

FI ランプが点灯してエンジンが掛からない問題は、ガソリンタンクの水抜き穴が詰まってタンクに水が入り込んでいたのが原因だそうで。

明日、ハイエースで回収に行こう。

ラベル:

PC環境再構築

Socket 939 版の Athlon 64 X2 もだいぶ値が落ちたので、Athlon 64 X2 4200+ と 1GB RAM x 2 を購入。開発用の FreeBSD 環境を Athlon XP 1600+ マシンから、VMWare Player 2.0 on Windows Vista x64 に移動。
issei@vmbsd[~/work/GameObj/tool/src/cscript] sysctl hw.model
hw.model: AMD Athlon(tm) 64 X2 Dual Core Processor 4200+
issei@vmbsd[~/work/GameObj/tool/src/cscript] time gmake -j2
111.693u 43.318s 1:29.89 172.4% 5034+23476k 132+1783io 127pf+0w

以前の環境では3分半以上かかっていたのが半分以下になった。よしよし。

VMWare Player 2.0 がインストールされたディレクトリを覗いたところ freebsd.iso というファイル名で、VMWare Tools のイメージファイル発見。VMWare Workstaion 入手しなくても VMWare Tools 使えるようになったのね。

ports の emulators/vmware-guestd6 を使ってインストール。/etc/rc.conf を設定して /usr/local/etc/rc.d/vmware-guestd.sh start したところ、vmmemctl.ko にシンボルがないと言われて kernel panic。
${INSTALL_PROGRAM} ${WRKDIR}/vmmemctl-only/vmmemctl.ko ${VMWARE_KMODDIR}

emulators/vmware-guestd6/Makefile の上記の行、INSTALL_PROGRAM だとシンボル情報を strip してしまうので、STRIP を空文字列にするか INSTALL_DATA にしないとダメじゃないかなぁ。とりあえず手でコピーして対処。後で port メンテナの人に話振っておこう。

ラベル:

火曜日, 7月 24, 2007

EXOCHI: architecture and programming environment for a heterogeneous multi-core multithreaded system

Intel Core2 プロセッサと GPU である Graphics MAedia Accelerator X3000 を異種混在プロセッサ環境と見なして、コンパイラ・デバッガ・実行環境を作ってみたという話。GPU 向けのコードはインラインアセンブリコードとして書き、GPU での並列動作は OpenMP pragma を使って指定。

画像フィルタ処理を使ってベンチマークをとったところ IA32 CPU 単体と比べて 1.41x から 10.97x (Bi Cubic) のスピードアップを実現。

abstract に書いてあるのはこんなところだけど、実装の詳細に興味ありあり。読む方向で。AMD とか SCEI はどうするのかね、このへん?

Untangling the Office: Multi-gigabit Wireless Research Could Soon Make Wired Computers and Peripherals Obsolete

60GHz 周波数帯を用いた広帯域・短距離型無線通信 (RF) について、研究の中心となっている GEDC のリリースニュース。
  • 時期
    • ピアツーピア通信は2年以内
    • 3年以内に多くの PNA (personal area network) アプリケーションが出てくる予定
  • 通信距離
    • 数メートル
    • 壁や人間の皮膚で容易に遮断される(健康への影響なし)
  • 通信速度
    • 1m で 15Gbps, 2m で 10Gbps, 5m で 5Gbps を既に実現
    • 来年までに倍を目指す
  • 想定している用途
    • データ通信
    • 動画
    • Firewire, USBの置き換え
  • 標準化
    • WiFi との後方互換性について検討中
    • ECMA
      • 2月に会合を持った。大手通信機器メーカーなどから代表参加。
      • 10月に再度会合。技術的な決定最終化。
    • IEEE
      • 802.15.3C
これが 2, 3 年で市場投入されて広まるとなると、だいぶ風景が変わりそうだ。会合参加者に Newlans とあるけど、どこだろ? その筋では有名な会社or研究組織なんだろうか。

以下覚書

Georgia Electronic Design Center (GEDC) の科学者達
超高周波数無線 (RF) を用いた短距離広帯域通信

3年以内に「数Gbps無線」が多くの personal area network (PAN) アプリケーションをもたらす。
次世代家庭マルチメディア
DVDを数秒で転送できるデータ接続



RF
周波数60GHz
米国では自由に使用できる (免許不要)

既に実現
1m で 15Gbps
2m で 10Gbps
5m で 5Gbps

想定している用途
・ビデオ
・データ通信

超高速ピアツーピア通信
2年以内に利用可能に

MP3 プレイヤー
携帯電話
キオクス
大量のデータを数秒で転送できるように

データセンターではラックサーバへのインストールがケーブル不要に

「利用可能なスループットにおける巨大な飛躍。10GpbsではDVDをキオスクから携帯電話に数秒でダウンロードできる、2台のラップトップや2台のiPodを素早く同期できる」

現在のデバイスの I/O システムは、そのような速度に対応できない。

現在 Firewire や USB を用いているデバイスでも RF が利用されうる

高解像度ビデオも主要な応用。DVDプレイヤーを手元に置き、5~10m先のスクリーンに転送できる。

現在の最大の挑戦:
データレートの向上
既に小さい電力消費のさらなる減少

来年には転送レートを現在の2倍に

Georgia Tech チームは、現在の無線LANで使われ値える WiFi との互換性を維持する方法について検討中

GEDC の研究者
システムアーキテクチャの変更により
データ転送を行う CMOS RF 統合回路の
インリジェンスと効率を追究
advanced CAD tool, testbed equipment を利用

重点研究対象 RFコンセプト
・single-input-single-output (SISO)
・multiple-input-multiple-output (MIMO)
・WLAN 802.11, WiFi 標準との後方互換性

「システム設計と回路設計の結合、アナログとデジタル技術の活用を追究。
解決すべき mixed-signal problem の問題がとてもエキサイティング」

健康上の問題はない
・転送電力が極めて小さい (10mW以下)
・60GHz周波数は皮膚で止められ、体内に侵入できない

通信が容易に遮断できることは、オフィスや集合住宅での使用に際して実用性を増す。
信号が壁で遮断されるので、近隣との混信が防げる

現在は、この通信帯域での世界標準規格はない (GEDC Director Laskar)
新たな 60GHz 標準について議論するため、ECMA International computer-standards Organization は GEDC で 2 月に会合を行った
3日間の会合参加者には下記の代表が含まれる。

Electronics and Telecommunications Research Institute of Korea
GEDC
Intel Corp.
IBM Corp.
Matsushita Electric Industrial Co. Ltd. (Panasonic)
Newlans
Philips Semiconductors
Samsung Electronics Co. Ltd and Samsung Electro-Mechanics Co. Ltd.
ECMA International Organization は GEDC で 10月に再び会合を行い、技術的な決定を最終化。

IEEE も 60GHz 標準に重きを置いており、802.15.3C と呼ばれる。

Laskar 氏は multi-gigabit 技術が標準化され成熟化するに伴い、他の応用も現れてくると考えている

日曜日, 7月 22, 2007

ZX-6R入院中

昨日も今日も、起きたら 13:00。確実に体力消耗してる。

原因は分かっていて、久しぶりに電車通勤してること。バイク ZX-6R の車検満了に伴い定期点検・車検に出したところ、ECUにトラブルがあってエンジンがかからなくなったとかで、バイク屋さんから検査機器があるところに転送されて長期入院中。

仕方ないので電車通勤してるけど、バイク通勤と比べると時間がかかるわ、満員電車で体力消耗するわで。

セカンドバイクに原付二種を中古で買ってくるかな。アドレスV125とかKSR110あたり。

ラベル:

金曜日, 7月 20, 2007

Graphvizによるファンクション・コールの視覚化

プログラム中の関数呼び出しを可視化する話。

面白そうだなと思って実行してみたら pvtrace コマンドがマトモに動かない。
  • シンボル検索に線形検索を使っている
  • 関数アドレスからシンボル名への変換に、毎回 popen("addr2line .."); を実行している
  • 関数呼び出しの相関関係の記録に、二次元配列を使っている
小規模な C プログラムなら動くけど、シンボル数が増えがちな C++ プログラムに使うには根本的にデータ構造作り変えないとダメだ。あと C++ だと全部の関数シンボルを図に書こうとすると STL コンテナなども出てきて数が多すぎなので、端折れるようにした方が良い。

nm コマンドと Perl 使って書いてみるか。まず Graphvis 用のファイルではなく、関数のコールシーケンスそのまま書き出すプログラム。

#! /usr/bin/perl

use strict;
use vars qw($NM_CMD %SYMBOLS);

$NM_CMD = "/usr/bin/nm -S";

my $file_sym = $ARGV[0] or die;
&read_symbol($file_sym);
my $file_inst = $ARGV[1] or die;

open(F, "$file_inst") or die "$file_inst";
my $depth = 0;
while (<F>) {
chomp;
/^([EX])0x([\da-f]+)/ or next;
my ($ex) = $1;
my ($addr) = hex($2);

$ex eq 'E' and ++$depth;
$ex eq 'X' and --$depth;
next if $ex eq 'X';
next unless exists($SYMBOLS{$addr});
print ' ' x $depth, $SYMBOLS{$addr}, "\n";
}
close(F);

sub read_symbol
{
my $file = shift;

open(P, "$NM_CMD $file |") or die "$NM_CMD $file";
while (<P>) {
next unless /^([\da-f]+)\s+([\da-f]+)\s+[tTW]\s+(\w+)$/;
my ($begin, $size, $symbol) = ($1, $2, $3);

local($_) = $symbol;
next if /^_ZNK?5boost/;
next if /^_ZN?K?S/;
next if /^_ZNK?9__gnu_cxx/;
next if /^_Z41__static_/;
next if /^_ZNK4mpl/;
next if /^__tcf_/;
next if /^_Z8__istypeim/;
next if /^_GLOBAL__/;
next if /^_Z10__maskruneim/;
next if /^_Znw/;
next if /^_Zdl/;
next if /^__cyg_/;

$SYMBOLS{hex($begin)} = $symbol;
}
close(P);
}


実行結果
main
_ZN7CScript7SysDeclC1Ev
_ZN7CScript7ICtxLexC2Ev
_ZN7CScript7SysDecl4ImplC1Ev
_ZN7CScript9LexHolderC1EPNS_7ICtxLexEPSi
_ZN7CScript9LexHolder4ImplC1EPNS_7ICtxLexEPSi
_ZN11yyFlexLexerC2EPSiPSo
_ZN9FlexLexerC2Ev
yyparse
yylex
_ZN7CScript9LexHolder5yylexEv
_ZN7CScript9LexHolder4Impl5yylexEv
(以下略)


よしよし。シンボル名は c++filt に通せば見やすくなる。

週末に Graphivis の文法調べて図にしてみよう。

追記

週末待たずにできた (callgraph.pl)。
#! /usr/bin/perl

use strict;
use vars qw($NM_CMD %SYMBOLS %CALLS %TOTAL);


$NM_CMD = "/usr/bin/nm -S";

# --------------------------------------------------------------------
my $file_sym = $ARGV[0] or die;
&read_symbol($file_sym);
my $file_inst = $ARGV[1] or die;

my (@stack);
open(F, "$file_inst") or die "$file_inst";
my $depth = 0;
while () {
chomp;
/^([EX])0x([\da-f]+)/ or next;
my ($ex) = $1;
my ($addr) = hex($2);

if ($ex eq 'E') {
push(@stack, $addr);
} elsif ($ex eq 'X') {
pop(@stack);
}
next if $ex eq 'X';
next unless exists($SYMBOLS{$addr});

my $sym_callee = $SYMBOLS{$addr};
my $sym_caller = &find_caller(\@stack);

next unless $sym_caller;

# print "$sym_caller -> $sym_callee\n";
++$CALLS{$sym_caller}{$sym_callee};
++$TOTAL{$sym_caller};
exists($TOTAL{$sym_callee}) or $TOTAL{$sym_callee} = 0;

# print ' ' x $#stack, $sym_callee, "\n";
}
close(F);

# --------------------------------------------------------------------
print "digraph G \{\n\n";
# vertecies
foreach (keys %TOTAL) {
my $caller = $_;
if ($TOTAL{$caller} > 0) {
print " \"$caller\" [shape=rectangle];\n";
} else {
print " \"$caller\" [shape=ellipse];\n";
}
}
# edges
foreach (keys %CALLS) {
my $caller = $_;
foreach (keys %{$CALLS{$caller}}) {
my $callee = $_;
my $count = $CALLS{$caller}{$callee};

print " \"$caller\" -> \"$callee\" [label=\"$count calls\" fontsize=\"10\"];\n";
}
}


print "}\n";

# --------------------------------------------------------------------
sub read_symbol
{
my $file = shift;

open(P, "$NM_CMD $file |") or die "$NM_CMD $file";
while (

) {
next unless /^([\da-f]+)\s+([\da-f]+)\s+[tTW]\s+(\w+)$/;
my ($begin, $size, $symbol) = ($1, $2, $3);

local($_) = $symbol;
next if /^_ZNK?5boost/;
next if /^_ZN?K?S/;
next if /^_ZNK?9__gnu_cxx/;
next if /^_Z41__static_/;
next if /^_ZNK4mpl/;
next if /^__tcf_/;
next if /^_Z8__istypeim/;
next if /^_GLOBAL__/;
next if /^_Z10__maskruneim/;
next if /^_Znw/;
next if /^_Zdl/;
next if /^__cyg_/;

$SYMBOLS{hex($begin)} = $symbol;
}
close(P);
}

sub find_caller
{
my $stack = shift;
my $depth = scalar(@$stack);

local($_) = $depth - 2;
for (; $_ >= 0; --$_) {
my $addr = $stack->[$_];

return $SYMBOLS{$addr} if $SYMBOLS{$addr};
}
}



プログラムの実行ファイル名を cscrc, 実行時トレースファイルを cscrc.sym とすると、下記コマンドで Graphvis 入力ファイルを作成できる。
% ./code/callgraph.pl cscrc cscrc.sym | c++filt > cscrc.dot                                       

CScriptのコンパイラ・仮想マシン実行の様子を図にしてみた。

ラベル:

木曜日, 7月 19, 2007

Computer Science Teaching to be Redesigned

コンピュータサイエンスの教育方法についての話。
"Instead of teaching students a lot of facts and then giving them a problem to solve, this method starts out by giving them a problem," he said. "Then they have to go figure out what facts they need to learn to solve it."

伝統的なやり方
  1. 最初に必要なことを教える
  2. それを使った問題演習をさせる
Problem based learning
  1. 先に問題を与えて
  2. その問題を解決するには、何を学ぶ必要があるのかを発見させる
どうやって学生を手助けするのか、どんな課題をどういう順番で与えていくのかなど詳細は書いていないので分からないけど、自分の経験に照らし合わせても目的意識が明確な人間の方が吸収が早いと思う。

自分が抱えている仕事に直結しないいわゆる「お勉強」であっても、その中で課題を見つけて取り組んでいける人は覚えが早いし、質問も良く出てくる。新しいことを身に着ける場合、自分の場合は何に着目してどうやって学習しているのか、他の人はどうなのか、今後、少し意識してみようかな。

水曜日, 7月 18, 2007

お買い物


第1版を持っているけど、C++ Boost Library の日本語ドキュメントという感じで、ざっと流して何があるのかを把握するのに役立った。その後だいぶライブラリも増え、xpressive などいくつかのライブラリは実際に使って内容も把握できてるけど、他は追いきれていないので nice timing ということで。

まだ手元に届いていないので確信できないけど、第1版から推測するに、おそらく Boost Library の設計・実装についてはページ数割いて解説してないと思います。あくまでリファレンス&チュートリアル。時間を金で買う系の本。

ラベル:

続々 ゲームプログラミングについて各分野で60点を取れるくらいの 感じの本

だだもれ 経由 或曰: ゲームプログラミングについて各分野で60点を取れるくらいの 感じの本

何冊かお勧めが追加されてる。私も読んでないのがあるから、あとで注文しときます。

ドラゴンブックが最適化に入れてあるのは、確かに少し違うかもしれません。でもコンパイラ屋さんで食っていくのでなければ、処理系が行う最適化についてはドラゴンブックで取り上げられているレジスタ割り当て、ヒープホール最適化、共通部分式除去、ループ最適化ぐらいを理解しておけば、とりあえずは間に合うかなと思ってます。

あと最適化に関しては、広く言うと記憶域と速度のトレードオフ、データの事前計算 (コンピュータグラフィックスだと PRTとか) なども含まれると思いますが、そのへんも包括的に扱ってる本はまだ見つけてません。

http://www.page.sannet.ne.jp/hirasho/diary/diary0707.html#17p2
本というのはどこか本を作る会社が作るわけで、どうやって そういう所とそういう話になるんだろう、とかいうあたりがまるで想像できません。
大きな会社なら出版社とも付き合いがあると思いますから、その編集さん通して企画に適した分野の編集さん紹介してもらえると思います。

私は学生時代にテクニカルライティングをやっていたことがあり、その時に一緒に仕事した編集さんがいたり(しばらく連絡取ってないので、もしかしたら辞めてしまってるかもしれませんが)、あとは今の会社で付き合いがある出版社があります。まったく人間関係がないところから企画を持ち込んだことはありません。

仕事の立ち上げ方は会社によって差があると思いますが、私の場合はこちらから案をいくつか出して編集さんが見込みありそうなものを絞り込み。メールベースで粗い企画内容を打ち合わせして、それをもとに編集さんが企画書を起こして社内の編集会議通してスタートって感じでした。小説などは違うかもしれませんが、実用系の書籍の場合はこんなものかと。

ラベル:

火曜日, 7月 17, 2007

Cooperative Task Management without Manual Stack Management (2)

先日の続き。覚書。

理論的な話。このあとで Win32 の Fiber 使った Design & Impl. の話が出てくる。

読むのは section 4 まで終わってるんだけど、メモをそこそこ見られる形に起こすのが面倒だ。なんか適当なツール使わないと時間がもったいないな。

1. Introduction

プロジェクトにおいて、並行性に関して提供すべきプログラミングモデルを決める

プロジェクトメンバーが過去に経験したバグ
  • 再現が難しい
  • 発見と修正はさらに難しい
コミュニティの叡智⇒イベントドリブン

このモデルによって次のような問題発生の機会自体が減る
  • 競合 (race condition)
  • デッドロック
経験を積むにつれて "イベントドリブン" という用語が複数の個別コンセプトをまとめたものと判明。もっとも重要なこととして、"イベントドリブン" という用語が、並行性に関する推論において、利益はやっかいな手動スタック管理なしには実現できないということを示唆している。
Section 2
  • まとめると問題になる 2 つの個別コンセプトを定義
  • 主要なアイデアと混乱しないよう 3 つの関連するコンセプトについて触れる
Section 3
  • 手動スタック管理 vs 自動スタック管理
  • 自動スタック管理における最大の問題を緩和する方法を示す
Section 4
  • 混在スタック管理モデル
  • 単一プログラムで、自動スタック管理と手動スタック管理の混在/相互運用を可能に

Section 5
2つのシステムに実装した経験談

Section 6
Related work

Section 7
結論
2. Definitions

コンセプト
  • タスク管理
  • スタック管理
  • I/O 応答管理
  • 衝突管理 (conflict management)
  • データ分離 (data partitioning)
すべてが直交しているわけではないが、個別に考えることが、全システム中でどのように相互作用しているかを理解するのに有用。

2.1 Task management

タスク
  • 個別の制御フローを持つ
  • 共有状態へのアクセスを行う
タスク実行の3モデル
preemptive task management
  • タスク側が検知しないタイミングで実行を中断される
  • マルチプロセッサへのタスク分散可能

serial task management
  • 次のタスク実行前に、一つのタスクが実行を終了する
  • 共有状態へのアクセスで競合が発生しない
  • 共有状態に対してタスク間不変性の定義と保証 (assure)
  • マルチプロセッサで並列処理できない
  • 遅いタスクが長時間他のタスクを待たせてはいけない

cooporative task management
  • タスクは明確に定義された地点でのみ、実行を中断して他のタスクに制御を渡す。一般には長時間かかる I/O 処理が制御切り替えポイント。
  • タスクが I/O 処理を待たずに処理をインターリーブ (交互実行) するには適しているが、マルチプロセッサ並列性を利用してアプリケーション性能向上はできない。
  • グローバルな共有状態の不変性は、タスクが明示的に切り替わるタイミングでのみ保証すれば良い。タスクが再実行 (resume) したときには整合性が保たれていると想定できる。
  • タスク切り替え前に、グローバルな状態に依存するタスク固有の状態があった場合は、実行再開後に無効になっている可能性がある (同様の問題が preemtpvie マルチタスクでロックを開放する場合にも発生する)
  • I/O ライブラリ関数呼び出しをラップして、I/O の初期化を行い制御を他のタスクに移すようにする必要あり。
  • I/O 処理終了後に、関連するタスクをスケジュール可能にする必要あり。
2.2 Stack management
  • 協調的タスク管理を実現するための一般的な方法: プログラムをイベントハンドラの集合として構成する。この手法を「手動スタック管理」と呼ぶことにする。
  • 点:
  • 概念的に1つのタスクの制御の流れ、ならびにタスク固有の状態が複数の手続きに分割される。プログラミング言語のスコープ機能を無意味にする。これがソフトウェアの発展にともなうほとんどの問題を発生させるので本質的。
  • 重要:構造化プログラミング言語が提供する自動スタック管理を利用しつつ、協調的タスク管理を使うことも可能 (section 3.3)
  • いくつかの言語では、言語組み込みの機能として透過的な closure 作成可能 (例; scheme の call-with-current-construction) で、手動スタック管理の考えを未然に防ぐ。
  • この peper では洗練された closuer がない伝統的なシステム言語でのスタック管理問題に焦点
2.3 I/O management
synchronus I/O management
I/O 処理終了まで呼び出し側タスクがブロックされる

asynchronism I/O management
呼び出し側に即座に戻る。重複する非同期処理を開始して、結果が返ってくるのを待つ。

I/O の並行性はタスク実行の並行性とは独立。なぜなら、I/O 処理は計算の共有状態にアクセスしないため。

適切なプリミティブがあれば、非同期 I/O をラップして同期 I/O を提供する、あるいは逆を提供することも可能。

2.4 Conflict management

タスク管理方法によって、共有状態に対する原子性 (atomicity) 保証方法が異なる

conflict management: 使用可能な原子性からリソース衝突を回避する意味のある手段に変換する方法についての考察
  • serial task management
    • タスク全体が共有状態に対する原子的操作
    • タスク間の衝突回避のための明示的なメカニズム不要
  • preemtpvie task management
    • 他のタスクが並列で実行されているため、常に共有状態の不変性を保証する必要がある
一般的な解決策: 同期プリミティブ (e.g. ロック、セマフォ、モニタ)

ハードウェアもしくはランタイム環境により提供される小規模な原子的操作を基礎として、共有状態が常に保つべき複雑な不変性を維持するメカニズムを構築する。
pessimistic synchronize mechanism
処理を完了するのに必要なリソースから他のタスクを締め出す
optimistic synchronize mechanism
投機的に結果を求め、他のタスク処理と衝突していることを検出したら再実行、もしくはそれ以上進められたない場合には pessimistic synchronize mechanism に移行する。
  • 協調的マルクタスクでは任意の大きな原子的操作を効果的に提供できる。
  • 明示的に指定された任意の yield 替えポイント 間は原子的に (他のタスクに途中で切り替わることなく) 実行。
  • 単一プロセッサ用 OS カーネルにおいて、割り込みをマスクすることで原子的シーケンスを構築するのと類似。

ラベル:

続 ゲームプログラミングについて各分野で60点を取れるくらいの 感じの本

各分野をつまみ食いさせて興味を持たせるきっかけにしつつも、最悪そこに書いてあることだけでもとりあえずゲームが作れる、というぐらいの本があると便利な気がするのです。

その点は同感です。新人プログラマ向けの研修で使用している資料を整理すると、ちょうど良さそうなんですけどね。

実際に文章を書く場合の話ですが、本当に1から書くとかなり大変ですが、概説的な内容なら、
  1. 対象読者層を決める
  2. 章立てを決める
  3. 各項目について、参考文献を探してくる
  4. 詳細は参考文献に当たってもらうことを前提として、各項目について必要最低限の内容を絞って記述する。
    • その際の記述のベースとして、参考文献を活用する。Copy & Paste はもちろんまずいですが、内容を咀嚼した上で自分が必要と思うことを補って利用するのは問題ありませんので。
    • 詳細は専門書に当たってもらうこととして、リファレンスを充実させる。
というような作り方をすると、比較的短期間で筋が通ったものが作れると思います。

同人でやってると〆切が曖昧になってなかなか話が進まないので、可能なら仕事にしてしまった方が良いと思います。会社と、著作権や印税収入は会社に帰属させる形にするから時間使わせてくれ、というような交渉はできません?

私はひらしょーさんがいっているような書籍は社会的にも出す意味があるし、きちんと書ければ、それなりに売れ行きも見込めると思います。

ラベル:

月曜日, 7月 16, 2007

実家

実家の PC がインターネットにつながらない問題は、ブロードバンドルータの動作がおかしくなっていた模様。電源入れなおして復帰。

帰省したついでに親とシャツを買いに出かける。相変わらず細身過ぎて既製品だと合うサイズが無いので、オーダーすることに。

夜は MotoGP 観戦。MotoGP クラスは早々にロッシがリタイヤしてしまい、その後はダニー・ペドロサ独走態勢で展開としてはイマイチだった。しかし今期序盤は散々な成績だったホンダが、新パーツ投入で一気に上位に来たので、今後のポイント争いは面白くなりそう。

ラベル:

土曜日, 7月 14, 2007

ゲームプログラミングについて各分野で60点を取れるくらいの 感じの本

ゲームプログラミングについて各分野で60点を取れるくらいの 感じの本が私の知る限りない。幾何、ライブラリ設計、アプリケーション設計、 通信、素材管理、ツール、AI、モーション、最適化、デバッグ体制、描画技術、 ハードウェアアーキテクチャ、アルゴリズム、 などなどの全分野についてそれなりな知識を得られるような本。
一冊ではなく、既存の書籍をワンセットにしてくれということなら簡単にリストを作れそうです。ゲームプログラミング固有の知識分野は、ほとんど無いと思いますので。

実際、私が入社後、ゲームプログラミングのために読んだものは開発機材のマニュアルとサンプルコード(PS2 Linux の DVD-ROM に大半入ってる) 、実際のゲームのコード。あとはメモリや素材管理についてリードプログラマに聞いた程度、それ以外に関しては当時すでに読んでいた書籍, paper やコードで間に合いました。

というわけで、分野別に役立った本リスト。一部を除いて入社前の学生時代に読んでたものなので、別に経験何年の職業プログラマでなくとも読めるはず。
  • 物理/幾何/モーション/描画系
    • 大学教養課程の力学の教科書
    • RealTime Rendering (ここから参考文献をいくつか辿って追加で読んだ)
  • ハードウェアアーキテクチャ
    • コンピュータの構成と設計
    • 計算機設計技法
  • アルゴリズム
    • 定本 Cプログラマのためのアルゴリズムとデータ構造
    • STL の <algorithm>, <numeric>
    • 珠玉のプログラミング
    • The Art of Computer Programming
  • 最適化
    • コンパイラ 原理・技法・ツール
    • Inside the C++ Object Model
  • デバッグ
    • Code Complete
    • Writing Solid Code
    • Debuging the Development Process
  • 素材管理
    • ゲームクリエーターズバイブル
  • 通信
    • TCP/IPによるネットワーク構築
    • 詳解 TCP/IP
    • UNIX Network Programming
  • ライブラリ設計/アプリケーション設計
    • ソフトウェア作法
    • プログラム設計の着想
    • プログラマの打ち明け話
    • Lions' commentary on UNIX
    • 4.4BSDの設計と実装
    • デザパタ本とか UML 本は、共通言語として使うために読んでおいたほうがいいかも
    • その他、ソースコードいろいろ (FreeBSD, Apache, Emacs, etc...)
  • AI
    • AI Programming Wisdom
あとは全般的な Tips として Geme Programming Gems シリーズ。オンラインゲームやるなら RDBMS 関連も追加、プログラミング言語として C++ 関連とツール用に Perl, Ruby, Python, C# どれかは押さえておきたい

CG, 代数, アルゴリズム, コンパイラ理論など、学問的に専門分野が確立しているものは、書籍で大枠を押さえて、先端の話は paper 探してくるのが手っ取り早い。今だと Internet で探すと PDF で掲載されている場合も多いし、理工系の論文誌だと有名どころは東工大の付属図書館にあるので、大岡山まで足を延ばせば有料でコピーできる。

ラベル:

お買いもの

昨年、私が唯一まともに遊んだゲーム(なのか?)である「ひぐらしのく頃に」の作者が送る新作サウンドノベル。予約が始まってたので、適当なところでオンライン注文出しておく。

ラベル:

実家

実家から葡萄が届いていたのでお礼の電話を入れたところ、インターネットにつながらなくて困っている旨。ちょっと PC 操作して調べてもらったところ、PC がブロードバンドルータ (DHCPサーバ) から IP アドレスを取得できてない。

これは現地に行かないとダメだな。明日に入れていた予定も台風のためキャンセルになったことだし、この連休使って一度帰省する方向で。

普段はバイクで帰るけど、台風が来そうなので公共交通機関を使うことに。調べてたら、いつの間にか東武鉄道の特急スペーシアが JR 乗り入れしてたのね。これ使えば早い。

ラベル:

HMS中級@桶川

ホンダが開催しているバイクのライディングスクール。台風の影響で梅雨前線が活発化する中、行ってきました。

レンタル車両は CBR600RR。普段乗っている ZX-6R は車検と定期点検のためこの二週間ほど乗っていないため、バイク自体が久し振り。かなり感覚を忘れていて、最初は「このぐらいは出来たはず」という操作がなかなかできず。

一日かけて徐々に思い出して、感覚が戻ったところで一日終了。合羽着用で走っていたこともあり、いつもより疲労したような。

ラベル:

金曜日, 7月 13, 2007

はてなスター

相変わらず面白いところを狙ってくるな>はてな

継続して使うかどうかはともかく、試しに導入してみる。

スター表示領域が付いたり付かなかったりで何かと思ってソースを読んでみたら、これは <h3><a href="..."> </a></h3> というタグのネストを見つけて表示位置決めてるのね。blogger 側のテンプレートに手を入れて対応。

読書記録:とてつもない日本

現職外務大臣、麻生太郎氏による日本論…と書くと疲れそうな内容に思えるけど肩肘張れずに読めて、明るい気分になれる本。過去の演説などを下敷きにしている部分もあり、半分ぐらいは既知の内容でしたが楽しく読めました。

麻生さんは総務大臣時代に Web で「あっ、そうだろう!」と題したエッセイを連載されていて、これがなかなか面白かったので書籍も購入してみた次第。最近はエッセイ連載はないようですが、外務大臣記者会見外務大臣演説 で着々とコンテンツが増えてます。(更新チェックするのが面倒なので RSSでフィードして欲しいのだけど)

参院選間近なこともあって、年金問題やら格差問題やら眉間に皺寄せて口から泡を飛ばしている人も多いですが、せめて政治家には現実を見据えながら楽観的な未来を語ってほしいと思います。あと私ぐらいの年齢だと、今から暗い顔をして退職後の収入の議論してる暇があったら、楽しく仕事する方に注力した方がよほど楽しい老後が待ってるような気がしてなりません。

読書記録:SAN & NAS ストレージネットワーク管理


オープンシステムは常に技術の変化が激しいけれど、特にこの数年間のインフラ分野を見た場合、最大の変化はストレージとホストの分離。その主要技術である SAN (Storate Area Network) と NAS (Network Attached Storage) に関する入門書。

SANとNASはいずれもストレージを複数ホストで共有するための仕組みだが、SAN はストレージデバイスをホストに接続するための規格である SASI/SCSI の後継、NAS は LAN 上でファイルを共有するためのファイルサーバを出自に持つという違いがあり、それが基本的な性格・方向性を特徴付けている。

本書では、このような SAN/NAS の背景から解説をはじめ、特定のベンダ/アプリケーションに偏ることなく、
  • ストレージインフラの設計と導入
  • 実際の業務で大きな領域を占めることになる運用
  • バックアップ/リカバリ
について、現実的な観点から議論を展開している。特に日常運用やディザスタリカバリに関して、経験に裏付けられた地に足の着いた解説がされており、ベンダの営業担当者と話をする際には(相手から積極的に売り込んでこないため)抜け落ちがちなポイントが押さえられている。

Cooperative Task Management without Manual Stack Management (PDF)

先日読んだ Coroutines in Lua 関連 paper。

Abstract によると、こんな内容だと理解した。
しばしば並行プログラミング実現方法の両極として「イベント駆動プログラミング」と「マルチスレッドプログラミング」があり、前者のほうが可読性・保守性が高いという議論がなされる。これは本来別の軸として評価すべき
  • タスク管理
  • スタック管理
が混同されており議論が混迷しているので、軸を分離して再評価。両方の良いトコ取りを考える。

あと、これ読んだ後で「Why Events Are A Bad Idea」という paper も見てみるつもり。タイトルからして刺激的だ。私なら Events are considered harmful とか付けそうだけど。

お買いもの

上2つは学生時代からのバイブル。第3版が出ていたので、第2版は人にプレゼントして購入。

あとは、今後、ちょっと R&D 寄りのテクニカルなジョブを作ることを考えているので、文献調べるのに ACM Digital Library 購読。表のページには $198 と書いてあるけど、とりあえず Web で ID だけ登録しておいたら、すぐに値引きされたオファーが来た。(ありがち)

ラベル:

水曜日, 7月 11, 2007

Coroutines in Lua

組込用として広く使われている言語に Lua があるが、これは複数の制御の流れを扱うために(CScript のような明示的なスレッドではなく)非対称コルーチンを用いている。それに関する paper。自分で協調的マルチスレッディング処理を設計/実装してみて多少理解が深まったところで、読んでみようということで。

しかコルーチンの話って 1963 年に Conway, M. が既に paper 出してるのね。何年遅れでトラッキングしてるんだか>私

追記

paper 覚書。

2. Lua Coroutines

コルーチンの方式二つ。プログラムの記述能力は変わらない(paperにサンプルコード有り)。Lua では asymmetric coroutines を採用。理由は単純さならびにポータビリティ。
symmetric coroutines
  • 指定したコルーチンに制御を移すという、単一の操作のみがある。
  • すべてのコルーチンが対等なため、制御の流れが分かりづらい。
asymmetric coroutines
  • コルーチンの起動・停止という2つの異なる操作がある。
  • 一般のサブルーチン呼び出しと振る舞いが似ており、制御の流れが分かりやすい。
コルーチンは、本質的には独立したプログラムカウンタとレジスタをリソースとして持つプログラム実行状態。Lua ではスクリプト言語と C 言語の関数を相互呼び出しできるため、リソースとして含まれるスタックには仮想マシンの関数呼び出しとCの関数呼び出しが含まれる。

Cスタックを退避・復元するポータブルな方法はないため、Lua ではコルーチンスタックにC関数が含まれている場合にはコルーチンの中断(yield)を不可としている。この目的のためにも asymmetric coroutines が適している。

2.1 Lua Coroutine Facilities
coroutine.create
コルーチン作成

coroutine.resume
コルーチン再開

coroutine.yield
コルーチン中断

coroutine.wrap
コルーチン補助関数
機能的には create, resume, yield ですべて。wrap はコルーチン呼び出し、停止をあたかも通常の関数のように使えるようにするためのラッパーを作成する。

コルーチンとの通信方法

既存の関数 f() から coroutine.wrap() を使ってコルーチンを作成した場合。

  • メインルーチン⇒コルーチン
    • メインルーチン側から見て(実引数)
      コルーチン呼び出しの引数
    • コルーチン側から見て(仮引数)
      • 初回:f() の引数
      • 二回目以降:coroutine.yeield の戻り値
  • コルーチン⇒メインルーチン
    • コルーチン側から見て
      yield() の引数
    • メインルーチン側から見て
      コルーチンの戻り値
2.2 An Operational Semantics for Lua Asymmetric Coroutines

プログラム意味論の議論。要は coroutine.create, coroutine.resume, coroutine.yield というオペレータがどういう動作をするのかを形式的に定義してるだけ。特記する内容は特になし。

3 Programming With Lua Asymmetric Coroutines

Lua コルーチンを使ったプログラム例。
generator
一連の値を作り出し、起動するたびに呼び出し側に新しい値を返すもの。

コルーチンを generator として使い、二分木の pre order で走査する iterator を実装する例。関数再帰呼び出しを用いて、非常に直感的かつシンプルに実装している。

ちなみに gcc 3.4 付属の STL で使っている赤黒木のノード。
  struct _Rb_tree_node_base
{
typedef _Rb_tree_node_base* _Base_ptr;
typedef const _Rb_tree_node_base* _Const_Base_ptr;

_Rb_tree_color _M_color;
_Base_ptr _M_parent;
_Base_ptr _M_left;
_Base_ptr _M_right;
// 以下略

イテレーション処理のコード。FreeBSD だと /usr/src/contrib/libstdc++/src/tree.cc にある。
  _Rb_tree_node_base*
_Rb_tree_increment(_Rb_tree_node_base* __x)
{
if (__x->_M_right != 0)
{
__x = __x->_M_right;
while (__x->_M_left != 0)
__x = __x->_M_left;
}
else
{
_Rb_tree_node_base* __y = __x->_M_parent;
while (__x == __y->_M_right)
{
__x = __y;
__y = __y->_M_parent;
}
if (__x->_M_right != __y)
__x = __y;
}
return __x;
}

親ノードへのリンクがあるのが大きな違い。コルーチンだと「どこまで辿ったか」「このノードはどこから辿ってきたのか」という情報が関数呼び出しのコールスタックに暗黙に保持されているのに対して、こちらだと自前で管理する必要がある分だけ複雑になる。

ただツリーが深くなった場合、再帰呼び出しだとツリーの深さ分だけスタックを喰うから、実はコルーチン方式のイテレータはかなりメモリ食い。C++ の STL イテレータのように手軽にコピーはできない。

3.2 User-Level Multitasking

並行プログラミングにコルーチンを用いる方法について。コルーチンは本質的には独立したプログラムカウンタとレジスタをリソースとして持つプログラム実行状態なので、スレッドと同じように並行プログラミングに使える、しかも使い方簡単、という話。

ただ、ここは paper に書いてあることは額面どおりは受け取れないな。

Lua の仕様だと並列処理(同時に複数のコルーチンを実行する)は、たとえマルチプロセッサマシンでも無理。並列処理を実現するなら、今はひとつになっているコルーチンの再開 と実行終了待ち (coroutine.resume) を分けることになるが、そうなると使い方はスレッドと同じぐらい面倒にならざるをえない。

また並行処理に関しては、オブジェクト指向でステートマシンとしても実装するのもシンプル&効率的。

4. Coroutines in Programming Languages

コルーチンがあるプログラミング言語の話。

Simula

symmetric, asymmetric 両方あり。コルーチン間の階層構造は、プログラム実行時に動的に生成。同じ階層にあるコルーチンは symmetric で resume を使って切り替えられる。子階層のコルーチンは call でアクティブ化。非常に複雑。

BCPL

C言語の祖先。symmetric, asymmetric 両方ありだが、asymmetric coroutine のみを使用するのが一般的。

Modula-2

並行処理実装のための基礎構造として symmetric coroutine を導入したが、あまり使われていない。

CLU

iterator という抽象概念を導入し、呼び出し間で状態を保つことから coroutine として説明。first class object ではなく使い方に厳しい制約あり。
Python

yield文を含む関数をgenerator functionと呼び、呼び出すとプログラム中の任意の場所で再開できる first class object を返す。ただし yield できるのは、その generator function の本体中のみで、そこからさらに関数を呼び出した先で yield するのは禁止。

Stackless Python

もともと継続 (continuation) を言語機能に取り入れるために作られて、その後 generator, coroutine, microthread をサポートする方向に主眼が移る。paper は 2000 年時点の話をしてるが、サイトを見るとどうやら今でも継続して開発中みたい。

Icon

ゴール指向評価 (goal-directed evaluation) で使う generator が制約された形の asymmetric coroutine。co-expression が事実上 asymmetric coroutine (参考:Co-expressions in Icon)



個人的には stackless python のコードは読んでみようと思った。あと今時だと、C#, JavaScript の yield 文について追記かな。

ラベル:

スクリプト D&I (18) タイムライン処理

今回のスクリプト言語は、C++ で書かれたプログラムから定期的に呼び出して使うことを想定しているが、特定の時間に処理を行いたいことが良くある。たとえば対戦格闘系のゲームを作っている場合、技の発動と同時にスクリプトを走らせて
  • 0.5秒後から無敵状態に
  • 0.7秒後に攻撃ヒット判定発生(1回目)
  • 0.9秒後に攻撃ヒット判定発生(2回目)
  • 1.0秒後に無敵状態解除
といった処理をさせたい。

開始時点からの経過時間を秒単位で取得するシステム関数 sysGetTimeSec() を用意すれば、この処理は次のように書ける。

void main()
{
  while (sysGetTimeSec() < 0.5)
    sysPass(1);
  // ここで無敵状態に
  while (sysGetTimeSec() < 0.7)
    sysPass(1);
  // ここでヒット判定1回目
  while (sysGetTimeSec() < 0.9)
    sysPass(1);
  // ここでヒット判定2回目
  while (sysGetTimeSec() < 1.0)
    sysPass(1);
  // ここで無敵状態解除
}


ただし、以下の点でバグが入り込む余地が大きい。
  • 記述の順序を逆転させると動作がおかしくなる(細かいタイミング調整を行っていると、うっかり逆転させてしまうことがある)
  • やりたいことに対して余分な記述が多い
後者は C プリプロセッサマクロでも対応可能な範囲だが、エラー検出を容易にするためと、マルチスレッド対応を考えて、スクリプト言語にタイムラインを意識した記述を行うための文法を定義することに。サンプルコードは次のようになる。

timeline player_timeline()
{
  when (0.5) {
   // ここで無敵状態に
  }
  when (0.7) {
   // ここでヒット判定1回目
  }
  when (0.9) {
   // ここでヒット判定2回目
  }
  when (1.0) {
   // ここで無敵状態解除
  }
}

void main()
{
  sysThreadBeginTimeline(player_timeline); // タイムライン処理の起動
  sysThreadWaitAll(); // タイムライン処理が終わるまで待つ
}


タイムラインに沿った記述を行うためのデータ型 timeline を用意し、文法を定義。当初、字句解析ルーチンから timeline という文字列に対して型を表す TOK_CAST を返したところ、構文解析ルーチンが通常の関数とタイムライン記述を行う関数どちらか識別できないとエラーになったので、専用のトークン TOK_TIMELINE を用意して文法を見直すことに。意外と面倒だった。

そもそも変数をまとめて宣言する (int a, b; とか) ときのために node_cast に副作用設定しているのがイマイチなんだよな。

設計上の選択
  • タイムライン処理をメインスレッドで実行するのか、それとも別スレッドに分けるか?
  • タイムライン処理スレッドは、スクリプト実行時に自動的に走らせるか、それともスクリプトコード中で明示的に開始指定させるか?
  • タイムライン処理の実行終了をどう検知するか?
  • 記述順序と時刻の逆転がある場合、コンパイル時エラーとするか、コンパイラが並び替えを行うか?
今回は言語・VMをマルチスレッド対応にしてあるので、タイムラインもそれに合わせてマルチスレッド化する。タイムラインの基準となる時刻についても「スクリプト開始時点から」ではなく「スレッド起動時点から」とする。

記述順序の問題は、エラー検出もコンパイル時並び替えもたいして手間は変わらないので、スクリプトコンパイラが並び替え処理を行う方向で。

個々の when() 句は、次のように書きなおして仮想機械のマシンコードに落とす
when (2.3) {
  do_something;
}

// ---------------------------------------- //
void __when()
{
  int run = (int)(2.3 * CSCR_FRAMES_PER_SEC);

  while (run >= sysThreadGetTime())
    sysPass(1);

  do_something;
}

// ---------------------------------------- //
void __when()
{
  int run = (int)(2.3 * CSCR_FRAMES_PER_SEC);  // (*1)

loop_begin:                    // (*2)  [taddr1]
  if (!(run >= sysThreadGetTime()))
    goto loop_end;              //    [taddr2]
  sysPass(1);
  goto loop_begin;

loop_end:
  do_something;                // (*3)
}


タイムライン関数全体は、次のように考えて1 passで対応可能に。2 pass 以上にして、まじめに最適化を行えばジャンプ処理は減るが、もともと性能に重きを置いていない言語なので、お手軽に済ませる。

goto label;
label_when_1:
  // 最初の when 句に対応するコード
  return;
label_when_2:
  // 2番目の when 句に対応するコード
  return;
label_when_3:
  // 3番目の when 句に対応するコード
  return;
label:
  // when 句を、時刻の小さい順に呼び出す(コンパイル時にソートする)
  call label_when_1
  call label_when_3
  call label_when_2


call - return 処理はあるが enter - leave (スタックフレームの作成/解放) 処理は入れてない。おそらく大丈夫だと思うけど、スタック壊さないか一度よく考えてみよう。

ラベル:

水曜日, 7月 04, 2007

CScript

細かいバグつぶしと、後回しになっていた関数前方宣言の実装 (changeset 500: 6f69b70c2b45)。

しかし、たかだか 10,000 行弱のコードで、フルビルドに 3分30秒 は若干辛いモノが。C++ テンプレートを多用しているからコンパイラに負荷かかるのは分かるけど。
issei@fbsd[~/work/GameObj/tool/src/cscript] sysctl hw.model
hw.model: AMD Athlon(tm) XP 1600+
issei@fbsd[~/work/GameObj/tool/src/cscript] gcc -v
Using built-in specs.
Configured with: FreeBSD/i386 system compiler
Thread model: posix
gcc version 3.4.6 [FreeBSD] 20060305
issei@fbsd[~/work/GameObj/tool/src/cscript] wc code/* m4/* | tail -1
9854 34343 297177 total
issei@fbsd[~/work/GameObj/tool/src/cscript] time gmake depend all > /dev/null
Create [scr_sys_test_pl.h]
194.081u 15.417s 3:33.36 98.1% 4551+18204k 24+1574io 12pf+0w

開発に使ってる FreeBSD マシンは CPU が Athlon XP 1600+ という2世代前の代物なので、そろそろ買い換えても良い頃合ではある。マルチコア CPU のマシンを用意して並列ビルドできる環境にするか。

日曜日, 7月 01, 2007

週末の過ごし方

先週の休みがあまり休みにならなかったので、寝て体力回復に努める。

MotoGP アッセン戦はリアルタイムで見てましたが、MotoGP クラスの Rossi 気迫の走りに震えました。いやー、面白かった。あと個人的に応援してる GP250 の関口太郎さんもポイント獲得。おめでとうございます。

ラベル:

スクリプト D&I (17) 割り込み処理

設計で悩み中。

割り込み処理の背景から。

今回のスクリプト言語は、C++ で書かれたプログラムから定期的に呼び出して使うことを想定している。基本的なシナリオは次の通り。
  1. VirtualMachine のインスタンスをメンバ変数として持つ。
  2. 一定間隔ごとに VirtualMachine::run() メンバ変数を呼び出す。最初は main() 関数から、二回目からはスクリプトが前回実行を中断したところから処理が再開される。
  3. スクリプト側に制御が移ったら、スクリプト側では必要な処理を行って sysPass() システム関数を呼び出し動作を中断。
ただ、処理の内容によってはこのプログラミングモデルよりも、必要に応じて C++ 側からスクリプトに「この処理をしてほしい」とリクエストする形にした方が記述しやすいことがある。実は現行のスクリプト言語の枠内でも、Win32 のメッセージ処理のように
// スクリプトのエントリポイント
void main()
{
  int msg;
  for (;;) {
    // C++ 側から処理要求を取得
    msg = getMessage();
    switch (msg) {
    case MSG_EMPTY: // 処理要求なし
      sysPass(1);
    case MSG_EXIT: // 終了
      sysExit();
      // 以下メッセージの種類に応じて、処理を分岐
    case MSG_..
      // ...
    }
  }
}
みたいなコードを書けば同じことは実現できるが、メッセージループみたいなゴツい代物をスクリプトで書くとなると、それなりのスキルがある人間しかスクリプトを書けなくなり、せっかく処理をスクリプトに逃がした意味が半減してしまう。MSG_EMPTY, MSG_EXIT の処理をミスると悲惨なことになるし。

そこで、スクリプト側の関数を C++ から直接呼び出せるようにする。方法はいくつか考えられるが、シンボルテーブルをコンパイル済みのスクリプトバイトコードに残さないことを前提とすると、C++ 側に公開したい関数に番号を付けておき、C++ 側からはその番号を使って呼び出すのが良さそう。

通常の VirtualMachine::run() とは非同期のタイミングで実行されるので、この処理の枠組みを割り込み (interrupt) と呼ぶことにして、スクリプトから C++ 側に公開する関数を割り込み関数、割り込み関数につける番号を割り込み番号としておく。割り込み処理を実行する C++ 側コードは次のようになる。
VirtualMachine vm;
// 初期化処理いろいろ省略
vm.interrupt(1);
問題はスクリプト側関数への番号づけと、それを VirtualMachine のインスタンスに反映させる方法をどうするか? まず思いつくのが既存のシステム関数の枠組みを利用してスクリプト側から登録させる方法、それからスクリプトをコンパイルする時点で割り込み番号と割り込み関数を解析してファイルに書き出しておき、それを VirtualMachine に読み込む際にローダーが登録してしまう方法。

システム関数で登録
/*
* 割り込みテスト
*/
// 割り込み処理1
interrupt testIntr1()
{
  sysPrintS("intr 1\n");
}

// 割り込み処理2
interrupt testIntr2()
{
  sysPrintS("intr 2\n");
}

void main()
{
  // 割り込み関数登録
  sysIntrRegister(1, testIntr1);
  sysIntrRegister(2, testIntr2);
  sysPass(1);
}

コンパイラとローダが処理
/*
* 割り込みテスト
*/
__interrupt[1] testIntr1()
{
  sysPrintS("intr 1\n");
}

__interrupt[2] testIntr2()
{
  sysPrintS("intr 2\n");
}

void main()
{
}

実装の手間が少ないのは前者だけど、スクリプトの作成/保守効率を考えると後者の方が良いかな。あと前者のコードだと main() で登録するのは面倒なので、スクリプト読み込み時に自動的に実行される処理(コンストラクタというか Perl の BEGIN {} 関数というか) が欲しくなる。

またいずれのケースでも割り込み関数から呼び出せるシステム関数は制約しておかないとまずい。たとえば sysPass() を呼び出しても、再度そこから処理を再開することはないので意味がない。あとは割り込み関数のプロトタイプをどうするか(パラメタと戻り値は使えるようにするか?)も考えどころ。引数は、割り込み関数内から必要に応じてシステム関数呼び出して C++ 側に聞きに行けばいいから、無しでも良いか。

とりあえず、手元では両方実装してみた。分散 SCM だと、こういう時にいちいちブランチ切るとかせずに hg clone でサクサク作業始められて便利ですね。

2007/07/02 10:00追記

clone したリポジトリを Web 経由で見えるように。

ラベル: