shedskinはスピードだけじゃない
shedskinはスピードだけじゃない
shedskinはpythonスクリプトの文章を直接解釈して,C++に対応するコードを出力している.
これは,nodejsやpypyでのJITコンパイリングによる速度向上とは別物である.
JITは専用のフレームワーク上で動くことを強制されるので,スタンドアロンな実行バイナリを出力不能なのである.
このことは何を意味するか
- shedskinは単独で実行可能なバイナリを出力可能
- 単独で実行可能なバイナリはMakefileを加工することで,shared o bjectに変換可能である
- nginx, apacheなどはshared objectで専用のモジュールをかける
- CGIなんか目じゃないぐらい早い(盛った)
shedskinはPythonでの生産性を兼ね備えながら,従来C++でしか書けなかったネーティブモジュールを作成可能に!!
下記のPythonスクリプト
コードになにか意味を持たせて書いたわけでは,ない. ベンチマーク用のスクリプトとして書いた.
import re, sys, os, math class A: def __init__(self): pass def echo(self, some): pass #print some, def add(self, rh, lh): return rh + lh def split(self, line, delim): return line.split(delim) def parse(self, line, regex): return re.search(regex, line).group(1) #instance make if __name__ == '__main__': for i in range(1,10000000): a = A() a.echo("") b = a.add(10, 10) c = a.split('a!a', '!') d = a.parse('mogemoge', 'o(.*?)o')
とりあえず速度の比較のデータ
pure Pythonで実行した結果
gkobayas@ubuntu:~/dev/shedskin_experiment/module_experimental2$ time python same.py real 0m32.796s user 0m32.534s sys 0m0.164s
gkobayas@ubuntu:~/dev/shedskin_experiment/module_experimental2$ time ./same real 0m24.086s user 0m23.941s sys 0m0.016s
あれ,,,そうでもない,,,まあ文字列操作が主だからしょうがないか,,,
本題はC++で書かれたコードからこのshedskinのデータを利用することである
必要条件として下記のものが挙げられる.
- shedskinで自動生成されたIFを持つプロトタイプ宣言を使用側で定義する.
- shedskinは内部で閉じていて通常のstd::stringなどに情報を流すことができないので,shedskinそのものをハックする必要がある.
- Makefileを実行バイナリではなくshared objectを吐くようにハックする必要がある.
Makefileを実行バイナリではなくshared objectを吐くようにハックする
Makefileを下記のように-shared -fPC変数を付ける
SHEDSKIN_LIBDIR=/usr/local/lib/python2.7/dist-packages/shedskin/lib CC=g++ CCFLAGS=-O2 -march=native -Wno-deprecated $(CPPFLAGS) -I. -I${SHEDSKIN_LIBDIR} -fPIC -shared # <-追加します LFLAGS=-lgc -lpcre $(LDFLAGS) -lutil CPPFILES=/home/gkobayas/dev/shedskin_experiment/module_experimental2/same.cpp \ ${SHEDSKIN_LIBDIR}/sys.cpp \ ${SHEDSKIN_LIBDIR}/stat.cpp \ ${SHEDSKIN_LIBDIR}/re.cpp \ ${SHEDSKIN_LIBDIR}/os/path.cpp \ ${SHEDSKIN_LIBDIR}/os/__init__.cpp \ ${SHEDSKIN_LIBDIR}/math.cpp \ ${SHEDSKIN_LIBDIR}/builtin.cpp HPPFILES=/home/gkobayas/dev/shedskin_experiment/module_experimental2/same.hpp \ ${SHEDSKIN_LIBDIR}/sys.hpp \ ${SHEDSKIN_LIBDIR}/stat.hpp \ ${SHEDSKIN_LIBDIR}/re.hpp \ ${SHEDSKIN_LIBDIR}/os/path.hpp \ ${SHEDSKIN_LIBDIR}/os/__init__.hpp \ ${SHEDSKIN_LIBDIR}/math.hpp \ ${SHEDSKIN_LIBDIR}/builtin.hpp #<-ちなみにハック対象
shedskinで自動生成されたIFを持つプロトタイプ宣言を使用側で定義する
shedskinによって自動生成されたclassのプロトタイプ宣言を見ると以下のようになっている.
externがついている変数は外部で利用可能なのである
つまりこの時,Aのインスタンスaがshedskinから引き出せる
extern __ss_int __3, __4, b, i; extern list<str *> *c; extern str *__name__, *d; extern A *a; //ここが引き出せる extern class_ *cl_A; class A : public pyobj { public: A() {} A(int __ss_init) { //たぶんこのインスタンスは呼べないんですよね this->__class__ = cl_A; __init__(); } void *echo(str *some); str *parse(str *line, str *regex); __ss_int add(__ss_int rh, __ss_int lh); list<str *> *split(str *line, str *delim); void *__init__(); //コンストラクタとは別に初期化用のメソッドがある. };
shedskinで自動生成されたIFを持つプロトタイプ宣言を使用側で定義する
実際に使用する側になる.
#include <string> #include <iostream> #include <gc/gc_allocator.h> //avoid corrision to prototype, declare only /* 必要なだけのプロトタイプ宣言 * header全部取り込むとか,Makefile作るコストと * 何のheaderが必要になるのか精査が面倒なので, * 必要な関数,変数だけ定義という風にした(externもしていないので動作してるは割と奇跡かもしれない) */ namespace __shedskin__{ class str{ public: str(const char* s); char* get_char(); int __len__(); }; }; namespace __same__ { using namespace __shedskin__; class A { public: void* echo(str* some); str* parse(str* line, str* regex); int add(int rh, int lh); }; A *a; }; using namespace std; using namespace __same__; using namespace __shedskin__; int main(){ cout << "test" << endl; str* b = new str("ababa"); str* c = new str("(b.*?b)"); str* d = a->parse(b, c);//aの機能を利用 cout << d->get_char() << endl;//正しく"bab"が出力される cout << b->__len__() << endl; // __len__関数も使えるよ cout << a->add(10, 15) << endl; // 足し算できます }
実行するとこんな感じ
gkobayas@ubuntu:~/dev/shedskin_experiment/module_experimental2$ ./a test bab 5 25h