From: Yuji Shigehiro Date: 1997-09-12T13:34:35+09:00 Subject: [ruby-list:4289] Re: [Q] tk ext... はじめまして. 重弘といいます. 私は, 研究テーマが VLSI のレイアウト設計自動化 ということになってまして, ・ オブジェクト指向 ・ グラフィックのサポート(それも, 話題の tcl/tk らしい) ・ お手軽なプログラミング という甘い響きに誘われて, ruby/tk を使ってみました. たくさんの線を引くスクリプトを書いてみたのですが... 余りにも遅い. おかしいなあ, と思ってソースを見てみると, なるほど, wish を子プロセスとして起動するのか. 確かに移植性は良いけど... > Date: Fri, 12 Sep 97 11:00:09 +0900 > Subject: [ruby-list:4283] Re: [Q] tk ext... > Message-Id: > 2万個のデータのときは(時間がかかったものの)動きました。 > 10万個のデータのときはあまりにもマシンがスローダウンしたので > 中止しました。topで見てるとwishの%MEMが70%以上で、スラッシング > 起こしてる模様(32MBしないので)。言語上の制約はないようですね。 定性的なデータは取りませんでしたが, 私がやっても似たようなものでし た. ps で見ると, wish の負荷がどんどん上がっていって, とんでもないこと になりました. 原因は, おそらく以下の通りです. 普通, ウィンドウアプリケーションは, イベントループの中でボタンを押すな どのイベントを受け取って, コールバックで線を引いたりします. (菊谷様の プログラムは, ぱっと見たところ, イベントループに入る前に線を引いてしま うみたいですが, wish が子プロセスで動作している以上, 事情は同じです.) 普通の X 等のアプリケーションであれば [A] 1. イベント発生. 2. (ボタンを押したのであれば, ここで, ボタンを押された絵に変える). 3. コールバックを実行する (ここで線を引きまくるが, 線を引くというリク エストはライブラリが一旦保留して, 実際に描画されるのはもっと後). 4. コールバックから帰って来る (ここでボタンを元の絵に戻すのか??) 5. イベントループに戻る. 処理すべきイベントが残っていなければ, ここで はじめて, 実際の描画処理がまとめて行われる. となりますが, ruby/tk の場合は [B] (ruby) (wish) 1. イベント発生. 2. (ボタンを押したのであれば, ここで, ボタンを押された絵に変える). 3. <------------------ コールバックを実行する要求. (想像ですが, 多分) ack を返す -------------------> (+) 4. 線を引く --------------------> 要求を受け取る <------------------- (想像ですが, 多分) ack を返す (*) 線を引く --------------------> 要求を受け取る <------------------- (想像ですが, 多分) ack を返す (*) ..... となります. で, 問題なのは, (*) の部分でして, もし一瞬でも ruby からの要求が途絶えると, wish は (ボタンを元の絵に戻して) 線の描画を開始してしまいます. なぜなら, wish にとってのコールバックは (+) で既に終わっていますから. ↑ ↑ (現在の ruby/tk の実装では, ruby と wish の通信チャネル(パイプ)が ↑ ひとつしかないので(←想像), ↑これを避けるのは困難だと思います.) wish の canvas は, 線を引く要求があったら, 単に線を引くだけではなく, 線の情報を後で変更できるように内部に蓄えたりするので, かなり遅いのでは ないでしょうか? さらに, まとめて引くのではなく, ruby からの要求が途絶 えたときに数本ずつ引くので, 致命的に遅くなります. (実際にコールバック で線を引くスクリプトを組んでみると, 押したボタンがすぐに戻って描画をは じめる様子が分かります.) 参考までに, テストに使ったスクリプトを付けます. draw を押すと, 500 本の線を 100 回引きます. 最初に線を引かずにループに必要な時間を計測し, その後で実際に引きます. (ちなみに, マシンが重くて動かなくなっても, 私は責任を持てません.) ---- #! /usr/local/bin/ruby require "tk" def drawlines() print Time.now, "\n" for j in 0 .. 99 print "*" $stdout.flush if (j & 1) != 0 col = "blue" else col = "red" end for i in 0 .. 499 # TkcLine.new($a, i, 0, 0, 500 - i, "-fill", col) end end print Time.now, "\n" for j in 0 .. 99 print "*" $stdout.flush if (j & 1) != 0 col = "blue" else col = "red" end for i in 0 .. 499 TkcLine.new($a, i, 0, 0, 500 - i, "-fill", col) end end print Time.now, "\n" # Tk.root.destroy end $a = TkCanvas.new{ height(500) width(500) } $b = TkButton.new{ text("draw") command(proc{drawlines()}) } TkPack.configure($a, $b, {"side"=>"left"}) Tk.mainloop # eof ---- 実は, (tk.rb が使えないからといって ruby をあきらめるのは惜しいので,) tcl/tk のライブラリを直接 *リンク* して ruby から呼ぶためのライブラリ をつくりました. これを使うと, 正しく[A] のように描画が終わってからボタ ンが戻るのが分かります. (ちなみに, いろいろ理由はあるのですが, 拙作ライブラリのインターフェー スは ruby/tk とは全く違います. tcl/tk の表現をなるべくそのまま ruby で 実現できるようにしてしまいました. (そうすれば, tcl/tk の man が使える.)) 自分で使う分には十分完成したのですが, せっかくなので tcl/tk の勉強を兼 ねて, 一通りの機能が使えるかどうかテストしているところです. なにせ, tcl/tk も ruby もはじめて... ---- 重弘裕二 阪大情報処理教育センター (sigehiro@rd.ecip.osaka-u.ac.jp) 阪大工情報システム白川研 (sigehiro@ise.eng.osaka-u.ac.jp)