テキスト構文とバイナリ構文の統合

テキスト構文も一応設計してみたけど、バイナリ構文と同じところが多いので統合することに。
当初、Preccsよりも絞り込んだプリミティブがウリだったのに、なんだかだんだんと同じような内容になってきてしまった。
バイナリ構文はTTでもしゃべったdefine-packet-type、テキスト構文は新設するdefine-formatで、今後はdefine-formatの方に統一。
define-packet-typeからの違いは、

  • シーケンスがデフォルト(seqを書く必要が無くなる)
  • 辞書構文の独立

辞書構文は別のdefine-ナントカをつける予定。要するに長くて見づらくなるのでその辺を修正。
従来は自由に手続き名を命名できたけど、今後は-encとか-decに統一して、変えたい場合は各自defineという方向で。。
define-packet-typeは、コンパイラが生成するプリミティブということで。つまり、LLVMとかCOINSは原始的な構造体しかプリミティブとして準備して無いけど、一旦define-packet-typeを使ったソースコードに変換してくれれば最適化は面倒見ますよ。と。

最適化項目

しかしこの最適化の内容がなかなか曲者で*1。。

  • 分岐のバランシング - 意外と効果が無い

プロトコルの処理では、メッセージに対するswitch-caseで実際の処理を分岐させるように書かせる。そこで、パケットトレースから、プロトコルの典型的な遷移を学習して、プロトコルステージを階層化することを考えた。こうすることでswitch-caseは小さくなり、プログラムを分割することが出来る。つまり、プロトコル処理の一部をPPUで行い一部をSPUで行うといった分割がより容易に行えることを期待した。
しかし、今の所自動的な分類は殆ど上手く行っていない。手でアノテーションする上手い方法を考えないといけない。
そもそも、(ご想像の通り)プロトコル処理において分岐が占めるコストは大したことが無い(callgrind調べ)。

  • 中間構造体の生成

プロシジャ間でデータを受け渡す方法は、直接的な値渡しのほかに構造体のポインタを渡すケースが有る。
構造体から渡したデータをループ変数に使うようなケースでは、一旦ローカル変数にコピーしてからアクセスするのが好ましい(レジスタ割付されないことが有る)ので、そのような細かい処理を自動的にやろう  つまり、構造体のコピーに対する所有権を自動的に管理しようというもの。他にstruct reorgのような最適化も狙っている*2
これは暗号処理のステートを渡したり、実CPU以上の並列度を持った処理のthreading(callで呼ばれる関係にある関数を接続する)最適化に有用な気がするが、そもそも論としてskySchemeの現在の機能ではAESが効率的に書けないので困る。

  • 関数ポインタによる分岐の生成 - 逆効果?

プロトコルステートを判断して分岐を行うところを、その処理を行う関数の関数ポインタに置き換えて比較を減らそうというもの。
これは最近のプロセッサの分岐予測を役立たずにするので効果が無いようだ。ただ他の要因が多くて上手いこと検証できていない。

  • bit精度の演算folding - あまり効果無し?

構造体は1 bit単位で管理しているので、プログラム本来の記述よりも大きな単位の演算命令を使用できる場合はそのように置き換える。
が、検出できるのは「構造体初期化時の代入」ばっかり。もちろんそこは早くなるかもしれないが、当然あまり意味は無い。。
これと中間構造体の生成はキャッシュ制御命令と組み合わせて初めて効果が有るタイプの最適化だが、LLVMでキャッシュ制御させるための良い方法がまだ分かってないので保留中。

今後の方向性

MeCabで要求されるようなTRIE構造とか、アプリケーションの内部データ構造に着目した方がいいんじゃないのというアドバイスは各所から頂いていて、確かにそっちの方が楽そうだし。。と、なびいている所。
今までバイナリプロトコル前提だったのを、テキストベースプロトコルに変更してみることで、最適化がより際立つのを期待。

*1:だから履歴書やESに書くようなskySchemeの紹介は互換性に重きを置いている。例えばSCEのESには「セキュアなデバイスドライバ記述」という主旨で書いた。

*2:現在は登場頻度順に並び替えるのみ