読者です 読者をやめる 読者になる 読者になる

Go Go!!

Program Go

Go をいじめてあそぶということなので行ってきた。大変たのしかった。いつもよくわからん面白イベントをやってくれている、 id:ranha さんにとても感謝。今後もやってください :)

http://atnd.org/events/2115

ごにょごにょ遊んだ結果はこのへんに置いてある。

http://github.com/shinh/test/tree/master/go/

まずはゴルフ

http://golf.shinh.org/l.rb?go

適当にやった。大変無駄な時間を過ごしたと思う。ゴルフの感覚としては D に近いなぁと思いました。標準入力が手軽に取れなさすぎてだるい。

scanf 欲しいよなぁ。マジメに考えると /usr/include/pcrecpp.h ってのはぐぐるのライブラリとして大好きなインターフェースの一つなのだけど、こいうのがあまりできそうにない言語仕様は残念だなぁという感じでしょうか。

print が import することなくあるのは嬉しいんだけど、マジメな話をすると配列出力すると size と capacity と address とか、そんなもん見たいわけないでしょ…っていうようなものが出てくるとか、うーん使い勝手は正直イマイチやね。

ゴルフの片手間

とりあえずゴルフやってる最中に、コンパイル通るべきコードにコンパイルエラーを出すケースがあることに気付く。一行コメントの最中に改行が無くて EOF が来るとヘンなことを言う。

http://github.com/shinh/test/blob/master/go/golfed_compile_error.go

1 << 464 を変数に代入しようとすると stupid shift とかいわれる。 463 だと文句がないので、 stupid なのはオマエだよとか言っていた。どうやらこれは 16 * 29 で、 29 というのは id:phoenix_hiro 先生によると、 32 - carry - sign - 1 ということだった。 carry はアセンブリでやれよなぁとか。

http://github.com/shinh/test/blob/master/go/464.go

Python とか Perl とかを殺す手として(たしか Ruby はこの手のやつはどっかで対処されてた)、例えば

python -c 'exec("1+"*100000+"1")'

などがあるわけなので、とりあえずこれをやってみた。

http://github.com/shinh/test/blob/master/go/memory_exhausted.go

結果としてはスタックのサイズ見て yyoverflow を呼ぶとかやっていて、殺せなかった。残念。

id:Gimite 先生がポインタのアドレス返すと毎度値が変わるとか言っていた。試しに下記のようなコードを書くと、毎回アドレスがかわってかつ巡回する。どうも & があるとなのか、エスケープ解析しててアドレスがどっかに渡されたと判明した時なのかはわからないけど、とにかく heap allocation が発生するようだ。うーん個人的にはいつのまにか heap allocation が発生してる、ってのは C 系言語としては許せないなぁ。

http://github.com/shinh/test/blob/master/go/ret_stack.go

goroutine

どう考えてもここが目玉だと個人的には思っています。まぁ私のうろおぼえ Erlang/Alef あたりの知識とそんなに見た目も機能も変わらん気がするんですが。

こんな感じのほとんど何もしない goroutine を 1000 個作るコードを書いてみて、 pthread と比較すると pthread より 3 倍程度速い。うーん正直そんなもんでいいのかなぁ…という感じ。

http://github.com/shinh/test/blob/master/go/goroutine.go

このチャンネルってヤツは面白いというかやっぱこいうのだと本質的に race condition が無いのがいいよねーとかいう話をしてて、チャンネルでチャンネル渡せるかなーとかやってみたのがこのコード。

http://github.com/shinh/test/blob/master/go/chanchan.go

c <- 99 をコメントから外すと、 deadlock 検知が起きる。なるほどねえ。

goroutine は最近の Erlang がやってくれる(らしい)ように、ちゃんと thread をまたいで実行させれるのかなーと色々やってみるにやってくれない。ドキュメントを読むと make(chan T, 4) とかで 4 個 thread を作るように読める。で公式ドキュメントからへちってきたこのコード。

http://github.com/shinh/test/blob/master/go/parallel.go

全然並列実行される気配はない。おかしいなぁ。未実装って感じでしょうか…

testsprite

なにか Go-SDL とかあるじゃんと気付いたので、 testsprite を書いてみることに。

http://github.com/banthar/Go-SDL

ただどうも API を網羅しきってないというかかなり足りてないので、適当に testsprite に必要な glue コードは自分で書いた。

http://shinh.skr.jp/tmp/go_sdl_for_testsprite.diff

で書いた testsprite.go

http://github.com/shinh/test/blob/master/go/testsprite.go

これは超ナイーブな実装で、 go ぽく書くなら(そしてゲーム的に嬉しい書き方をするなら) sprite ごとに goroutine 作る感じの方がいいかなぁと思うんだけど、手元ではだいたい 360 FPS でほぼ C と変わらん程度の速度は出てるみたい。要は画像処理がボトルネックになってるので、 2D のゲームに関しては激しい物理演算とかをしない限りは実用的そう、というくらいのことをは言えると思う。まぁ吐いてるコードはとうてい効率的には見えないんだけどね…

gobjdump

吐いてるコードと言えば、 [68]l の吐く ELF バイナリは objdump で内容は見えるが symbol table が gosymtab とかいう独自形式なのでどれがどの関数かさっぱりわからんくて困る。 6nm を使えば関数アドレスのリストは取れるんだけどね。

go の goroutine 以外に今のところ私がいいなーと思ったところとして、コンパイラやらランタイムシステムとかがかたっぱしから go 自身から見えるようになってるということがあって、 go の吐いた ELF は自分で割と手軽に読めるっぽいのであった。

というわけで objdump -D をしつつ対応する関数アドレスがあれば関数名を出力する、 gobjdump を go で書いてみた。それが下記のコード。これはいいなぁと正直思った。

http://github.com/shinh/test/blob/master/go/gobjdump.go

まとめ

個人的に面白いのはやっぱ goroutine と runtime まわりだなぁと思いました。 syntax は正直ダサいよなぁと思うけど、まぁ Python だと思えばいい気もする。 strings.ToTitle() とかあるしね。

他の人はというと、 id:ranha さんや id:niha さんたちは reflect とか型っぽいところで盛り上がってたみたいだ。あと1番遅れてきた id:ytqwerty さんがサクっと Y コンビネータを書いてたのはさすがだなぁと思った。

http://d.hatena.ne.jp/ytqwerty/20091114#p1

あとは GCC とか ARM とかそのへんがらみをいじってた人はなんか色々大変だったみたいだ。

あと個人的に印象に残ったのは id:ranha さんが呑み会で言っていた、「型の無い言語は適当に使ってる人か、頭のすごいいい人しか使えない」とかなんとかだった。実際頭よくない私は型無い言語キツいなぁと思ってるわけなんだけど、まぁとても納得したのでした。

まぁなにか悪口色々言ってたんですが、 Erlang みたいになんか VM みたいなのが動いちゃう子はどうしても抵抗があるし、 Plan9 とかインストールしてもなかなか実際には触らんし…ってこと考えると、 chan みたいな機構をそれなりにそろってるライブラリと一緒に手軽に使える手段として、まぁ悪くないなぁと思ったりもしました。

他にもなんかあったらあとでかく

なにかあれば下記メールアドレスへ。
shinichiro.hamaji _at_ gmail.com
shinichiro.h