はじめまして. 重弘といいます.
私は, 研究テーマが 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: <m0x9L8S-0000WPC / jdcpc13.nrj.ericsson.se>
> 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)