fiber 話を少しまとめてみる

いろんなお話を聞いて、散逸させてしまうのはチト惜しい。

並べる適切な順序が思い付かないのであいうえお順

ABA さんとこから

kizz さんとこから

^C さんとこから

gony さんとこから

dW バンザイ

やねうらおさんとこから

yuu_ さんとこから

私んとこから

  • 名称について

fiber / microthread / coroutine / concurrency などと、いろいろ呼び方があるのですけど (最後のは Io だけかも)、^C さんが述べておられるように、fiber のネーミングセンスの良さに従って fiber と勝手に呼びます。
世間的には microthread の通りが良いそうです。でも thread という単語から想起されるものとはかなり違うものなので fiber と呼びたいんです。

  • 何ものか

^C さんが非常に明解に書かれています。「非時分割で非プリエンプティブ(協調型)なスレッド」

私の言によれば「切り替えを自分で(できる/しなければならない)スレッド」

ピンと来ない人はやねうらおさんのサンプルを眺めるのがベストかと。 (http://www.sun-inet.or.jp/~yaneurao/yaneSDK3rd/chap0124.html)

  • 実現の仕方

アセンブラ (やねうらおさんとか) 、setjmp.h ( yuu_ さんとか、 Ruby からひっこ抜いてきたおかげでポータブルというのが素晴らしい) 、script 言語組み込み (Lua, Python, Io とか) 、最初っから script 言語で書く (Python, Ruby とか) 、libBulletML でやってるみたいにせっせとアプリ毎に特有な必要な情報だけ退避 (めんどいのでおススメできません) 。

  • いつ使うか

シューティングのシーン記述なんかは使うしか無いと思う。

逆に状態を持つ場合は困難が発生する。

fiber どうしの通信は難しい問題では無いけどなんらかの方法で解決しなければならない。

そんなに軽いものじゃないはずなのでシューティングの雑魚一匹に使うかというと微妙だと思っている。(やねうらおさんもそうおっしゃっている) でも I-Saint さんが実装されたように (http://i-saint.abz.jp/2003.html#2003/9/16) コンテキストを使い回せばなんとでもなる話でもある気がする (などと言いつつ私は実装読んでませんごめんなさい) 。そのマネージャを作る手間と、resumable なものとそうでないものを二つ使い分ける手間のトレードオフか。(追記: コンテキストを保持するのにメモリを結構喰うのと、そのメモリの確保に時間がかかるかもなだけで、メモリをまとめて確保してしまえばスイッチング自体は早いとのことです。詳しくは下の方でやねうらおさんに頂いたコメント参照。)

yuu_ さんがおっしゃられていた多数のオブジェクトが勝手きままに動いてそれぞれに干渉しあって複雑な状態を作るようなものは状態遷移でやったら難しい。そんな感じで Gikot (http://gikot.sourceforge.net/) みたいなものが動くと楽しそうだなあと思うのですけど Gikot 動かせてませんごめんなさい。

あと大袈裟なエフェクトとかの実装にもきっといいと思うのです。爆発が起きて、パーティクルまき散らして、画面フラッシュして、画面揺らして、塵パーティクル散らして、なんてエフェクトを書こうって時に enum { EFFECT1_EXPLOSION_STATE, ... } なんて書いてたら疲れてしまう。かと言って (私は平気でやりますけど) マジックナンバーだらけにするのもつらい。

  • 比べてみよう

手前味噌で、私が超作りかけ影ふみシュー krok で使った例を。今のところステージの記述にしか fiber を使っていない。つまり敵の配置は fiber で継続しつつ実行、それぞれの敵オブジェクトはターン情報などを使って状態遷移している。ので比較しやすいかと。

適当に data/stage1/stage.lua なんかを見て… function top() の中に…

   setPos(250, 0)
   for i = 1,5 do
      fire(3, 0)
      sleep(30)
   end

なんて書いてあるわけ。 (250, 0) の位置から fire して 30 フレーム待って fire して 30 フレーム待って、というのを 5回繰り返して次に行ってくれ、と。これを scene1 とすると scene2 とかも当然あるわけで、状態遷移的に書くと、

   if (scene == 1) then
      setPos(250, 0)
      if (math.mod(turn, 30) == 0) then
         fire(3, 0)
      end
   -- この後 scene ごとに elseif が並ぶ
   end

などとなるはず。こういうコードって長くなると読みにくくなるんだよな…

で、一方さっき fire で撃っているのは poynting とかいう敵なんですけどその敵の定義部分は

function poynting(i)
   local t = getTurn()
   if (t < 60) then
      changeDirectionAbs(i * math.pi * 0.005 * t)
      if (math.mod(t, 30) == 29) then
         fire(2, 0)
      end
   elseif (t < 100) then
      changeDirectionAbs(i * math.pi * (0.65 - 0.005 * t))
   end
end

となっていて、仮にこれを手続き的に書き直すと、

function poynting(i)
   for i = 1, 30 do
      changeDirectionRel(i * math.pi * 0.005)
      sleep(1)
   end
   fire(2, 0)
   for i = 1, 30 do
      changeDirectionRel(i * math.pi * 0.005)
      sleep(1)
   end
   for i = 1, 40 do
      changeDirectionRel(i * math.pi * (0.65 - 0.005))
   end
end

などとなるのかな。(実際には changeDirectionRel なんて無いけど)

この位ならどっちでも良いかなーと私なんかは思うわけですけどどうですかね。いやまあ、無理に手続きに直したから汚なくなっているという話もあるのですけど。

でもボスとか書く時は手続き的に書きたく思うのです。やっぱ使い分けじゃないっすかね。

  • 雑記

まとめてみようと思ったのに結局メモみたいになるのでした。

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