Perlテックブログ

ITエンジニアの成長意欲を刺激する技術考察、モジュール開発の日記。Perlイベントや国内や海外のPerlの記事の紹介。

SPVMに必要な機能の完成まで、あと5項目

SPVMの開発を2年くらい続けているのだけれど、だんだんとTODOリストが減ってきて、残り5項目になった。

  • C/C++言語の構造体を保持する仕組み
  • 複数の数値型の値を持つクラスを連続した配列領域に配置する仕組み(value型)
  • デフォルトでモータルで使えるC言語APIの整備
  • ループ展開
  • プリコンパイル済みのサブルーチンのより安全な読み込み

これができると、後は、標準関数の追加なんかの、比較的単純な作業と、実際に使ってみて、バグをとったり、パフォーマンスを測定して、改善したりという作業になる。

C/C++言語の構造体を保持する仕組み

SPVMはC/C++バインディングを簡単に行える仕組みを提供するので、C言語の構造体やC++のクラスを保持できる仕組みを作らないといけない。

今考えているのは、以下のように、パッケージのデスクリプターで、明示したらどうだろうかというもの。

package Point : struct;

structデスクリプターを持っていれば、それはC/C++の構造体やクラスとして扱うというもの。SPVMはPerlと同じで、コンストラクタは特別に持たずに、newメソッドを定義して、オブジェクト生成して、必要であれば、DESTROYでデストラクタを定義できる。このstructキーワードと、デストラクタを組み合わせれば、うまくC/C++の構造体/クラスを扱えるようになるかもしれない。

はっきりといいきれないのは、実際に手を動かして実装してみないと、わからないことがけっこうあるから。なんとなくこんな感じかなという感じで実装を進めていって、うまくいかなかったら、方向転換したり、微調整をしたりする。想像しているよりも、実装が複雑だったり、あっちが立てば、こっちが立たずということは、しょっちゅうだ。

複素数の関数のバインディング

C言語バインディングするときの、最も大きな壁は、複素数なんかの複数の数値型を持つ値型を受け取る関数だ。

オブジェクト指向言語では普通、オブジェクトは参照であって、配列は参照の配列になる。しかし、これは、メモリが飛び飛びなので、遅い。

f:id:perlcodesample:20180613214126j:plain

そこで、どうにか、連続したメモリ領域を確保して、値の集まりを扱えるようにする値型の仕組みを実装しないといけない。

これが実装できないと、C言語複素数に関する関数を実装したときに、思うようにパフォーマンスがでないだろう。

これは、Javaや他のスクリプト言語を参考にはできないので、独自に開発する必要がある。

デフォルトでモータルで使えるC言語APIの整備

メモリ管理はしたくない。うん、したくない、したくない。

C言語APIを呼び出すレベルでも、メモリ管理はしたくない。メモリの自己管理はバグの元。セグフォールトという苦しみは経験したくないんだもの。

Perlにはモータルという仕組みがあって、これで十分なんだけど、これは、自分でコード書かないといけないんだな。だからSPVMでは、これを入れ替えて、使いやすい短い名前のAPIの方をモータルに、長い名前のほうを非モータルにしようと考えている。

ループ展開

SPVMの配列アクセスは、undefのチェックをするし、インデックスの範囲チェックも行う。

そして、さらに、例外が発生しているかどうかというチェックも行います。

一度配列にアクセスするたびに、4回も、条件チェックが入るんですよ。

でもループする回数などがわかっていれば、ループ展開をして、チェックを10回に1回に減らすことができそう。初回だけ4回のチェックが入って、残りの9回は、チェックなしで配列アクセス。

こうすると速そうな予感がする。

プリコンパイル済みのサブルーチンのより安全な読み込み

SPVMは、SPVM自体がバージョンアップしたとしても、前のバージョンでプリコンパイルされたコードが、正しく動くように設計しようと思っている。つまり、プリコンパイルされたコードのバイナリ互換性を守るというわけだ。

Perlの場合はバージョンアップに対しては、それ以前いコンパイルされたコードは、壊れることが多い。Perlはバージョンアップに対して、バイナリ互換性を保証しておらず、コンパイルされたコードに対しては、再コンパイルが必要となる。

もっともperlbrewやplenvを使えば、作業ディレクトリで、バージョンアップして、モジュールの再インストールができるので何ら欠点ではない。

でも、SPVMはCPANにリリースされるモジュールなので、プリコンパイルされたコードが、SPVMのバージョンアップで壊れるのは、よくない。モジュール依存で、バージョンアップをしたら、なんだかよくわからない部分が壊れてよくわからん、ということになる。

バイナリ互換性を維持するために必要なことは、二つあって、インターフェースAPIを準備して、そこから関数を呼び出すこと。そして、インターフェースAPI後方互換性を守り続けること。

これが安全だけれど、パフォーマンスが必要な場合には、インターフェースから、呼び出せない場合もある。

それができない場合は、関数定義を仕様化して、その後方互換性を、ずっと守るということになる。

コアの数学関数だけは、仕様化して、それ以外は、インターフェースAPIの互換性を守るということになると思う。