つくづく自分が素人であることを思い知らされる。うーん。
学校着。ローラースケートかスケボーか知らないけど、それに乗っている自分を犬に引かせている人を見かける。いいなぁ、あれ。
すかたん(Tue Sep 30 00:40:54 JST 2003)
ほとんど身内向けのアンケートでねぇか。大体、パンピーがHaskell使うわきゃねーし。
身内向けのアンケートという点、まったくそのとおりで。もしかして、注意書きが必要でしたか?
あくせく働くのではなく賢く働こう: Kent Beck氏へのインタビュー(dpml)。最後の、
Beck:Javaは非常に悲観的だと思います。Javaのコンパイラに「このプログラムは実行できるか分かりません。だから実行しません」と言われたことがあるでしょう。私は、悲観的な言語の安全性とは幻想である、ということに気づいています。
は、どういう意味なんだろう? コンパイラを満足させれば、それで大丈夫だ、というのは幻想、という意味? 彼の著作物を読んだことがないのだけれど、ちゃんと読まないとなぁ。
よし、動いた。
これって、ホントに日記じゃなくてメモだな...。
そういえば、先週の秋葉原での散在散財とか、今週の温泉とかの散在散財のために、銀行行かなければ...。
うわ、ほんとに月姫アニメ化するんですね。うわー・・・。
外は明るくなってきたが、バグは取れない。
yacc の使い方がわかんない。うーーー・・・。
10月のPTT。インタプリタ生成系 Virtual Machine Builder の紹介ということで、もちろん行きます。
とても楽しみな反面、私が出来ることがどんどん少なくなっていくなぁという危機感が。
やっと騙し騙しコンパイラが通ったー。騙し騙しじゃダメなんだけど・・・。
というわけで、諸々の理由により cygwin のアップデート。
あまり騙さなくても良くなってきた。
cygwin 入れ替えたら cat が使えなくなってた。何故?
うーん、現状の奴と、バージョンをぐちゃぐちゃにしたからなようで。ヤレヤレ。
何度目かの挑戦。
なんか今日はずっとデバッグやってた気がする。そして明日もこんな日な予感。うわーん。
夢のFFXI ベンチを動かしてみるが、2000。CPUがCeleron1.4GHz じゃ、ちょっと辛いかもね。
Radeon9600 を買ったつもりなんだが、プロパティでは 9600Pro と出てくる。はて?
http://www.namikilab.tuat.ac.jp/~sasada/diary/vote.cgi?mode=show&no=7#c-9
プログラムなのにMaybeはねーよなぁ。そんなノンキな。
ということですが、Haskell をよく知らないんでわかりません。でも、「こんな感じ」という文法でプログラミングできたらいいなぁ。
main(){ ちょっとそれがあんな感じで、それを、そうそう、そんな感じ。 多分、こうするんだけど、まぁ、適当に。 でも、そこんところは上手くやっといてね。 ちゃんと、計算してから結果ここに書いとくこと。わかった? }
なぜか、C言語風にくくる。
def match_first(a,b) i = 0 while a[i] and b[i] and a[i] == b[i] i+=1 end a[0, i] end
色々ない頭を絞ってみたんですが、何も考えずに書いたのが一番分かりやすそう。
def match_first(a,b) a.size.times{|i| return(a[0,i]) if a[i] != b[i] } and a end
心情的には or と書きたい。
正規表現はよくわかりません。
なんか、誕生日が固まってるんですかね。おめでとうございます。
先日、重すぎてデキネー、と思っていたゲームが、Radeon9600 ではできるようになった。Direct3D をバリバリ使う2Dアドベンチャーゲームって、よくわからんなぁ・・・。
Radeonな幸せを体感できるのってなんかないかな・・・。
大規模プロジェクトに関わってる皆様。大爆笑してしまった。
いくつかの日記につっこみをさせてもらおうと思ったら、いくつかの理由で出来なかった。エラーだったり、つっこみページにいけなかったり、名前どうしよう、と思ったり ^^;
とりあえず、and
を利用してるのは、改行の変わりに;
を利用するより胡散臭くないからで、全然意味はないです。;
のほうが速いんじゃないですかね。
東府中の自衛隊基地は、通学路なんですが、アンテナがあるなんて知りませんでした。今度見てみようかなぁ。
嫁さんがかつてプログラミングの仕事をしていたころ、どうにも向いていない後輩に指導をしたことがあるそうな。
あるプログラムの誤りを説明していたとき、後輩(女性)は理解しようとしばらく懸命に努力した後に顔を上げ、必死の面持ちで、 「『何となく』じゃダメなんですか?」 と聞いたそうな。
嫁さんは、 「…ダメなのよ。」 と答えたらしい。
もう何年も前のこと。 その後輩の行方は杳として知れない…かどうかは知らない。
(Maybeで思い出しました。でも、HaskellのMaybeって別に『何となく』じゃないけどね。McCarthyのAMB演算子のほうがよっぽど…)
奥様はプログラマ。
おごってもらっちゃって、どうもありがとうございました。
CVSweb を試すが、annotate はできるが download が出来ない。うーん、なんでだろ。
わかった。
Error: Unexpected output from cvs co: cvs [checkout aborted]: Cannot check out files into the repository itself
だったのだけれど、cvsweb.cgi を置いたのが、CVSROOT 以下だったからか。うーむ。
パスワード入れて、終り。なかなか壮観。
CVSweb で見ると、いい加減につけてきたログがダメダメっぽい。というか、複数ファイルコミットして一行ログ、でやってきたので、ログの意味が無い。
作業したら、1ファイルづつコミット、面倒なんだよな。挫折していた xyzzy の cvs-mode を試してみようかしら。
そういえば、CVSリポジトリって、用途にごとに複数用意するもんなんでしょうか。今、ディレクトリ掘って分けているのだけど。rb/nantoka とか piece/nantoka みたいな。
while true begin eval('break') ensure p 'ensure' end end
このコードは、eval 抜きで break したのと同様の動作をしますが、こうあるべきなんでしょうか。それとも、こんなところまで面倒見なくてもいいんでしょうか。
と思ったらcvs-modeダウンロードできないのね(Nasal Daemon)。しかし、凄いページだ。
ビデオカードを買い換えるため、FF11 をやってみたいなぁと思うのだけど、やる時間があるかは相当疑問。積みゲーにするにはちと高いし。
秋葉原にきております。東洋という、喫茶店から無線Lanでつないでおります。いやー、便利な世 の中になったもんだ。
しかし、さすがにこういう喫茶店。聞こえてくる会話はアキバ系。こんなところでノー トPC広げてこんなこと書いてる私も私ですが。
家族用のPCのHDDが壊れたので、ちっちゃいの買おう、とか思って来たんだけど、なぜ か120GB HDDを買ってしまう。
あと、結局Radeon9600 買って、保険に G200 の中古。
tar.gzを作るための権限が無いのでは?
と、いうオチでした。 -p なのにディレクトリ選ぶのは、うーん。
アーカイブからcvs-mode落とせませんか。
"Not Found" と言われてしまいました>cvs-mode
実はお昼頃にアキバにいました。白色LEDのペンライトを買ってすぐ帰りましたが…
オヤヂの欲望渦巻く街 アキハバラ
cvs-mode-0.1.9.tar.gz なら落とせるようです。pre5とかpre7を落とそうとしてませんか?
あー、ほんとだ。すみません、離れていたので気づきませんでした>0.1.9
考えて見ると、「ほとんど」って、「ほとんど使ってない」なのか、「ほとんど Haskell」なのか、わからないな。前者のつもりだったんだけど。
n(略)さん、複数投票してないですか?(とか言ってみるテスト)。
初めて袋焼きそばを食す。カップ焼きそばより美味しいな。片付けが面倒だが。5袋200円。経済的にも優しい。
研究室に、もやしとかカイワレとか育てておけば、豊かな食生活に近づけることに気づく。さて、そんなに簡単に育てられるものだろうか?
うーん、もやしを育てるのって難しそうだなぁ。
カイワレはそんなに難しくないっぽい(カイワレダイコン)。熱ならPCがいくらでもあるしな。
んー、毎日水替えしないといけないのは面倒だなぁ・・・。
うぅ、飲みすぎた。
腹が減ったのでカップ焼きそばを作る。
ハックする。
悩む。
焼きそばを忘れる。
放置。
・・・。なんか、お粥を食ってる気分。
また学校に泊まる。泊まるもんじゃないな。作業効率が上がるわけでもなし。
やっぱり Haskell はマイナーらしいです。
携帯で DQ と FF ですか。凄い時代になったもんだ。
これ も携帯でできる時代がくるのかしら。
ちょっとドキドキしながら、ライバルである(勝手に決めてるし)Pybrid の処理速度と rucheme の処理速度を比べてみました。
・・・勝った!(勝負ナノカヨ)
さすがに、バイトコードコンパイルまでして速度で負けたら悲しすぎますから。
課題としては、「元のソースの情報が全部消えてる」ことか。これは困ったかもしれない。どうしてくれよう。
Scheme 使ってる?。意外と使ってる人は多いことがわかった。
こんな脳みその使い方したことがないなぁ...。
この記事の隣に「CPUの創りかた」の広告が載っているのに爆笑した。
飯を食ったら眠くなった。ダメ生活。
さて、こんどの投票の結果がどうなるか楽しみです。
なんか、某アレが進展したらしい。この間、「遅れたらどうするの?」って聞いたらキれられた(守るっつったら守るって感じで)ので、きちんと期限を守るのかと思ってたら、案の定2ヶ月遅れてるし。これだけ色々裏切られて、まだ続ける人の気が知れないが、まぁ、頑張ってください。皮肉ではなく、完成を楽しみにしてます。これでも、この作業に相当の時間を割いていたので。
文献探しは NACSIS WebCat が便利.私は中央大学に出向いて判例のマイクロフィルムからハードコピーを取るという楽しい体験をしたことが...( ← 自分トコの図書館から紹介状をもらって行くのが吉 )
お嬢様の部屋。うーん、私の知らない世界だ(知ってたらそれはそれで問題だが)。
萌々(もも)ちゃんはどうかと思う。苗字云々よりも>あひる
The Design of an Optimizing Compiler は、農工大図書館で探したけど見つかりませんでした。また、東工大図書館に行ったときに覗いて見たんですが、なかったようです。探し方が悪かっただけかもしれませんが。残念です。
また学校に泊まってしまった。いろいろと終わらない。うーん。
腹が減った・・・。カップ麺はもう嫌だ。私の体の半分はカップ麺で出来てるだろう、多分。
先日買った500円の古本が、非常に有用であることがわかった。英語の仕様読まんと・・・と思っていたのが日本語できっちり書いてある。非常に嬉しい。
先生、妹増量ってどういう意味ですか。僕にはよくわかりません。
前田さんの指摘から。ruby イテレータの展開は可能か。不可能か。可能ならどのようにすればよいか。どのような制約が付くか。不可能と言ってしまうのは、多分簡単なんだろうけれど。
動的コンパイルなら何か出来そうな予感。
腹が減ったので家に帰ろう。全然終わってない。とてもまずい。
ダメアリー。
なんか駄目な話をしていて、昔のゲームを思い出した。おもしろい麻雀ゲームはないかねぇ、って話なんだけど。
わくわく麻雀ぱにっく、と言って知っている人はどれくらいいるんだろうか。多分誰も知らないだろうから、書けるわけだけど、あれは面白かった。何がってあの馬鹿さが。なぜそこで麻雀、という理不尽さがたまらなかった。今はなきフォアナインです。
で、フォアナインといえば、GAOGAOシリーズです。あれはとても面白かった。一作目がWindowsに移植されていたのを今日知りました。2年前らしいですが。あれ、きちんと全部出せば、今でも面白いと思うんですけど、どうですかね。冒険活劇は流行らないのかな。作るのは無茶苦茶大変だろうけど、ああいうゲーム、またやりたいなぁ。Windows できっちり作り直したら、またやりたい作品の一つ。ああ、昔のゲームは面白かった。
というわけで、最近の麻雀ゲームは、真面目でいけません。もっともっと馬鹿なのがやってみたいところです。Active も、幻想曲の最新版でも出してくれないかね。3は麻雀ゲームの最高傑作だと思うんだけど。是非あれを越えていただきたい。麻雀ゲームは、PC98時代のほうが面白かったな・・・。
と、ここの読者層には絶対わからないであろう話を書いてみる。
まぁ、こんなことを考えている、だめな休日。
Design and Implementation of Object Oriented Virtual Machines。これって、大学の専攻? それとも授業科目? 後者かな。いいなー、こんな授業がある学校。
でも、最近日記(その他)で色々教えてもらえてるからな。実は凄く贅沢かもしれない。ありがたいことです。
そうか、半年かけてOOなVM作るって授業なのか。なるほど。
TOSレジスタを入れたらほんのちょびっとだけ速くなった。ほんのちょびっと。
だいたい、rucheme normal より2倍ちょっと速い。次はオペランド複合を試してみるか?
たぬきの皮算用、なかなか馬鹿でよろしいかと
Excite エキサイト : サーチストリーム>サーチストリーム。やばい。楽しい。なんか、駄目な単語が見えたりするんですが。
一応、ruby での効率のいい命令ディスパッチを考える。
user system total real 4.906000 0.016000 4.922000 ( 5.000000) # __send__ symbol 5.188000 0.000000 5.188000 ( 5.328000) # __send__ string 6.765000 0.016000 6.781000 ( 6.891000) # case bytecode; when N; method 4.047000 0.031000 4.078000 ( 4.156000) # method
最後のは、まぁ参考。Rubyじゃ無理だし。
やっぱり、 __send__ symbol
で決まりかな。
ん、計測のオーバーヘッドが結構あるか。
マクロ以外の primitive な式は実装おわり。次は peephole 最適化で遊ぶか。自己末尾再帰最適化を行うとき、ジャンプにしたいけれど、自己末尾であるかどうかを判定する方法がわからない。アドホックにやってしまうか?
[..].each{|e, i| ...}
ってやると、勝手に each_with_index になってくれると嬉しいな。
最後に、到達不可能な部分を削るようなものを作ろうとしたら、私はフローグラフを作ったことがないことに気づく。はて、どうやるんだろう。いや、頑張ればできるだろうけど、どうやれば楽ができるか。
ある文字列変換メソッド trans が、次のようなのだったとき、
def trans str str.gsub(re1, str1).gsub(re2, str2). ... end
逆変換メソッドを簡単に用意する方法はあるか。変換の対をデータとしてもっておいて、逆から変換するメソッドを定義するメソッドを書けばいいだけの話か。
青木峰郎さんの日記の「本日のリンク元」(の検索キー)を見ると、いかに青木さんの日記が多彩な話題を取り扱っているかがわかる。
なんとなく風俗系に偏っている気もする。全裸ってなんだろ。
そういえば、聞こうと思っていたことを忘れていた。今度聞こう。
学校に来て、PCのスイッチを押したら起動しない。焦る。無茶苦茶焦る。すげーー焦る。で、結局ビデオカード(G200)の不調だったらしい。今はオンボード。すげーショボイ。今週末にでも買いに行かないと・・・。金ないっちゅうのに。
SPAM が一日一通だったのが、10通くらい着てる。うーん。
うーん、evaluator とあわせてみたら、全然動かない。ダメダメ。
うーん、最初から命令を抽象的に書いてしまったのが敗因か。あと、形式的に書くとスゲーデバッグしずらい。
うーん、バイトコード化しても、2倍くらいにしか速くなってない。ショック・・・。
関数フレームの扱いが非常に不味いというのが一つ。5個も関数呼出し毎に退避してりゃ、そりゃ遅いかも。
n(言わなくてもわかるので略)さんに、マウスパッドを頂きました。これで副作用のある言語をバリバリ書きたいと思います。
しまった! shiro さんの、あの「研究開発」Tシャツは写真で撮っておくべきだった!(もちろん shiroさんも)
事故は、ある日突然に。。怖いなぁ。
到達不能…jump, returnの後、ラベルまで消す(参照されないラベルは消しておく)。末尾再帰…callの次がreturnなら(returnにラベルが付いていても可)callをcall-tailに変える。known local function (もともとcall-local)ならjumpに変える。
まんま前田さんのパクリで末尾再帰はやりました。そのとき、
call return => call-tail return
に変換したんですが、return が残ってしまった。で、return を何も考えずに消すと、他がその return を参照している可能性があって、きちんとフローグラフ作って他の影響がないことを確認してから消さないと駄目なのかなぁ? と。
call **label: return
のときは、return は消さない、というだけでいいのかな・・・。まぁ、余分な return があってもなくても、実行には関係ないんですけど。なんかかっこ悪くて(あと optimization には邪魔になるか)。
あと、フレームの関係(スタックはヒープに取るようにしました)で、call-local => branch の変換がまだ出来ていないです。これは凄くやりたいんですが。
あと、br の飛び先が return or tail-call だったら、そいつに変えるってのもやりました。
peepholeについてはWulfらの"The Design of an Optimizing Compiler"が本当に面白くてためになるんですけど、古い本(初版1975)だからなあ…あ、amazon.comで古本は買えるなあ。 Schemeでは、
(defpeep tail-merge2 tail-merge2-count (m n tag) :input ((CALL m n) (LABEL tag) (RETURN)) :output ((CALL-TAIL m) (LABEL tag) (RETURN))) (defpeep elim-unreachable elim-unreachable-count (insn1 insn2 args) :triggers (BR RETURN CALL-TAIL) :input (insn1 (insn2 . args)) :if ((not (eq? insn2 'LABEL))) :output (insn1)) (def-jump-peep branch-chaining5 branch-chaining5-count (tag1) (tag2) :input ((QUOTE #F) (BR tag1)) :jump-target tag1 :target-input ((BR-FALSE tag2)) :output ((BR tag2)) :target-output ((BR-FALSE tag2)))
なんていうルールをたくさん書きました。
このpeephole最適化のルールは、自動で最適化ルーチンに展開されるんですか?
1. パターンマッチして置き換える関数定義 2. それを表(キーは命令シンボル)に登録するコード 3. 統計(パターンがヒットした回数)を記録する変数定義 に展開されます。 メインルーチンは、
fired = false; while fired do instructions.each { |i| peeper_list = table[i.opcode] peeper_list.each { |peeper| fired = peeper.try(i) } } end
みたいな感じ。
今日は停電だったようです。気が付いたら復活してた。
direct threadingの課題としては、いくつか。
gcc しか、というのは、私が windows マンセー人間なので、やっぱり VC に対応させたいという話です。でも、switch に落とすのは悔しい。というわけで、IA32 アセンブラをシコシコ書かないといけないのかなぁ。
例外をどう実装するか、というのも一つの問題な気がします。命令複合とか考えなければ、そんなに難しくない気もしますが。命令複合の際、基本ブロックの融合(そこまでするんか)のとき、嫌な気がします。
C関数の呼び出しは... なんとなく思ってるだけですが、全然関係ないかも。
Scheme 処理系は、eval を再帰で書くと、普通の手続き型言語では call/cc などの実装で困るので、あんまりしないと思うんですが、コンパイラはガンガン再帰使っていいわけで。ちょっと書いてるんですが、結構楽ですね。へへへ。
昨日の飲み会で shiro さんが仰っていた「みんな自分の Scheme の処理系を作ってしまう」というのは、非常に納得させられる話です。
昨日の飲み会といえば、お話できない方がいらっしゃったのが残念でした。植松さんには CE についてとか、新井さんにはリファレンスについてとか。まぁ、次回があるか。
というわけで、次回もよろしくお願いします。
Symbol には singleton method を加えることができない。あるシンボルと、他のシンボルを区別したいときにはどうすればよいか。
シンボルを諦めるしかないか。アホだなー。
S式でパターンマッチさせる何かを書かないと駄目なのか。面倒だなぁ・・・。
ちょろっと作ったら、
((lambda (a) (+ a a)) 2) => 0000: push 2 0002: make_procedure 7 0004: call 1 0006: return 0007: vref 0 0 0010: vref 0 0 0013: gref + 0015: call 2 0017: return
なかなかの駄目っぷり。この状態から最適化を目指していいものか。んー。
目標は
push 2 dup gref + call 2 return
なんだろうが、難しそう。
0000: push 2 0002: br 7 0004: .. 0006: .. 0007: vref 0 0 0010: dup 0013: gref + 0015: tail-call 2
になら出来そう。ローカル変数を一個引くのがちっと面白くない。br 分も無駄なのか。でも、これ以上は面倒なだけか...。
雨。めんどうくさいな・・・。
RHG 読書会で内職。ダイナミックスレッディング direct threading vs switch vs C。
先に結果。誰か追試してもらえると面白いかも。
clock : 94586 // Direct Threading clock : 533417 // Switch clock : 47268 // C native
ソースコード。
#include <stdio.h> #include <time.h> #define NEXT() \ goto *((void *)(codes[pc])) // printf("PC: %d SP: %d TOS: %d\n",pc, sp, tos); \ #define TEST_MAX 100000 #define DEBUGP //#define DEBUGP printf int stack[0x100]; int bcodes[] = { 0,TEST_MAX, // push TEST_MAX 0,1, // push 1 2, // sub 8, // dup 6,2, // br_true 2 4, // return }; void* codes[0x100]; void* ops[0x100]; int test_tc(int flag){ int pc = 0; int sp = 0; int tos; if(flag){ ops[0] = &&inst_push; ops[1] = &&inst_add; ops[2] = &&inst_sub; ops[3] = &&inst_br; ops[4] = &&inst_return; ops[5] = &&inst_br; ops[6] = &&isnt_br_true; ops[7] = &&inst_br_false; ops[8] = &&inst_dup; ops[10]= &&inst_op_fusion_br; ops[11]= &&inst_dmy1; ops[12]= &&inst_dmy2; return 0; } tos = 0; pc = 0; NEXT(); inst_push: stack[sp++] = tos; tos = (int)codes[++pc]; DEBUGP("push => %d\n", tos); pc++; NEXT(); inst_dup: stack[sp++] = tos; pc++; NEXT(); inst_add: tos += (int)stack[--sp]; DEBUGP("add => %d\n", tos); pc++; NEXT(); inst_sub: tos = stack[--sp] - tos; DEBUGP("sub => %d\n", tos); pc++; NEXT(); inst_br: pc = (int)codes[++pc]; NEXT(); isnt_br_true: if(tos){ tos = (int)stack[--sp]; pc = (int)codes[++pc]; NEXT(); } else{ tos = (int)stack[--sp]; pc += 2; NEXT(); } inst_br_false: if(tos == 0){ tos = (int)stack[--sp]; pc = (int)codes[++pc]; NEXT(); } else{ tos = (int)stack[--sp]; pc++; NEXT(); } inst_return: sp--; return tos; inst_op_fusion_br: goto inst_dmy2; inst_dmy1: printf("dmy"); NEXT(); inst_dmy2: printf("dmy"); NEXT(); } int test_sw(){ int pc = 0; int sp = 0; int tos; while(1){ switch(bcodes[pc]){ case 0: // push stack[sp++] = tos; tos = (int)codes[++pc]; DEBUGP("push => %d\n", tos); pc++; break; case 2: // sub tos = stack[--sp] - tos; DEBUGP("sub => %d\n", tos); pc++; break; case 4: // return sp--; return tos; case 6: // if true if(tos){ tos = (int)stack[--sp]; pc = (int)codes[++pc]; } else{ tos = (int)stack[--sp]; pc += 2; } break; case 8: // dup stack[sp++] = tos; pc++; break; } } } int test_c(){ volatile int i; for(i=0;i<TEST_MAX;i++){ // none } } int compile(){ int i; int size = sizeof(bcodes) / sizeof(bcodes[0]); for(i=0; i<size; i++){ codes[i] = ops[bcodes[i]]; if(bcodes[i] == 0 || bcodes[i] == 6 || bcodes[i] == 7){ i++; codes[i] = (void *)bcodes[i]; printf("** %d\n", codes[i]); } } return 0; } #define MAX 0x7fff //#define MAX 0x700 int main(){ int i; clock_t t; test_tc(1); compile(); // printf("ans => %d\n",test(0)); t = clock(); for(i=0;i<MAX;i++){ test_tc(0); } printf("clock : %d\n", clock() - t); t = clock(); for(i=0;i<MAX;i++){ test_sw(); } printf("clock : %d\n", clock() - t); t = clock(); for(i=0;i<MAX;i++){ test_c(); } printf("clock : %d\n", clock() - t); return 0; }
やっぱ private instance variable は欲しいなぁ。
飲み会 読書会、相変わらず大変たのしうございました。
詳細はRWikiのページ(RHG読書会::東京 Reloaded)を。
prefetch ってなんだろう、と考えて、命令ごとに次の命令のアドレスをロードするようにした。
#define NEXT() \ DEBUGP("PC: %d SP: %d TOS: %d PTR: %08X\n",pc, sp, tos, ptr); \ goto *ptr; #define PRE(n) \ ptr = codes[pc + n] ... inst_push: PRE(2); stack[sp++] = tos; tos = (int)codes[++pc]; DEBUGP("push => %d\n", tos); pc++; NEXT();
ら、遅くなった。prefetch って、何を prefetch するのでしょうか。不勉強ですみません。
誤: Dynamic 正: Direct
prefetchしたら、test_tcはさらに最大2割速くなりました。
がぁ、お恥ずかしい>ダイレクト
Coppermineで、stack[sp]のかわりにポインタで*spとしたらtest_tcは1割ほど遅くなりました。そういうもの??
dynamic replicationの結果をシミュレートしてみました。 あんまり速くならないね。test_tcは、もともとBTBミスがほぼ皆無だから。
include <stdio.h> #include <time.h> #define NEXT() \ goto *((void *)(codes[pc])) // printf("PC: %d SP: %d TOS: %d\n",pc, sp, tos); \ #define TEST_MAX 100000 #define DEBUGP //#define DEBUGP printf int stack[0x100]; int bcodes[] = { 0,TEST_MAX, // push TEST_MAX 9,1, // push' 1 2, // sub 8, // dup 6,2, // br_true 2 4, // return }; void* codes[0x100]; void* ops[0x100]; int test_tc(int flag){ int pc = 0; int sp = 0; int tos; if(flag){ ops[0] = &&inst_push; ops[1] = &&inst_add; ops[2] = &&inst_sub; ops[3] = &&inst_br; ops[4] = &&inst_return; ops[5] = &&inst_br; ops[6] = &&isnt_br_true; ops[7] = &&inst_br_false; ops[8] = &&inst_dup; ops[9] = &&inst_push2; ops[10]= &&inst_op_fusion_br; ops[11]= &&inst_dmy1; ops[12]= &&inst_dmy2; return 0; } tos = 0; pc = 0; //NEXT(); inst_push: stack[sp++] = tos; tos = (int)codes[pc + 1]; DEBUGP("push => %d\n", tos); pc += 2; //NEXT(); inst_push2: stack[sp++] = tos; tos = (int)codes[pc + 1]; DEBUGP("push => %d\n", tos); pc += 2; //NEXT(); inst_sub: tos = stack[--sp] - tos; DEBUGP("sub => %d\n", tos); pc++; //NEXT(); inst_dup: stack[sp++] = tos; pc++; //NEXT(); isnt_br_true: if(tos){ tos = (int)stack[--sp]; pc = (int)codes[++pc]; NEXT(); } else{ tos = (int)stack[--sp]; pc += 2; //NEXT(); } inst_return: sp--; return tos; inst_add: tos += stack[--sp]; DEBUGP("add => %d\n", tos); pc++; NEXT(); inst_br: pc = (int)codes[++pc]; NEXT(); inst_br_false: if(tos == 0){ tos = (int)stack[--sp]; pc = (int)codes[++pc]; NEXT(); } else{ tos = (int)stack[--sp]; pc++; NEXT(); } inst_op_fusion_br: goto inst_dmy2; inst_dmy1: printf("dmy"); NEXT(); inst_dmy2: printf("dmy"); NEXT(); } int test_sw(){ int pc = 0; int sp = 0; int tos; while(1){ switch(bcodes[pc]){ case 0: // push case 9: stack[sp++] = tos; tos = (int)codes[++pc]; DEBUGP("push => %d\n", tos); pc++; break; case 2: // sub tos = stack[--sp] - tos; DEBUGP("sub => %d\n", tos); pc++; break; case 4: // return sp--; return tos; case 6: // if true if(tos){ tos = (int)stack[--sp]; pc = (int)codes[++pc]; } else{ tos = (int)stack[--sp]; pc += 2; } break; case 8: // dup stack[sp++] = tos; pc++; break; } } } int test_c(){ volatile int i; for(i=0;i<TEST_MAX;i++){ // none } } int compile(){ int i; int size = sizeof(bcodes) / sizeof(bcodes[0]); for(i=0; i<size; i++){ codes[i] = ops[bcodes[i]]; if(bcodes[i] == 0 || bcodes[i] == 9 || bcodes[i] == 6 || bcodes[i] == 7){ i++; codes[i] = (void *)bcodes[i]; printf("** %d\n", codes[i]); } } return 0; } #define MAX 0x7fff //#define MAX 0x700 int main(){ int i; clock_t t; test_tc(1); compile(); // printf("ans => %d\n",test(0)); t = clock(); for(i=0;i<MAX;i++){ test_tc(0); } printf("clock : %d\n", clock() - t); t = clock(); for(i=0;i<MAX;i++){ test_sw(); } printf("clock : %d\n", clock() - t); t = clock(); for(i=0;i<MAX;i++){ test_c(); } printf("clock : %d\n", clock() - t); return 0; }
というわけで、そろそろ帰ろう。
ツインズ7, 8話。やっぱりキャラクター違うよな。でも、資産があるって素晴らしいよな。しかし、毎回毎回入浴シーン、よくがんばってるなぁ。
MIDIといえば。昔 PC-98用に MIDIファイルを読んでシリアルを叩いてMIDI音源を鳴らすのを作った覚えが。懐かしい。最近では埃を被っている AG-10。サウンドカード内臓音源で十分なんだもんなぁ。
MIDIフォーマット、よくわかんないから見ながら解析してった気が。あれ、資料見ながらだったかな。どうやって作ったんだっけ。忘れたな、もう。
というわけで、今日もおでかけ。
考えてみると(考えなくても)スゲー貴重な体験。実はガクガクブルブルでした。
MIDI そのものは,とにかく流せば OK だから,SMF (Standard MIDI File) の資料じゃないかな.それともシリアル受信しながら解析? MIDI の仕様書は池袋ジュンク堂だとデスクトップミュージック本の棚の端にあったような.
いや、昔(高校時代)の話なんで。
って、流せばオッケーじゃないでしょ、たしか。流すタイミングとか、ホストでコントロールするんじゃ? それとも、流せばシーケンサが勝手にやってくれたのかなぁ。
MIDI には,直ちにノートオン/オフしろ,のようなメッセージしか (基本的には) ないので,ホストから送るタイミングとかは MIDI の外側で決めないと.っていうかそういう情報が付加されてるのが SMF (F は Format だったかも) なんだけど,SMF なファイルの拡張子はふつ〜 .MID だったから,SMF が MIDI そのものだと思われがちナノ鴨菜
あー、なるほど。
SMPTEタイムコードを送ることも(いちおう)できますよ。タイムコード送信だけで規格の転送速度(31250bit/s)をオーバーするんですけどね(これを回避するための涙ぐましい努力が……)。
今日ははじめての東工大。英語はきっとわかりません。
というわけで、英語わかりませんでした。というかね、日本人なんだから、日本語で質問してくれヨ(馬鹿)。いや、非常に聞きやすい英語だったと思うのだけど、脳ミソが付いていかなかった。結局わかったのは自分の知っているところだけだった。ヤレヤレ。
Parallel GC と Concurrent GC、よく違いがわからなかった。というか、パラのほうは説明してたような気がしたけど、コンカレントは説明してたのかなぁ・・・。
質問したかったのだけど、やっぱり英語に臆して出来なかった。やっぱり英語が課題だ。
松岡先生とか、千葉さんとか田浦さんとか。英語うますぎだよ。本当に日本人なんでつか? 聞き取れなかったんだもん。日本人英語は聞きやすいはずなのだけどなぁ。
というわけで逃げるように退散。
終わって、失意の中帰っていく途中、松岡研飯野さんに声をかけられる。SACSIS で居たのを覚えていてもらったようで、大変ありがたいことです。で、ご好意で松岡研のクラスタを見せてもらうことに。
いろいろ興味深いお話もお伺いすることが出来ました。非常によくわかりました。日本語だったし。
Java な人に話が聞けなかったのはちょっと残念かも。研究室の人たちがみんな忙しそうに働いていたのが印象的だった。お忙しい中、いろいろ解説していただいてどうもありがとうございました m(__)m
。
松岡研は、見学、質問 welcome だそうです。事前にメールがあれば、準備もしてくれるそうです。興味のある方は是非。
Java については OpenJIT をなぁ。あの辺の技術の詳細が、やっぱり聞いてみたいぞ。
山城さんの研究室とか、聞いとけばよかったな。一応ぶらぶらしてみたんだけど、見つけられず。
帰りに東工大図書館を覗いてみる。和書、意外に少ない。これなら農工大工学部図書館のほうが豊富かも。書籍は研究室の中でがめてる確保してるんだろうな。洋書はそれなりにあった。けど古かった。
そういえば、大岡山駅を降りたとき、一生懸命地図を探したんだけど、みつからなかった。しょうがないから人に聞くかぁ、とか思ったら目の前にあった。あれはやばいくらい近いだろ。しかも、学校広いし。
あまりに眠いので4時間ほど寝てしまった...。脳みその中GCしてくれてるような気はするんだが、きっとバグ持ちに違いない。
しかも風邪ひいたっぽい...。
RHG に shiro さんが・・・。これは楽しみ。
というか、明日という日程はそういう意味か・・・。どうなのかなって思ってたけど。楽しみです。
うーん、明日はちゃんと髭そってスーツ着ていかないとだめかな...。社会人のコスプレで...。
というかね、実行時の命令複合って、Rava で試していたアプローチだったのですよね。
単純にくっつけただけじゃ(Ruby は最適化やらないから)ダメダメなので、アドホックに(少ないパターンについて)組み合わせるものを作って。あれをもっとシステマティックに行うようにするって話なんだろうなぁ。
漆塗りマウスのHomePage(simpl-ml)。スゲー。欲しい。
VM上での分岐予測って、意味あんのかなぁ? よく読んでないけど・・・。
簡単な例。
=ipush arg1 push(arg1) =iadd a = pop() b = pop() push(a+b) ---- ipush 10 ipush 20 iadd ==> ipush_ipush_iadd arg1, arg2 => (1) direct concatenate push(arg1) push(arg2) a = pop() b = pop() push(a+b) => (2) optimized concatenate push(arg1 + arg2)
VMgen では、直接くっつけたのしかやらないから、余計なスタック操作が残るじゃーん。とか思ってたんですが、gcc に食わせて、最適化させるのですねぇ。コピー伝播でこれが最適化されるとは・・・。ズルイゾ。
最適化が十分でないコンパイラを使うとき、このアプローチは使えないよなぁ、多分(とか言ってみる)。
=ipush arg1 arg1 =iadd pop + pop
こう書かせれば、さっきの最適化はできるような気がするなぁ。
というか、vmgen の記述方法はまんまこれなんですね・・・。
なんか、正規順序でリダクションしてくような処理系にすれば、最適化が容易に出来るような気がしてきた。ほんとか? なら、S式で命令記述作ってみるかな?
(define-inst (ipush arg1) () arg1) (define-inst (iadd) (i1 i2) (+ i1 i2)) (define-inst (isub) (i1 i2) (- i2 i1)) ipush 10 ipush 20 iadd ipush 100 isub (define-inst (ipush_ipush_iadd_ipush_isub arg1 arg2 arg3) (ipush arg1) (ipush arg2) (iadd) ;; reduction (ipush arg3) (isub)) (define-inst (ipush_ipush_iadd_ipush_isub arg1 arg2 arg3) (+ arg1 arg2) (ipush arg3) (isub) ;; reduction ) (define-inst (ipush_ipush_iadd_ipush_isub arg1 arg2 arg3) (- (+ arg1 arg2) arg3))
正規順序関係ないし・・・。つーかこれリダクションじゃないやん。
って、これは逆アセンブルしてるだけじゃん・・・。
オペランドを引数、じゃなくて、スタックの値を引数にとることにしてみよう。
rava をこれで書き換えてみようかなあ。面倒だなぁ・・・。
というかね、私の修論研究って、一応 SMTアーキテクチャプロセッサを有効に活用するためのシステムソフトウェアなんですよ。
頭がふらふらする。風邪かな・・・。
バイトしよ、バイト。全然終わってないぞ・・・。
今日の図書館。
うーん、中田先生のこの本を借りたのは何度目だろう。
bit、読みました。なんか、知ったような単語が既に 80年代のトピックなのですね...。
図書館の bit 、ページが切り取られている部分があった。どこにでも馬鹿はいるもんだ。やれやれ・・・。
前田さんの命令セットを S式にしたらこんな感じ?(仕事しろよ俺...)
- (get-arg n) は、予約語とする。 - body 中の操作名は、実装依存の処理と変換するとする。 要するにそれに相当する ruby scriptに。 - define-inst、defin-inst/ctrl の引数の最初は、オペランドの数。 オペランドは (get-arg n) で n番目のオペランドを取得。 * 返り値を push する ;; (vref index depth) (define-inst (vref 2) (get-local-env (get-arg 1) (get-arg 2))) ;; (gref symbol) (define-inst (gref 1) (get-top-env (get-arg 1))) ;; (vset index depth) (define-inst (vset 2 val) (set-local-env (get-arg 1) (get-arg 2) val)) ;; (gset symbol) (define-inst (gset 1 val) (set-top-env (get-arg 1) val)) ;; (quote const) (define-inst (quote 1) (get-arg 1)) ;; (discard n) (define-inst (discard 1) (let ((x (pop))) (dotimes (- (get-arg 1) 1) (pop)) x)) ;; (make-procedure label) (define-inst (make-procedure 1) (make-procedure (get-arg 1))) * 最後に push しない ;; 環境を一個中に作っちゃう ;; (make-env n) (define-inst/ctrl (make-env 1) (make-env (get-arg 1))) ;; (return) (define-inst/ctrl (return 0) (pop-control)) ;; (br label) (define-inst/ctrl (br 1) (jump (get-arg 1))) ;; (br-false label) (define-inst/ctrl (br-false 1 cond) (if (not cond) (jump (get-arg 1)))) ;; (br-true label) (define-inst/ctrl (br-false 1 cond) (if cond (jump (get-arg 1)))) ;; (call narg) (define-inst/ctrl (call 1 proc) (if (c-proc? proc) (call-c proc (get-arg 1)) (push-control (get-arg 1)))) ;; (call-tail narg) (define-inst/ctrl (call-tail 1 proc) (if (c-proc? proc) (call-c proc (get-arg 1)) (set-control (get-arg 1)))) ;; (call-local label narg depth) (define-inst/ctrl (call-local 3) (push-control-local (get-arg 1) (get-arg 2) (get-arg 3))) ;; (pop) (define-inst/ctrl (pop 0) (pop)) ;; (check-stack n) (define-inst/ctrl (check-stack 1) (check-stack (get-arg 1)))
ちょっと修正。make-procedure の nvars, nargs, has_rest? が何を表すのかわかんなかったので省く。またちょっと修正。set! 系を付ける。ruby ではリファレンスを作るとやっぱり処理が遅いので、arrayのリストでいいや、と。それとも、hash のリストにしようかな。どうしようかな。make-env も追加。let を変換して利用。これは、もしかしてダメな方法かもしれないなぁ。もうちょっと考えよう。
こいつから、rubyソースへの変換はきっと楽なんだろう。楽なことにしておく。rucheme/c は、全てこいつらにコンパイルするようにしてしまうか。んで、こいつらから、動的に複合命令を作っていく。まとめられるだけまとめてしまえばいいだろう。これなら簡単に出来そうだ。
やっぱり ruby は eval が使えていいなぁ。はっきりいって、汎用的アプローチじゃないけど。分岐の部分で工夫が出来そう。
ruby では、switch & goto な手は使えない。case/when では遅すぎるし、そもそも goto に相当するものが無い。なので、call threading にするしかない(と私は思ってる)。とすると、br がもうちょっと纏められる形になると、もっと良いのか。
って、なんか ruby への translator になってきた・・・。
「優勝しないタイガースを愛する会」、その夜解散 。うーむ。asahi.com もこういう記事書くんだ。
勢い余って、上記S式から、VMループを処理するような何かを書いてしまった。やることはテキスト処理なので、Ruby が楽だなぁ、やっぱり。S式弄りでも。
じゃぁ、雑誌記事楽しみにしてます。うーんと恥ずかしいやつ :)
シリーズ Vol.36 THE 娘育成シミュレーション。2048年つったら、すでにリアル娘も巣立ってるんじゃないか。いや、凄い可能性の低い未来ですけど。
なんか、この表記なら、scheme上で実装するのが楽そうやなぁ。Java用を書いて、Schemeで動かしてみようか。しかし、200命令書くの面倒くさいんだよな・・・。
うーん、S式操作は scheme でやったほうが、やっぱ楽かも。rucheme/normal でここだけはやるようにしようかなぁ・・・。
VM駆動部分はできたので、肝心の compile 部分を書かなければ。って、ほかにやることあるだろ、俺・・・。
私みたいなダメ人間を尊Kしちゃいけません.複合命令については RISC が流行る前(つまりヘネパタ以前)の文献に結構基本的なコトが載ってるように思います.1980 年に bit 別冊で出てる「ダイナミック・アーキテクチャ」は,眺めるといいかも.たしか,農工大図書館では bit のバックナンバーと一緒になってたような.
尊Kするしないは置いといて、飲み代はよろしく。
「VM上での」つーか、VMを実行するx86とかAlphaとかが激しく分岐予測ミスするので、これをなんとかしようという話。threaded codeの実行時間の半分以上が分岐予測ミスペナルティだとか。
あぁ、そうだったんですか。なるほど。やっぱりちゃんと読まないとダメですねぇ。
http://www.shudo.net/publications/klab-200106/ 前田さんに教えてもらったりなんだりで知ったいくつかのインタプリタの性能向上テクニックをスライドにしたものです。なんとか threading とか、stack caching とか。スライド 12枚目に、dispatch 処理部を VM 命令間で共有しないことで分岐予測ミスが減るんじゃないか? と書いてしまったのですが、ほんとでしょうか。
減ります。分岐命令処理部のif文のthenとnextでディスパッチ命令を分けるだけでも減ります。vmgen論文の5.4節参照。
その節を読んで、やっとBTBの意味がわかりました。
大堀先生の本の解答集ってないでしょうかね。独学で解答例がないとつらいです。
じゃぁ、今度MLで読書会やりますか、新潟で(ぉ。
いいっすね〜。SICP読了後はSMLに戻るつもりです。
Haskell にしましょうよ。> 新潟のSさん
間をとって、LazyMLってのはどうですか?> n????n ん、?は4つかな。
Lazy な ML => Haskell ってイメージがあるんですが、ダメですか、こういうの。
日記で、けっこう面白い議論になっているのがあるんだけど、私の日記では1日しか(表に)表示しません。これってもったいない気がするんだけど、どこかで議論だけを保存させてくれるような wiki って無いものでしょうか。うちでは wiki 禁止らしいんで・・・。
多分、もりあがってるのってVM実装の話だと思うのですが(もう忘れてるらしい)。
alternative第二段。
スタック一本 vs スタック一杯。とりあえず、メソッド(関数)フレーム用制御スタックを用意するか、しないかってところで。
私は、スタック一本のほうが次の点でいいと思っていました。
でも、制御用スタックを分けることで、末尾再帰が簡単になるようです。また、フレームを積む時期がフレキシブルになるような気がします。
さて、どっちが良いか。速度よりも(言語仕様との兼ね合いによる)実装のしやすさにかかるのかもしれないな。これは。
VM命令構成法。基本命令が n 個ある。この n 個が2個つながるようなパターンは n * n 個ある(ただし、分岐命令などの後には命令はつかないこととする。分岐遅延なんて勿論なし :-P)。n * n 個を手で実装するのは大変である。幸い、n個の命令は既に実装した。ならば、その n 個の命令を利用して、それが2つならんだパターンを楽に作ることが出来ないだろうか。
単純に並べると、まぁ出来なくは無いが、最適化したものではない。基本命令処理を並べたものを勝手に最適化してくれるような、メタ命令記述があればよい。これを繰り返せば、最初に n個の処理プリミティブを書くだけで、基本命令を 1〜m個並べた Σ n ** i (1<=i<=m)個の命令を作ることができる。
さて、こんなメタ記述は可能か。
Optimizing direct threaded code by selective inliningの手法は、ただくっつけているだけなような気がする。元になるアセンブラが何処からやってきたのか不明(fig.8)。また、アセンブラを利用すると、移植性が嫌な感じ。
まぁ、べらぼうに沢山乗っけても、I-cache に乗らなくなりそう。一つ5word だとして 20byte、4KB I-cache として、400命令くらいが上限かな。
んで、実際に動かしてみて、つかわねー命令を統計とって、勝手に消してくれんの。きっと楽な世界。
もちろん前田さんの論文を自動化できないか、って話です。
スタックマシンを前提とすれば、そんなに難しい話では無さそう。自分では余裕が無いのでやらないだろうけど。
あ、特定オペランドの命令化を忘れていた・・・。
そうか、頻出命令(or 命令+頻出オペランド)を抽出するのと、命令融合の自動化は別の話か。
とか、偉そうに書いたけど、誰か既にやってそうだな。
HikiFarmを用意するとか。http://www.namaraii.com/hiki/?HikiFarm
wiki が駄目なんで、hikifarm も駄目だと思うのです。
PicoJava が instruction folding を動的にやってたなあ、と思い出した。> 複数命令をつなげたものを最適化
要は、命令セットを自動生成、もしくは、自動的に最適化したい、ってことなんだと思います。「最適」の基準が山ほどありそう。命令数、インタプリタでの性能、コードサイズなどなど。
中西さん(九大)が何年か前に組み込み向けにやってた研究を思い出した。>命令セット自動最適化 コードサイズを小さくするために、命令に対応するビット列をハフマンエンコーディングで作る、ってやつ。
東工大の脇田さんが去年のILCでVMの記述言語の話をしてました。インストラクション仕様の記述からVMコードが自動生成されるというものです。命令の統合は自動じゃないけど、ちょっとした指定でできたような。いろいろ試してみるには便利かもしれません。
中西さんのは論文になっていたような...新しいシステムソフトの潮流だったかな。コードサイズがひとつの尺度。このテのって、何命令まで見るか、オペコード、オペランド、果ては分岐までやりだしたりして。
Ertlらのvmgenは、複合命令の最適化をCコンパイラに任せてます。実行時に最適化するのはむずいなあ。Etrlらの新しい論文だと、複合命令の効果としてはBTBミス削減が大きいらしい。あと、かなり無節操にインタプリタを大きくしても、キャッシュミスは問題にならないらしい。(素朴なコンパイラ並みの速度が得られて、移植には1時間とか書いてある…)
東工大脇田さんの論文、のことでしょうか。中西さんのは見つからず。ところで、スタック操作って、C言語で最適化されるものでしょうか?
最適化は、まぁ、速いのが正義っていう方針で。
命令複合のために抽象化されなければならないのはオペランドとスタック操作だと思っています。なので、その辺をごにょごにょしてくれるようなシステムがあれば・・・。全部形式記述にしなくてもいいと思うんですが、甘いですかね。
Gauche の disasm の結果を見ていると、Gauche VM の命令体系では、値をスタックではなくレジスタに格納しているように見える。ScmVMRec->val0 かな、多分。
まつもとさんの未踏の報告書でも acc というレジスタを用意していた。
JavaVM は、全てスタックに対する操作。SmallTalk-80 も、多分スタックに対する操作になっていると思う。
どちらを取ればいい、ということに関する、定量的な評価って、どこかの論文でやってるんでしょうか。調べようにも、どういう単語で調べていいかわからないので。
これに関しては、結構悩んだんだけど、どっちがいいんだかよくわからない。結果を使いまわすような言語の場合、レジスタへ対する演算のほうがいいんだろうか。あと、レジスタマシンへの応用が楽になるのかもしれないなぁ(結果格納レジスタのリネーミングを頑張ってみたり)。あと、関数(メソッド)リターンのコストが安くなったり・・・。
CPU でも、arg用、return用レジスタってあるしなぁ。RISC じゃ。
なんか、レジスタにしたほうがいいような気がしてきた。ひよるか。
真面目に得失を考えてみる。命令の結果がスタックに格納されるか、レジスタに格納されるかの対比。
■レジスタ
■スタック
性能的には、スタックへ格納する率が多ければ後者が有利になるのかな。命令を分けたことによる性能差がどれくらいになるのかよくわからないが。
とにかくメソッド呼び出しを速くしなければならない ruby を考えてみると、レジスタのほうが有利、なのかな。
あぁ、すげー初歩的なところで悩んでいる気がする。
プロセッサ作るのなら、フラグによってスタックへ格納するかレジスタへ格納するか分けてもいいんだろうけどな。
Implementation of Stack-Based Languages on Register Machines 面白そうだな。しかし、これで卒論なのか・・・。
とりあえずレジスタにするように書き換えてみるか・・・。
今の ruby の C用 API を使おうとすると、拡張ライブラリのメソッドコールって全部クリティカルセクションにしないといけないのかな。うわ・・・。あれ、そんなことないのかな? うーん?
既存の C コードとの、例外のやり取りってどうやるんだ・・・。raise in C code はフックできるだろうが、rescue in C code って無理かな・・・。C の rescue の方法って知らないけど。push_xxx するのかしらん。
今日の古本。久しぶりに古本屋で散在。今月の読書会(の飲み会)は、マジでヤバイ。
また本棚の肥やしが増えた。
新・闘わないプログラマ No.298。すげー。確かに見てみたい。
nobsun さんの日記のコメント、長すぎ。
高性能プログラミング。あー、非常にわかりやすい。一覧が素晴らしい。
エイヤっと論文を整理(といってもファイルに押し込んだだけ)。いい加減にカテゴライズしたら、4つほどファイルが余った。
論文100個くらいあるんじゃなかろうか。もちろん、まじめに読んだ論文は10個にも満たない(マテ)。
先日見つけて「おー、すげーこの論文」とか思ってたのが、昔々に刷ってあったことを発見する。うーむむ・・・。
やっぱ、刷るだけ刷って、安心してしまうのはよくないな。なんとかならないものか。
いろいろやってたんだが、結局明日のゼミの資料は出来ていない。どうしたものか。
『イリヤの空、UFOの夏』特製ブックレット付きドラマCD。悩む。悩む。そんな金ないってば。でも、無銭飲食列伝はとてもとても聞いてみたい。聞いてみたい。うーん・・・。別にこつえー氏の絵に転んだってわけではなく。
ruby で「箱」(間接参照のための何か)を作るには、どれが一番効率がいいのか。とりあえず思いついたもので、クラスと配列とハッシュで確認。
require 'benchmark' include Benchmark obj = Object.new class <<obj attr_accessor :ivar end obj.ivar = nil ary = [nil] hash = {:var=>nil} LC = 1000000 bmbm{|bm| bm.report{ LC.times{ obj.ivar = 10 a = obj.ivar } } bm.report{ LC.times{ ary[0] = 10 a = ary[0] } } bm.report{ LC.times{ hash[:var] = 10 a = hash[:var] } } } #=> user system total real 1.875000 0.000000 1.875000 ( 1.875000) # obj 1.781000 0.000000 1.781000 ( 1.781000) # ary 2.032000 0.000000 2.032000 ( 2.047000) # hash
array が速くなったのか、hash が遅くなったのか。arry だろうな。
まぁ、array が一番速そう。
うわ、雨降ってる。帰れねぇ。
というわけで、ノートPCに入れていた Ever17 をやる。備えあれば憂いなし(違)。
性能にはいろんな事情が絡んできますが、vmのループが最終的に機械語に落ちる場合は、vmのレジスタがCPUのレジスタに載ることが期待できる分だけ(レジスタ使用が)有利かなあ。(Gaucheでは、val0レジスタはvmループのローカル変数で、必要な場合だけScmVMRec->val0へと退避されます)。ただ、命令列をコンパクトにするのも結構性能に効いて来るので、その効果が期待できるなら全部スタックの方が良いかも。
Gauche VM命令セットを決めたのは、shiroさんの勘が主ですか?
スタックマシンのVMにレジスタを持たせるような半端なこと(超失礼)しないで、JITコンパイルしようぜ! とか言ってみる
勘という程のこともなく、何も工夫のない命令セットだと思います。 とりあえず動かすことが先決で、そのあとチューンすればいいと思っていたので。 実際に動かしてみながら若干調整したところはあります。 ちょっと変わっているところがあるとすれば、引数フレームより先に 継続フレームを積むため、PRE_CALL, PRE_TAILという命令があるところかな。 これも別に私のオリジナルではなく、「継続フレームを先に積んでおくとtail callが 簡略化されるよん」というのをどっかで読んだからです (R.Kelsey "Tail-Recursive Stack Disciplines for an Interpreter" だったかな)。 個人的な経験では、VM命令セットよりもスタックフレームのデザインが重要かと思います。
いや、非常に納得いく仕様だと感じました(全て読んだわけではありませんが)。先にフレームを積むのは、Smalltalk でもやっているようでした。私も先にフレームを積むほうがいいんじゃ、とか思ってます。とか言う。
コードが小さくなったところでどうせアクセスはワード単位だとか、 スタックマシンだと命令の並べ変えが面倒臭そうだとか、怪しげな 判断基準を持ち込んでみたり。
rvm だとか vvm だとか怪しく汎用 VM の実装を提示してみたり。
コードが小さくなれば、命令フェッチ&デコードの手間が減るからいいんじゃないでしょうか。命令の並べ替えって、なんですか? JITコンパイル?
fetch はデータがアクセスが word align だったりすると byte code に なってたとしてもあんまり嬉しくないかもしれないという話です.decode はあんまり考えてません.少ない方が表引きも速いのは確かでしょうけど, byte 単位でメモリアクセスするより word 単位のが.... ああ? packed byte array にできたらキャッシュにやさしくて速くなるのか? やっぱり適 当に考えてもまともな判断基準にならない....
命令が 256 個までに収まっててくれると 32bit register で 256 個のレ ジスタ三つ指定してちょうどとか,そうすると (operation, destination, source, target) みたいな四つ組がそのまま表現できるとかそんな感じと か.どのみち定数のオペランドがな.... R3000 みたいにジャンプの飛び先 に制限があったり,word いっぱいの整数を即値で扱えなかったりしそうだ.
命令の並べ替えってのは VM レベルでも最適化が効くかもしれないという話 で,実はわざわざ VM 命令レベルでやらんでもという気がしてたんで怪しい 話と :-P byte code を word 単位にパックして扱う VLIW や EPIC な VM は 楽しいかもしれないけど多分自己満足 :-)
あー、1命令のサイズの小ささの話だったんですね。私はプログラムの命令列の短さを考えていました。私も個人的にはバイトコードなんてダメっぽいので、ワードコードにしようと考えいます。いまどき、そんなところでメモリけちる必要はないでしょうから。命令列に対するD cache miss ですが・・・。うーん、シーケンシャルな読み込みなんで、そんな影響ないような気がするけどなぁ。
レジスタマシンのVMは・・・どうなんでしょうね。Parrot、結局どうなるのかな。
VM命令レベルでの命令の並べ替えについては、並べ替えとはいえないかもしれないけどpeephole最適化による命令の置き換えは有効だそうです。VLIW なVMは考えたこともなかったけど、スーパースカラなVMは4年のときやろうとしてました :)。まだ野望は捨ててません。捨てたほうが賢明だろが。
VMにとって何が最適かは、目的を絞らないと発散しますね。スクリプト言語では、コンパイルにそんなに時間をかけられないという事情があります。Hotspotだけ徐々に最適化してくのはありかもしれませんが。ネイティブコードに落とすなら最初からフルコンパイルしちゃえばいいような気もします(普通のCommonLispシステムはトップレベルで式をタイプするたびにネイティブにコンパイルしているけど、あれはJITとは言いませんよね)。
Gaucheについて言えば、現在は1命令1ワードのトークンディスパッチ方式で、定数は小さいものはVM命令のビットフィールドに押し込み、それ以外はそのまま命令列に含ませています。よく出てくる命令シーケンスについては、合成命令を作って命令数を減らすと性能向上に若干効果がありました。I cacheに載らなくなるくらいVMループが大きくなりさえしなければ、CISC的な命令セットにした方がよさげに思います。
最速インタプリタをうたうQschemeという処理系はthreaded codeを使っているので、一度Gaucheでも試してみたいと思っています。元祖Pentiumではデータとコードを混ぜると良くないというベンチがありましたが…
暑い! 暑すぎて目が覚めた。なんだこの気温は。
Ruby 使ってる?の結果を見ると、Rubyは知らない人が居ない、メジャーな言語ということがわかりました。つまり、そのへん歩いている人に聞いてみれば、みんな Ruby って知ってるってことですね。
プログラミング言語のユーザビリティ、というテーマから、マクロのユーザビリティ、という話になってきているようですが。
eval ってどうなんですかね。あれは cpp みたいなもん(凄い乱暴だ)だと思うんですが。いや、cpp は乱暴すぎるか。
ruby の attr_reader とかって、言語の機能っぽい(予約語っぽい)ですけど、そういうわけじゃないですよね。
暑いので学校に避難するか・・・。
コンパイルするようにすれば、使い物になるかなぁ。やってみようかなぁ。どうしよ。
やばい、MR の実行ログを表示したまま席を立ったら、妹が部屋に居た・・・。
東方妖々夢いいですよー
いくらよくても喫茶店でやるものでしょうか?(いや、デモだけど)
正規表現ってよく知らないんですが。
r1 = /(a)/ r2 = /(b)(c)/ r = /(#{r1})(#{r2})/ r =~ 'abc' p [$1,$2,$3,$4,$5] #=> ["a", "a", "bc", "b", "c"]
r の $1,$2 は 'a' と 'bc' になって欲しかったけど、やっぱり無理か。やはり racc を使うか。
すべてのパターンを根性で並べるのは多分ダメだろうなぁ。
http://mtndbys.s14.xrea.com/seezle/seezle/diary/?date=20030912#p05
匿名で(臆病者)つっこもうと思ったけど長いのでこっちに書く。
proper な理由? 文脈からしていーかげんな理由ってことかしらん。
個人的にはどんな理由で行ってもいいのだけど、他の人の足をひっぱってくれるな、と思ったりする愚痴。
もちろん、やる気があるけれど足をひっぱってしまうのはしょうがない、それは教育者側の努力で底上げしてしかるべきなのだが、「別に俺これやりたいわけじゃないしー」とかいって努力もしない馬鹿のために教育のレベルを下げている現状は、やる気のある学生から見ると非常に不愉快(自分にはやる気があったか、という点については後述)。
別に勉強(教育を受ける)だけが大学じゃないから一つの側面でしかないんだけどさ。
それに甘んじてレベルの低い教育しか行わない大学もダメなんだが...。(これは、大学側にたってみれば、「しょーがねーだろ」といわれるかもしれないな。だからそっち側に回りたくないんだけど...)
レベルの低い教育⇒やる気がなくてもなんとなく卒業できる⇒やる気がない奴が集まる⇒(最初に戻る)
という悪循環。逆のスパイラルにするには英断が必要なんだろう。大学選びは大切だということを、今更になって思う。でも、高校生にはどの大学がどうだ、なんてわかんねーよなぁ。
やる気がある奴は自分で勝手にやる、そもそも大学なんて教えてもらいに行くもんじゃない、と言われると、まったくそのとおりなんだけど。だけど金払ってんだもん、得られるものは多いほうがいいじゃん。
え、俺っすか? 結局独学も何もしてなかったやる気のない学生だった。だから、今知らないことばかりでアップアップしてるわけだ。ヤレヤレ。4年になってから勉強しだしたようなもんだよ...。そもそもこんな人のせいにするような文を書いてる時点で甘えてるし。まぁ、甘えてしまうから、縛りというものがあるといいなぁと思うのだけど。現在は適度にしばり(修論)があって、適度に余裕(修論は来年度末提出)があって、とてもいい感じだけど。
そういう点でK本さんは凄いよな。自分でどんどんいろんなことやってんだもん。いや、ほんと尊敬してます。多分。ということで今月の飲み会、もとい読書会はおごって下さい。温泉行くので今月ピンチなんです。
教育といえば、電通大ではプロトコルスタック2人、コンパイラ2人、CPU2人の6人チームでそれぞれの仕事を半年でするって実習があるらしいと聞いた。いいなぁ、こういう実習。とても楽しそうだ。
東大のCPU・コンパイラ製作実習をリファーするようなものらしいけれど。まぁ、大学側は非常に大変そうだが・・・。
いや、農工大にもシステム製作実験っていう、2 or 3人でなんか作れっていう話もあるんですけどね。まぁ、これはこれで、自由度が高くて楽しかったなぁ。研究の方法とか何もわからず、とにかくやるだけに終わっていたが。もうちょっと色々知っていれば、もうちょっといろんなことが出来たと思うんだけど。1年から毎年これやってくれれば楽しかったんだろうになぁ。
やっぱり色々と選べるようにするべきだよな。一律同じカリキュラムってのは、やっぱりおかしい。なんで今更 is_alpha とかを再実装せにゃならん。しかも比較使って(と、is_alpha を使って実験レポートを書いたら×をつけられたことを未だに根に持っている)。
えーと、あまりに自分勝手な意見なので、上記は無視してください。
でも、飲み会はおごって下さい>岸Mさん
Rucheme による置き換えモデルの表示。
<== (define (inc a) (+ a 1)) ==> <== (define (dec a) (- a 1)) ==> <== (define (+A a b) (if (= a 0) b (inc (+A (dec a) b)))) ==> <== (define (+B a b) (if (= a 0) b (+B (dec a) (inc b)))) ==> <== (+A 5 7) (+A 5 7) (if (= 5 0) b (inc (+A (dec a) b))) (if #f b (inc (+A (dec a) b))) (inc (+A (dec 5) b)) (inc (+A (- 5 1) b)) (inc (+A 4 7)) (inc (if (= 4 0) b (inc (+A (dec a) b)))) (inc (if #f b (inc (+A (dec a) b)))) (inc (inc (+A (dec 4) b))) (inc (inc (+A (- 4 1) b))) (inc (inc (+A 3 7))) (inc (inc (if (= 3 0) b (inc (+A (dec a) b))))) (inc (inc (if #f b (inc (+A (dec a) b))))) (inc (inc (inc (+A (dec 3) b)))) (inc (inc (inc (+A (- 3 1) b)))) (inc (inc (inc (+A 2 7)))) (inc (inc (inc (if (= 2 0) b (inc (+A (dec a) b)))))) (inc (inc (inc (if #f b (inc (+A (dec a) b)))))) (inc (inc (inc (inc (+A (dec 2) b))))) (inc (inc (inc (inc (+A (- 2 1) b))))) (inc (inc (inc (inc (+A 1 7))))) (inc (inc (inc (inc (if (= 1 0) b (inc (+A (dec a) b))))))) (inc (inc (inc (inc (if #f b (inc (+A (dec a) b))))))) (inc (inc (inc (inc (inc (+A (dec 1) b)))))) (inc (inc (inc (inc (inc (+A (- 1 1) b)))))) (inc (inc (inc (inc (inc (+A 0 7)))))) (inc (inc (inc (inc (inc (if (= 0 0) b (inc (+A (dec a) b)))))))) (inc (inc (inc (inc (inc (if #t b (inc (+A (dec a) b)))))))) (inc (inc (inc (inc (inc 7))))) (inc (inc (inc (inc (+ 7 1))))) (inc (inc (inc (inc 8)))) (inc (inc (inc (+ 8 1)))) (inc (inc (inc 9))) (inc (inc (+ 9 1))) (inc (inc 10)) (inc (+ 10 1)) (inc 11) (+ 11 1) ==> 12 <== (+B 5 7) (+B 5 7) (if (= 5 0) b (+B (dec a) (inc b))) (if #f b (+B (dec a) (inc b))) (+B (dec 5) (inc b)) (+B (- 5 1) (inc b)) (+B 4 (inc 7)) (+B 4 (+ 7 1)) (+B 4 8) (if (= 4 0) b (+B (dec a) (inc b))) (if #f b (+B (dec a) (inc b))) (+B (dec 4) (inc b)) (+B (- 4 1) (inc b)) (+B 3 (inc 8)) (+B 3 (+ 8 1)) (+B 3 9) (if (= 3 0) b (+B (dec a) (inc b))) (if #f b (+B (dec a) (inc b))) (+B (dec 3) (inc b)) (+B (- 3 1) (inc b)) (+B 2 (inc 9)) (+B 2 (+ 9 1)) (+B 2 10) (if (= 2 0) b (+B (dec a) (inc b))) (if #f b (+B (dec a) (inc b))) (+B (dec 2) (inc b)) (+B (- 2 1) (inc b)) (+B 1 (inc 10)) (+B 1 (+ 10 1)) (+B 1 11) (if (= 1 0) b (+B (dec a) (inc b))) (if #f b (+B (dec a) (inc b))) (+B (dec 1) (inc b)) (+B (- 1 1) (inc b)) (+B 0 (inc 11)) (+B 0 (+ 11 1)) (+B 0 12) (if (= 0 0) b (+B (dec a) (inc b))) (if #t b (+B (dec a) (inc b))) ==> 12
これ、教育用に使えないかなぁ。使えないかぁ。
レキシカルスコープの場合、静的にバインディングの位置が決まるので、ギチギチにコンパイルするときはとくに何も工夫は要らない。○か×か。
インタープリタを作っていて、定数の再定義が便利であることを悟る。うーむ。
いいなぁ、それやりたかったなぁ。どこの学科なんだろう。
この話は昨日、鈴木(貢)さんに聞きました。
自分はサークルやってて授業はいい加減だった人間ですが、 読んでて納得して自分が反省すべき部分もある気もするけど、 やっぱり納得いかない部分もあるような気がします。うーむ。
早稲田 情報学科では、12 bitプロセッサ作ったよ。教科書通りのものを CAD ソフトに入力すれば出来るんだけど、自分達でイジる余地があって、ココとアソコは同時に動かせるなあ、とか考えてシーケンサをイジるのが楽しかった覚えがある。教科書はこれ http://www.saiensu.co.jp/books-htm/ISBN4-7819-0765-2.htm
CPS じゃないよなぁ、これ。やっぱり。continuation を適用するとき、フレームコピーしてちゃ・・・。とりあえず正しく動くけど、なんか違う。
一度 continuation 作ったら、そいつに対して破壊的な何かをしちゃいけないんだな。で、そうするためにはリストにしないと・・・。
処理速度測定のため、新しいのにS式つっこんでみてたら。なーんか処理がおわんねーなーってドキドキしてたら、ファイルを指定してなくて、標準入力からの待ちになってた。
あれ、なんか速くなったな。proc から __send__ にしたからか。
ついでに、continuation を marshal してみる。環境までくっつけちゃうのは、ちょっとまずいかもしれないな。まぁ、これでマイグレーションも出来るでしょう。多分。
しまった、トップレベルの環境とそれ以外の環境ってわけないと駄目なのか。あれ、そんなことないか?
マイグレーションしたとき、トップレベルの環境をどうするのかって結構問題だな。現状だと元のを全部持っていくことになって、移動先のトップレベルを無視することになる。
まぁ、それでもいいような気がするが。
インタプリタの中断が出来るか、これで。
うーん、なんか速くなってるぞ。なんでだろう。よくわからない。
こいつで CGI を動かすことを考えてみる。セッションごとにコンティニュエーションを作って、、、うーん、どうなんだろうな。
ということで、rucheme 0.2.0 です。もうバージョンアップかよって感じですが、前がダメダメだったんで。で、結構よくなったと自負してます。
to-yaml って関数つけたんですが、結構面白いかもしれない。
ちなみに ML は全然余裕があります。多分。
business navi 特集 『こうは絶対なりたくない! 飲み会のウザいヤツ大特集』(mput's essay)。
盛り上がっている最中に帰ってしまう
それはしょうがないんじゃ・・・。じゃぁどうしろっつうんだろ。
vm.c:VMインタプリタ(メインループ)。大変興味深いのです。
11時。学校に帰室。
神田駅には初めて降りた。地図を忘れて無茶苦茶迷う。三菱総研に行くはずが、三菱マテリアルで道を聞く。全然違うところ歩いてるじゃーん。遅刻。階段で行ったら締め切られていた。受付のお姉さんは冷たかった。ケーキのでる会議室は初めてだった。
甘いものを奢っていただきました。ありがとうございました。あれだけ濃い最適化の話を聞いたのは初めてでした。まさか東方妖々夢のデモを見るとは思わなかったですが。
あなたの本来のストライクゾーンは17〜26歳です。
本来って言われてもな。
私に cannibalism の趣味はありませんが... stdin とかは移動先のほうにリダイレクトしたいか元のほうで動いてほしいか,微妙ですよね.HORB を使っていて,JVM ごとに挙動が違うコードに,はまったことがあります.
いや、その環境じゃなくて、バインディングする環境
論文別刷り届いたよ。記念贈呈するから、来たときね。
どうもです。
東方妖々夢いいですよー
require 'benchmark' include Benchmark class Pair def initialize a,b @car = a; @cdr = b end attr_reader :car,:cdr end def m1 a,b a + b end def m2 a a[0] + a[1] end def m3 arg a = arg.car; arg = arg.cdr b = arg.car; a + b end def m4 arg a = arg[0]; arg = arg[1] b = arg[0]; a + b end ary = [0,1] par = Pair.new(0,Pair.new(1,0)) LC = 50000000 bm{|b| b.report{ LC.times{ m1(0,1) } } b.report{ LC.times{ a = [] a << 0 a << 1 m1(*a) } } b.report{ LC.times{ a = [] a << 0 a << 1 m2(a) } } b.report{ LC.times{ p = Pair.new(0,0) p = Pair.new(0,p) m3(p) } } b.report{ LC.times{ p = [1,0] p = [0,p] m4(p) } } } #=> user system total real 86.094000 0.000000 86.094000 ( 86.657000) 258.672000 7.594000 266.266000 (269.984000) 312.938000 9.141000 322.079000 (326.266000) 1200.359000 37.921000 1238.280000 (1258.453000) 365.312000 2.750000 368.062000 (373.172000)
うー、12倍・・・。うー・・・。
これは、Pair を C で書けば、速くなるかな。。。なら、Pair で行くか。
笑った。 from f2c.h
/** barf [ba:rf] 2. "He suggested using FORTRAN, and everybody barfed." - From The Shogakukan DICTIONARY OF NEW ENGLISH (Second edition) */
あー、帰りたい。せめて夕飯が食べたい。
そういえば、和歌山行ったとき、第六大陸という本を読みました。K本さんに強く勧められたからなんですが。
内容は、13歳の少女にいろいろと恥ずかしい行為をさせて、最後は食べてしまう、という感じ。さすが岸Mさんが勧める小説です。
プロジェクトXって感じなんですかね。1500億は安すぎるんじゃないかなぁ・・・。
バグがあるのはわかってんだが、それをなおす気力が出ない。むー。徹夜はやっぱりいかんな。
テーブルを wikiスタイルにどう組み込むか。んー。
わー、WEBrich ってこうやって使うんだー・・・。スゲー。
というわけで、何か出来ないかなー、とやってみようとしたけれど、何をどうしていいのかわからなかったので挫折。偉い人はどうやって使ってるんだろう。
shutdown に時間がかかる。windowsだからなのかな。何か入力すると、DOS窓に反応が無くなる。どうやって待ってるんだろう。select 関連で、なんかアレなのかな・・・。
include WEBrick s = HTTPServer.new( :Port => 2000 ) class ClassesViewerServlet < HTTPServlet::AbstractServlet def do_GET(req, res) res.body = "<HTML><pre>#{class_list}</pre></HTML>" res['Content-Type'] = "text/html" end def class_list lst = [] ObjectSpace.each_object{|obj| if obj.kind_of? Class lst << "<a href='class/#{obj.to_s}'>#{obj.to_s}</a>" end } lst.sort.join("\n") end end class ClassViewerServlet < HTTPServlet::AbstractServlet def do_GET(req, res) res.body = "<HTML><pre>#{method_list(req.path)}</pre></HTML>" res['Content-Type'] = "text/html" end def method_list klass /\/class\/(.+)/ =~ klass.to_s classname = $1 klass = eval($1) klass.instance_methods.sort.map{|e| "<a href='./method/#{classname}.#{e}'>#{e}</a>" }.join("\n") end end class MethodViewerServlet < HTTPServlet::AbstractServlet def do_GET(req, res) res.body = "<html><p>#{referu(req.path_info)}</p></html>" res['Content-Type'] = "text/html" end def referu n /\/(.+)/ =~ n $1 + "\n" + `refe.bat #{$1}`.to_s end end s.mount("/class/method", MethodViewerServlet) s.mount("/class", ClassViewerServlet) s.mount("/", ClassesViewerServlet) trap("INT"){ s.shutdown } s.start
えらいいい加減。
今日の失敗(の一つ)。定数のつもりで __NANTOKA__ と書いていてエラー。やられた。
やむる(YAML)があるんだかられふぇるがあってもいいんぢゃないだろうか。(よくない)
require 'refe/database' class Object REFERU_DB__ = ReFe::Database.new.method_document def referu key key = "#{self.class}##{key}" begin REFERU_DB__[key] rescue 'no reference' end end end puts "str".referu(:sub)
出来るところでは == を equal? に変更。kind_of? を respond_to? に変更。Symbol にはわざわざ symbol? というメソッドを追加してしまった。
焼け石に水という噂もある。
配列の、最初の要素とそれ以降、まぁリストにおける car と cdr に分けたい処理があったんだけど、ary[1..-1] ではコピーが起きて嫌だな、ということで、わざわざクラスを一個用意して管理することにする。
遅くなってしまった・・・。コピー起きないのかな、もしかして。うーん、起きないか。元に戻すかなぁ。。。見た目的にはクラスで管理したほうが綺麗なんだけど。
戻したら 15% 早くなった。ヤレヤレ。
call/cc もやったし、もうこれくらいでいいか。しかし、速度比 1000倍ってのもなんだかなぁ。
(define (count x y) (if (= x 0) y (count (- x 1) (+ y 1)))) (count 100000 0)
具体的には上記コードが 2分弱(500Mhz ノート)。Gauche だと一瞬。
とりあえずリリースしよう。前回の RHG 読書会の議論の成果なわけですので、是非見てもらいたいとは思っているわけです。
wiki で S式を使うってのが流行っているようなので、hiki にでも入れてみようか。でも、私は hiki をよく知らない。
ちなみに、continuation の migration は、今はまだできない。永続化も同様。どうしようかな、できるようにするべきか・・・。少し弄れば出来そうだけど。continuation を yaml で、とか。うへ。
しかし、スタックオーバーフローが起きなくなったのはいいが、逆にその制限ができなくなってしまった。ちょっと大きな再帰構造をかけるとメモリ食いまくり。CPU占有率が0って何事じゃー、とか思ったんだけど、スワップに時間が食われまくりらしく。
スタックサイズを数えるのはちょっと負荷が気になるんだけど。どうしたもんかな。でも、ソフトウェア組み込みで利用するにはそれがないと駄目かな・・・。
やっぱり、評価器自体を再帰呼び出し、のほうがスクリプト言語では良いのかもしれないなぁ。
私は凄い厳密な処理系を作ってるけど、そうじゃない処理系のほうが実用性は高いのかな。たとえば、if などの式はバインディング不可、とか。誰も if なんて束縛しないよね。そうしてしまえば、相当早くなるんだけど。パース時点でやること決めてみたり。
そういえば、恥ずかしながらやっと lexical closure についてわかったような気がする。あぁ、lexical だぁ、と。遅。
google オメー。
こんなものは嫌だシリーズ。喋らない番兵君。
o = Foo.new begin p o.__id__ rescue p "dumb sentinel!." end
というわけで Rucheme ふたたび、なのです。対話型インタプリタの開発には是非大人数で望みたいのだけど、MLには何人くるか。
テーマなんだけど、ネットワーク対応テーマを考えています。そこまでやって誰が使うんだ、という気がとてもとてもするんだけど。
いや、別に10人で制限ってつもりじゃなくて、10人も居ないだろう、って判断なんですが・・・。無理そうならほかに移します。でも、10人来ないだろう、きっと。
test/unit とか、rubyunit とか、テストツールってどうやって使い分けるものなのでしょうか。
OpenMP。大変わかりやすい解説。広い並列性へのディレクティブだけど、最初の動機はやっぱり SMP みたいですよ。
正規表現、前置記法にするのなら、 (=~ #/abc/ "abc")
みたいになるのかな。何を返すんだろう。MatchData 返せばいいんかな。
0.1.1 ということで、named let に対応。より具体的にはさかえさんのところの 引数の数 に対応。
あと、let にバグがあったので修正。新しい環境つかってねーでやんの。
Catan 公式サイト。カタンネットゲームになるのね。俺、一度も勝ったことが無いんだよな。やっぱりこれは頭のよしあしなんだろうなぁ。
せっかく遅いのにも関わらずまじめに評価してるんだから、もっとアグレッシブな機能が付けられるかも知れない。たとえば、実行中に Ctrl+C で止めて、トップ環境を変更してまた再開するとか。一部をロールバックする機構さえ作れば出来ないことはない、のかな。ほんとか? 今度やってみよう。
今番兵君に Object.new を使ってるんだけど、marshal したときにダメダメになるね・・・。どうしようかな。
Pair だけ dump_ とか加工しようかしら。
あぁ、バグ発見。ううん・・・。これは根本的解決は・・・それこそまじめに CPS するか。
うわ、大改造が必要。
とすると、push_frame / pop_frame という名前を変えたほうがいいんだろうな。Continuation.new に restore_continuation 、になるのかな・・・。それこそ call_continuation にしてしまおうか。そのまんまだしな。
やっと CPS とは、ってわかってきたような気がする。でも、やっぱりよくわからない・・・。
ああ、ダメダメだ。実行時スタックのスナップショット、全然取ってない。あー・・・。別にスナップショットを取る必要はないか・・・。
うーん・・・。linked list と Array ・・・。配列 A の要素 n 個未満、というものを一つのオブジェクトとして扱う方法は無いものか・・・。やっぱりlinked list が必要に・・・。でも、__send__ にさくっと送れないしな・・・。
性能がー、っていろいろベンチマークとってたけど、そもそもこんなんなんだから、性能なんてどうでもいいかー。とりあえずキッチリ動くもの作るかー。
い○○とモードはつけるのですか>Rucheme2(?)
それが目玉です。
Ruchemeメーリングリスト入りたいんですが、RubyもSchemeもヘナチョコなので、当座はROMしかできないと思います。パイが少ないので躊躇しています。公開アーカイブが出来ればうれしいです。
私もへなちょこなので多分大丈夫です。へなちょこなので、公開アーカイブを作る気力がありません。ちなみに今2人ほどいらっしゃるようです。やっぱり2人、ニッチな話です。
というか、私が教えて君モードになるためのMLなので(ぇ)、人数は多いほうがいいんじゃないか、とか。というか、猫耳とかメイドとか、詳しい人きてくれないかなぁ。
QuickML なんだから直接召喚すへし。xxxx さんとか ooo さんとか。。。
メイドと Scheme 双方に通じてる人って知らないのですよねぇ。誰が知りません?
Schemeに通じている人をメイド色に染める、って方法も。
apply の3個目以降の引数ってどういうシチュエーションで使うんだろう・・・。
うー、ソースが汚い・・・。apply, map, for-each 終り。汚いなぁ・・・。
んー、named let 出来るようにするべきか・・・。面倒くさいな・・・。
# (let <bindings> <body>) self.def_syntax 'let', %q{ bindings = args.car body = args.cdr env = Environment.new @env var = nil pr = lambda{|result| env.define var, result if var if bindings == Nil begin_sexp body, env else var = bindings.car.car init = bindings.car.cdr.car bindings = bindings.cdr #p var.to_s #p init.to_s push_frame init, @env, pr <==== ココ end } pr.call(nil) } # (let* <bindings> <body>) self.def_syntax 'let*', %q{ bindings = args.car body = args.cdr env = Environment.new @env var = nil pr = lambda{|result| env.define var, result if var if bindings == Nil begin_sexp body, env else var = bindings.car.car init = bindings.car.cdr.car bindings = bindings.cdr #p var.to_s #p init.to_s push_frame init, env, pr <==== ココ end } pr.call(nil) }
let と let*。非常に嫌な感じがします。たった一文字違うだけのコピペ。どうまとめるのが綺麗か。はて。
def syn_let args, env, passing_env bindings = args.car body = args.cdr var = nil pr = lambda{|result| env.define var, result if var if bindings == Nil begin_sexp body, env else var = bindings.car.car init = bindings.car.cdr.car bindings = bindings.cdr #p var.to_s #p init.to_s push_frame init, passing_env, pr end } pr.call(nil) end # (let <bindings> <body>) self.def_syntax 'let', %q{ env = Environment.new @env syn_let args, env, @env } # (let* <bindings> <body>) self.def_syntax 'let*', %q{ env = Environment.new @env syn_let args, env, env }
とりあえずこんな感じ。
こんな感じのぐちゃぐちゃのコードで動いてるのは自分でも驚き。
あ、この定義 letrec か・・・。
def syn_let args bindings = args.car body = args.cdr var = nil passing_env = Environment.new @env pr = lambda{|result| passing_env.define var, result if var if bindings == Nil begin_sexp body, passing_env else var = bindings.car.car init = bindings.car.cdr.car bindings = bindings.cdr #p var.to_s #p init.to_s passing_env = yield(passing_env) push_frame init, passing_env, pr end } pr.call(nil) end # (let <bindings> <body>) self.def_syntax 'let', %q{ syn_let(args){|e| @env} } # (let* <bindings> <body>) self.def_syntax 'let*', %q{ syn_let(args){|e| Environment.new e} } # (letrec <bindings> <body>) self.def_syntax 'letrec', %q{ syn_let(args){|e| e} }
なんか汚い・・・。
やっぱり、関数定義などを do/end じゃなくて {/} で出来るのは嬉しかったり。
うーん、マクロやっぱり面倒くさいな・・・。展開のタイミングもやっぱりよくわからないし。
Klass === obj と obj.kind_of? Klass 、どっちが速いか、のテスト(ruby1.8.0 mswin)
require 'benchmark' include Benchmark LC = 10000000 str = 'hoe' bm{|bm| bm.report{ LC.times{ String === str } } bm.report{ LC.times{ str.kind_of? String } } } str = 103 bm{|bm| bm.report{ LC.times{ String === str } } bm.report{ LC.times{ str.kind_of? String } } } =begin user system total real 24.725000 0.000000 24.725000 ( 24.725000) 24.465000 0.000000 24.465000 ( 24.495000) user system total real 27.039000 0.000000 27.039000 ( 27.039000) 26.158000 0.000000 26.158000 ( 26.158000) =end
kind_of? のほうが速いらしい。全部書き換えよう・・・。
しかし、false なときは遅いんだな。respond_to? でやったほうがいいのかもしれんなぁ。
bm.report{ LC.times{ str.respond_to? :gsub } } を追加。 user system total real 24.695000 0.000000 24.695000 ( 24.705000) 24.465000 0.000000 24.465000 ( 24.505000) 24.596000 0.000000 24.596000 ( 24.596000) # respond_to? user system total real 26.668000 0.000000 26.668000 ( 26.668000) 26.138000 0.000000 26.138000 ( 26.138000) 24.144000 0.000000 24.144000 ( 24.145000) # respond_to?
失敗時、クラスをたどらないから速い、と。respond_to? で全部書き換えるか。
QuickML.COM: 利用状況 を見る。凄いなぁ。右肩上がり。
というか、ちょこっと使わせてもらおうと思っていた矢先なので、ちょっと残念。どうしようかな。10人なら十分か。駄目そうだったら FreeML に移そう。
JDCセミナー:ガベージコレクション in Java ウワーーー、申し込みオワッテルーーー。しまった、やられた・・・。油断してた。
行こうか迷ってたけど、行けないとわかると行きたくてたまらなくなる。うーん。残念。
なんか流行ってるらしいあずまんが占い。「あなたは【美浜ちよ】なタイプ。」うーん、特に狙ったわけじゃないのになぁ・・・。ここで晒すと「やっぱり」とかいわれそうなきがするが、はて。
なんか、ノーベル賞取るらしい。計算機じゃとれねーよ。
先日のアレを新しいのでやってみたら、
(time (count l)) (time (count/cps l (lambda (x) x))) (time (count/tr l 0)) (time (count/tr l 0)) (time (count/cps l (lambda (x) x))) (time (count l)) time : 35.812 normal 100000 time : 101.672 cps 100000 time : 36.375 tr 100000 time : 35.859 tr 100000 time : 68.813 cps 100000 time : 39.453 normal 100000
GC だね、ボトルネックは。コリャ。やっぱ1フレーム1オブジェクトは贅沢すぎたかしらん。
どうやっても再帰無しの評価器が作れない。というか、美しくない。細かい部分で駄目駄目。バイトコードにしてしまったほうが早いような気がしてきたぞ。
日本で最初の年号は「大化」。知ってた?。知らない人、結構居たっぽい?
syntax の評価ルールが未だによくわからないな。
(display and)
は許されるのか、とか。(OK:G, NG:D,C)
うーん、なんか解決方法が思いついた気がする。しかし、R5RS 読んでも、正確なものってわからないな。数値の文字列表現とか、どこに載ってるんだろう。3####.####
ってなんだよ。精度関係なんだろうけど。3@4
とかも、書いてない気が。
結局3..4
がシンボルになれるのかっていうのも書いてないし。
あれ、 define って式キーワードじゃないの。そもそもシンタックスキーワードと式キーワードってどう違うんだ。値を持つか持たないかってことかな。
うわー・・・。末尾再帰版のほうが2倍遅い・・・。これはマズイ。何故だ。
うーん、関数フレームを作るとき、一個オブジェクトを作るのが敗因か。
あれ、作るオブジェクトの回数は同じか・・・。じゃぁGCかなぁ。
あ、文字列出力の問題だったらしい。S式の出力に時間がかかりすぎていたようで。出力部分だけ削っていたので、文字列生成部分のオーバーヘッドを見逃していた。
これで、末尾再帰、再帰無しと同等の速度になった。大体 Gauche の2倍くらいの遅さ。
やっと、旧 Rucheme に追いついたのかなー、これで。
R5RSは最低共通項を定めていて、意図的に処理系の解釈による拡張を許している部分があります。"3..4"に関しては、エラーにするかシンボルにするかでしょうが、わりと多くの処理系が、「数値として解釈できないトークンはシンボルとみなす」という方策を取っているようです。ちなみに3@4は7.1.1のプロダクションルールから導けますよ。 それから、syntactic keywordを評価した場合の動作は未定義なので、それも処理系依存ですね。
なるほど。全仕様ってわけではない、ということですね。a@b が Complex であることは書いてあると思うのですが、これが極形式での表記というのは、実際に処理系で試すまでわかりませんでした。特に規定されてないのかなぁ。
シンボルについてですが、気になっているのが、7.1.1 の識別子の定義が 識別子 ::= <頭文字> <後続文字>*
となっていることで、頭文字には数字が入っていません。7.1.2 には、シンボル ::= 識別子
とも書いてあるようで。でも、試した処理系では、全て 3..4
をシンボル(及び識別子)にしてますね。
そういえば、ライブラリ関係、何も書いてないですしねぇ。use とか。
おお、本当だ。@の意味が書いてない>r5rs。今まで気づきませんでした。
7.1.1.の識別子の定義は、「r5rs準拠の処理系は最低限これは識別子として認識してね」というふうに読むべきもののようです。
なるほど、最低限って意味がやっとわかりました ^^;
@が極形式というのは知りませんでした。ささださんのHPを見てると発見があるなぁ。
日が変わって帰宅。海が綺麗でした。遠くで見ただけだけど。
以前から困っていた「ノートPCについて、アダプターを刺しても充電されたりされなかったり」現象、やっと原因がわかった。アダプターのケーブルが断線してるのしてた・・・。ヤバスギル。火花散ってるし・・・。
これのためにいったいどれだけ苦労したことか・・・。
自己紹介を考えなければならない、というのは結構辛い。
本当のことを書くと、クライアントに不安を与えてしまう。というか、断られてしまう。こんな初心者を、ってな感じで。なので、適当に取捨選択しながらはったりをかます必要がある。でもあまりに大きいと、過剰な期待をもたれてしまう。
書いてみたら、はったりが効き過ぎてしまったような気がした。反省。
うーん、123c5
ってシンボルにしちゃっていいのかなぁ・・・。
う、数値ってきちんと parser 作んないと駄目かな・・・。RACC の練習してみるか。
win だと微妙にインストールがアレだったので、展開したアーカイブの中でテストしてみる。
簡単なのを書いてみる。ここまでわかるのが大変だった。ダメスギ。
sdigit10: flag DIGITS { result = val[0] * val[1] } | DIGITS { result = val[0] } flag : PLUS { result = 1 } | MINUS { result = -1 }
パース失敗のとき、それを伝えるには例外しかないだろうか。うーん。
数字の仕様、これ大変だなぁ・・・。
真面目に実装すると、スゲー長いような・・・。
やってみたら 8 shift/reduce conflict。仕様書どおりに行くなんて思うのが間違いか。
もうちょっと考えてみよう。256本読んで、RACC の勉強しないと。
というわけで、和歌山行ってきます。新幹線のお供は RHG。
暑い。
明日は4時半に家を出ないと間に合いません。多分。
というわけで、4時には起きないと間に合いません。多分。
寝坊しないためには、やっぱり徹夜しかないかな・・・。
まぁそういうわけで、2日くらい更新しません。多分。
妹テーマというのはわからないでもないが(わかるのかよ)、妹スケルトンって、なんか怖いな。
分散妹処理って、何処理するんですか。
あるソフトウェアに、A.rb と B.rb というファイルがあって、それぞれライブラリだとする。
A.rb のデバッグには、B.rb が必要、B.rb も A.rb が必要。if $0 == __FILE__
で、それぞれを require したんだけど、この場合、A.rb の起動と、require B
で A.rb を二度読み込むことになるんだね。ヤラレタ。
テストコードはやっぱり別ファイルにしないといけないのか・・・。
素朴な疑問なんですが、Scheme って null list を作るのを '() って書くのが多いと思うんですが、() じゃだめなんでしょうか。私は後者で書いちゃうんだけど。
やっぱりテストがよくわかんないな。どういうファイル構成とかにすればいいんだろう。
はて、RSR5 には複素数表現についての規定がないのか? というか、リテラルについての定義か。どういう正規表現になるんだろうな。って、あった。7章か。しかし、これ全部サポートすんの辛いな・・・。
あれ。これか。R進数ってのが嫌だなぁ・・・。う、正規表現じゃ無理かも・・・。そんなことないか。
文字列の正規表現って (.*?((?!\\\\)|[^\\]))
でいいのかなぁ。長いなぁ。格好悪いなぁ。
if __FILE__ == $PROGRAM_NAME then $LOADED_FEATURES << __FILE__ end とか。
English.rbって使ったことがなかったんで、なんのことか最初わからなかった ^^; $0 だとフルパス入っちゃって、面倒なのでやっぱり別ファイルにすることで解決しました。しかし、テストってめんどうくせー。
r5rsでは () は自己評価フォームではなかった覚えが。したがってホントは '() と書かないとだめ。
あー、なるほど。リテラルに入ってないですね(リテラルは真偽値、数値、文字、文字列)。これから気をつけます。
%S{ } でS式評価してみたり?
gosh では
(let* ((a (cons 1 2))) (set-cdr! a a) a)
は無限ループするらしい。
Dr.Scheme は #0=(1 . #0#)
うーん、ちゃんと read 出来る。凄い。これを実現するには、最初にサイクルを探しておく、とかが必要なのかな。その後いっせいに処理。
仕様にこんなこと書いてあったかなぁ。
テストをしたことがないと言ったら「それはソフトウェアを作ったことにならない」(誇張)と言われたので、require 'test/unit'
をと書いてみる。
うーん、やっぱりよくわからないので、誰かテスト書いてください(違)。
(let ((a (cons + ()))) (set-cdr! a (list a)) ; (display a) (eval a (interaction-environment))
サイクルなS式を評価しようとすると、やっぱり反応がなくなるらしい(Gauche/Dr.Scheme)。
ruby でも似たようなことは出来るが、lisp だともうちょっと綺麗(ほんとか?)。
(define (f a) (+ 1 2)) (f (set! f (lambda (a) (+ 1 2 3))))
Dr.Scheme だと 3。Gauche だと 6。要するに、関数の評価順序の問題。んで、要するにこんな書き方はするなって意味だ。
応用例(何の)
(define f ()) (f (set! f (lambda (a) (+ 1 2))))
WORM_SOBIG.F というのがたくさん着てるらしい。うーん。
う・・・、再帰を使わないScheme の実装って難しいな・・・。
env.rb っての作って require したら、標準ライブラリのほう呼んでやんの。わからずに10分くらい悩んだ。
あかんなぁ。if がむっちゃ難しい。
また馬鹿。10分くらい悩んだ。
class C Stored = C.new def self.set_store key,val Stored.set key,val # Error!! end def initialize @hash = {} end def set key, val @hash[key] = val end end
これは落とし穴だろー、ってこんなことしないか?
if はやっぱりフックしないとだめかしらん。再帰って偉大だな。
やっと算数が出来るようになった。
Module Test::Unit::Assertions これを適当に組み合わせて使うのか。
研究会で和歌山まで行くんですが、一泊二日の慌しさ。うーん、ゆっくりしたいぞ。
うー、やっぱり酔っ払ってるとハックできない・・・。nokadaさんへの道はまだまだ遠い。
write* して下さい。なお、この表示フォーマットはCommonLispで使われており、SchemeではSRFI-38で規定されています。Gaucheはまだ読み込みの方には対応していません。
なるほど。Common Lisp やっぱり仕様がでかいだけあるんですね。
Chez Schemeも循環リストを回避するみたい。MacSchemeだったかPCSchemeはストップするまで表示してたような。
そういえば、循環リストを検出するには、という有名なクイズがありましたね。対象とするリスト以外は使ってはいけない。副作用もだめ。というやつ。
循環リストの検出、spine方向だけなら1つ飛びに進むsentinelを使うのがポピュラーだと思うのですが、tree(というか一般のDG)でもそれ出来るんですか?
当該ノードより先にある部分については単に子について同じことを繰り返せばいいだけだと思いますが。あれなんか勘違いしてるかな(>儂)
children を集める必要があるからだめかなぁ。
おいおい、9月だよ・・・。
(define (f) (define (cf) (+ 1 2)) (set! + -) (cf))
gauche では 3。これは、パース時に NUMADD にしてしまうからなんでしょうね。いや、(+ 1 2) を 3 にしてるのか。パース時に。
問題無いんだろうけど(誰もこんなことシネーヨ)、仕様はどうだったかな。
継続の話。
複数作った継続って、環境を共有できるものはしないといけないんだっけ?
(define x ()) (let ((t ())) (if (call-with-current-continuation (lambda (c) (set! x c))) (display t)) ;; <<== A (set! t 10)) (x #t) ; goto A
gauche, mit-scheme, chez-scheme と、10 が返りました。共有してます。(ちなみに MIT-Scheme はとまんねー(笑))
ruby で似たように作ると、nil です。つまり、環境は共有しません。勿論そのように作っており、ruby での continuation はそういうものですので、そうなるのが当然です。
が、環境をきちんと共有しないといけないならば、rucheme は ruby の callcc を単純に使ってるだけなので、ダメダメであるということになります。
バカ。
class String def m [3] # self[3] とやりたかったらしい。 end end
5分くらい悩んでしまったらしい。
うう、駄目だ。「リファクタリング」買おう・・・。
なぜか今日一日 C++ で高速化に頭を悩ませる。てきとーにインライン化したら遅くなってしまった。
わ、また ruby-list に未読がたくさん!
あれ?、Rubyでも環境は共有されるのでは? 以下のコードを実行したら、ちゃんと10が返ってきましたよー
x = nil lambda{|t| if callcc{|c| x = c; nil } p t exit end t = 10 }.call(nil) x.call(true)
あれ、私はどんな例を試したんだろう? ただ寝ぼけてただけかも。
そうだよなぁ、これが出来ないとスレッド分けられないよなぁ。
はいはい、ぜひ、アンケートのツボなんかも一緒に書いてあるとうれしいね。
アンケートのツボってどんなのでしょ。
散在というのは、わざとなのか、誤記なのか、どっちでしょう? :)
素で気づきませんでした・・・>散在
悲観的な言語の安全性
たとえば,↓をコンパイルエラーを出ないように変えたとしても,何も安全に なるわけではないということでは?
ところでこの記事の日本語訳,原文の最後の節が1つ欠けてるね. Working smarter, not harder: An interview with Kent Beck
すみません、どの辺が危険なのかわかりません・・・。
コンパイラがプログラマに対していろいろ警告してあげても、その警告は結局のところ、プログラマに対して catch (Exception e) {/* 空 */} というような危険なコードを書くような圧力をかけてしまうだけのことで、よい導き方ではない。 ということ???>悲観的な言語の安全性は幻想でしかない
上のコード例は「fooが未代入で使われることはないのに,コンパイラにはそ れが分からなくてエラーになる」例のつもりです.
Javaのスタンスは,
というものですよね.私自身は別にそう悪くないと思ってます(せっかく型付 きの言語ならそこまでやって欲しい).
Beck氏の意見を想像するに,以下のようなことではないでしょうか(想像なので, 本当のところは分かりません).
訳されてない一文 "I find that attitude disturbing in a program." (「あるでしょう。」と「私は、」の間に入る)というニュアンスからして,そんなところでしょう.
PITのインタプリタ生成系 Virtual Machine Builder の紹介 おもしろそうですね〜 行きたいけど,授業があるし. レポートお願いします(ぉ