###############
# Tcl速度計測 #
###############

P/ECE用Tclインタプリタを作りました。P/ECE上で、Tcl言語のスクリプトを実行します。

Tclは、GUIツールキットTkと組み合わせてTcl/Tkとして使うのが一般的ですが、Tcl単独でも使用できます。
今回は、Tkは無しで、Tclだけを作成しました。

Tclは、アプリケーションへの組み込みに適した、グルー言語でもあります。
組み込み用グルー言語と言えば、最近はLuaが人気ですけれど、Tclも十分な実績のあるグルー言語の一つです。
組み込み用グルー言語として重要な点は、インタプリタ本体の容量が小さいことです。

Tclは、文法がシンプルなのが特徴です。
確かに構文解析はシンプル(というかほとんど無い)なのですけれど、その代わりに字句解析が多少複雑です。
そのためか、インタプリタ本体の容量が、“非常に小さい”と言えるほどに小さくはなりません。

普通の言語では文法に含まれる基本機能(演算やフロー制御など)を、Tclではコマンドとして実現します。
その分、インタプリタ本体がシンプルで小さくなる・・・かと思いきやそうでもありません。
基本機能をコマンドで実現するために、インタプリタ本体が「知っていなければならない」部分がかなりあるからです。
要するに、基本機能については、インタプリタ本体に実装されている部分がかなりあります。

そんなわけで、Tclはあんまり小さくないです。
Tclの標準リリースをP/ECEに組み込んだことは無いのですけれど、たぶん容量オーバーすると思います。
Tclを小さくする試みは既に行われていて、「Jim」(http://jim.tcl.tk/index.html/doc/www/www/)や「Tiny Tcl」(http://tinytcl.sourceforge.net/)などが有ります。
「Jim」は100〜200KBで、P/ECEで使うにはまだ大き過ぎます。「Tiny Tcl」は約60KBで、P/ECEでも使えそうです。
ちなみに、Luaの標準リリースをP/ECEに組み込んだ場合は、70KB(インタプリタ本体)〜100KB(インタプリタ本体＋標準ライブラリ)でした。

「Tiny Tcl」をP/ECEに組み込んでみようと思ったのですけれど、調べているうちに気が変わりました。
「Tiny Tcl」は、「Tcl 6.7」の標準リリースを元に容量削減したものなのですが、もっと容量削減できると思ったからです。

前述のように、基本機能については、インタプリタ本体に実装されている部分がかなりあります。
ところが、この“基本機能”というのが、必ずしも“必須機能”ではないのです。
「Tcl 6.7」の標準リリースの実装では、さほど重要でない部分も、インタプリタ本体に実装されています。
たとえば、ヒストリ機能は重要度が低いと思うのですが、インタプリタ本体に実装されており、結構な容量を占めています。
(後付けのコマンドでヒストリ機能を実現するのは難しいので、これは仕方が無いとは思いますが…)
正規表現のキャッシュもインタプリタ本体に実装されており、容量増加の一因になっています。
他にもいろいろ、必須ではないと思う機能が、インタプリタ本体に実装されているようでした。

P/ECE用としては重要でない“基本機能”を削除すれば、もっと容量削減できると思いました。
そこで、「Tiny Tcl」と同じく「Tcl 6.7」の標準リリースを元にして、容量削減してみることにしました。
削除した基本機能のうち、特に容量削減効果が大きかったものは、以下のとおりです:
・ヒストリ機能は全て削除しました。使ったことが無いので。
・トレース機能は全て削除しました。Tkでは使うこともありますが、Tcl単体ではあまり使わないと思うので。
・正規表現は全て削除しました。大抵は「string match」で用が足りるので。
・infoに必要な情報は全て削除しました。実行環境で使えなくても構わないので。
・エラー機能も最小限を除いて削除しました。実行環境で詳細なエラー調査を行うことは少ないと思うので。

もう一点、容量削減のために行ったこととして、メモリ管理を簡略化しました。
具体的には、ガーベージコレクターと併用することを前提として、メモリ開放処理を全て削除しました。
実行環境であるP/ECE用のガーベージコレクターは、「Boehm GC」(http://www.hboehm.info/gc/)を使用しました。
開発環境であるPC用のガーベージコレクターは、「K.Sasada's Home Page」(http://www.namikilab.tuat.ac.jp/~sasada/)さんの『PiGC : P/ECE 用ガベージコレクター』(http://www.namikilab.tuat.ac.jp/~sasada/prog/pigc.html)を元に改造したGCを使用しました。

以上の容量削減を行った結果、Tclインタプリタ本体＋ビルトインコマンドの合計サイズが、20KBになりました。
実際には、ガーベージコレクターや各種ライブラリへの依存もありますので、アプリケーション全体のサイズはもっと大きいです。
従って、単純に比較することはできないのですが、とは言えなかなかの容量削減ができたと思います。
まだソースを整理していなくて無駄な処理も残っているので、精査すればさらにもう少し容量削減できるかも知れません。

さて、Tclは速度が遅いことも特徴(?)です。
Tclは「全て文字列」な言語であり、何か行うたびに文字列の解釈を行います。
「Tcl 8.x」以降は、バイトコードコンパイラが導入され、文字列の解釈を行う回数が減って高速になりました。
しかし、「Tcl 8.x」は複雑で容量も大きくP/ECEに組み込むのは難しいため、今回元にしたのは古い「Tcl 6.7」です。
そのうえ、今回の容量削減においては、速度を犠牲にしてでも容量削減できる所は、積極的に容量削減しました。
だから、元の「Tcl 6.7」よりも、さらに遅くなっているはずです。

2012年2月8日の記事で、『2〜1000の範囲にある素数の数』を数える、Luaスクリプトの実行時間を計測しました。
今回も同じ方法で、Tclスクリプトの実行時間を計測してみました。
ただし今回は、『2〜1000の範囲にある素数の数』ではなく、『2〜300の範囲にある素数の数』を数えました。
Tclの処理速度では、前者を行うには時間がかかり過ぎるからです。

ダウンロードはこちら バイナリ(84.1KB) ソース(16.0MB)

結果はこのようになりました。(高速版浮動小数点ライブラリを高速RAMに配置、P/ECEを48MHzで駆動)

	┌───┬────┬────┐
	│      │結果[個]│時間[ms]│
	├───┼────┼────┤
	│C(int)│      62│      21│
	│C(flt)│      62│     110│
	│C(dbl)│      62│     441│
	│Lua   │      62│     829│
	│Tcl   │      62│ 3426450│
	└───┴────┴────┘

想像以上に遅いです。C言語やLuaでは1秒以内に終わる処理が、Tclでは約1時間かかりました。

今回のTclスクリプトでは、浮動小数点演算が発生しないので、「C(int)」相当の処理をTclで行ったことになります。
しかし、文字列の解釈に起因する遅さなので、仮に浮動小数点演算が発生しても、結果はあまり違わないと思います。
あと、Tclインタプリタが小さなメモリブロックをたくさん確保するので、ガーベージコレクションの時間も長くなり、遅さに影響していると思います。

Tclは数値演算に向いていないことがあらためて確認できたわけですが、グルー言語としてはさほど重大な問題ではありません。
せっかく容量削減できたのですから、P/ECE上での使いどころを検討して、活用したいと思っています。

今回作成した「P/ECE用Tclインタプリタ」で、対応した機能の一覧は、「Tclｲﾝﾀﾌﾟﾘﾀ対応状況.xls」をご参照ください。

==================
Tue Oct 28 2014
Naoyuki Sawa
nsawa@piece-me.org
==================
