Perlゼミ(サンプルコードPerl入門)

新着情報 8/16(木) 8時更新 Perl入学式 イベント情報

2017年06月20日

Perlが30倍速くなるかもしれないSPVMの開発版をリリースしました。

Perlが30倍速くなるかもしれないSPVMの開発版をリリースしました。

SPVM(CPAN)

かもしれないと書いたのはベンチマークをまだとってないからで、理論的にはJava VMの速度まで、近づけることができるんじゃないかなと思うから。

開発版なので、まだ機能が足りないのだけれど、CPANにリリースして、CentOS, FreeBSD, Windowsで動かすところまでやったので、ここで公開します。Macを持っていないので、Macの方は、CPANからインストールできるか確認していただけるとありがたいです。

  • SPVMはPerlに似た新しいプログラミング言語で、バーチャルマシン上で動き、関数をPerlから簡単に呼び出すことができる。
  • 完全な静的型を持ち、Javaと同じデータ型、byte, short, int, long, float, doubleを持つ
  • モジュールを書いてすぐに実行できる。XSのように事前のコンパイルはいらない。
  • 64ビット整数をサポートしている環境のみサポート(longの値が渡せない受け取れないため)
use FindBin;
use lib "$FindBin::Bin/lib";
 
use SPVM 'MyModule2';
 
my $total = SPVM::MyModule2::foo(3, 5);
print $total . "\n";

(参考)FindBin

以下はSPVMのモジュールファイルで、拡張子は「spvm」です。

# lib/SPVM/MyModule1.spvm
package MyModule1 {
  has x : int;
  has y : int;
   
  sub sum ($a : int, $b : int) : int {
     
    my $total = $a + $b;
     
    return $total;
  }
}
 
# lib/SPVM/MyModule2.spvm
use MyModule1;
package MyModule2 {
   
  sub foo ($a : int, $b : int) : int {
     
    my $total = ($a * $b) + MyModule1::sum(2, 4);
     
    return $total;
  }
}

用語や文法は、できる限りPerlと同じにしています。Perlを書きなれた人が、すぐに書くことができるように。XSのように事前のコンパイルの必要はありません。

開発中に考えていたこと

CPANリリースまで、来たので、ここまで開発中に考えていたことを書いておく。

動機

動機は、統計・解析と機械学習のために、Perlの集合演算を速くしたいというものだ。Perlの弱点は数値演算と集合演算で、Perlは、高速化を行うための、データ構造を持っていない。

たとえば、intの配列というものはなく、すべてのデータはSV構造体に入れられ、それが、メモリ上に飛び飛びに配置される。また関数は、スタック上に、整数型や浮動小数点型を積むことはできない。

Perlは文字列に関しては、優秀なパフォーマンスを出しますが、数値計算と集合演算に関しては、ものすごく悪いパフォーマンスだ。

これを解決するためには、XSというC言語拡張を書くしかない。でも、XSは、ものすごく難しいし、メモリ管理に失敗するとセグメンテーションフォールトが起こるし、事前にコンパイルする必要がある。

もちろん、速度はC言語なので、ピカイチなのだけれど、PerlとXSの中間地点で、もう少し簡単で、安全で、書ける方法はないかと考えていた。

僕はRstatsというR言語のAPIをPerlに持ってくるためのプロジェクトをC++で書いていたのだけれど、行き詰ってしまった。

それは、集合演算を書くためには、つねに、XSを書かないといけないという部分だ。XSの最大の弱点は、モジュール化ができないということだ。XSで書いたコードを、Perlから呼び出すことは簡単だけれど、XSどうしで運用するのは、不可能に近い。

  • 適切なデータ構造
  • 高速な処理
  • モジュール化
  • Perlから簡単に利用できる

試行錯誤

これをPerlでやろうと思ったときに、方法を考えていたのだけれど、いろいろと参考にした。

静的型

pythonにはRPythonというものがある。これは、pythonのサブセットで、pythonに静的型をつけようというものだ。

最近ではこれを真似してRPerlというのもある。

動的言語のサブセットとして、静的型をもたせるというアイデアは、グッっときた。

そうだ「静的型にして、集合演算に適切なデータ構造が必要だなぁ」という感じ。

静的型はパフォーマンスのためには必須だ。データがあらかじめ決定していれば、型の判定のための条件分岐をいれる必要がない。

最近のCPUは、分岐予測というものを行うから、最初は、数値、次は、浮動小数点、次は、オブジェクトのように、データ判定に条件分岐を使うと、そこで、速度が遅くなる。

静的型だとコンパイル時に、データが決定されるから、条件分岐を行うことがそもそもない。

型推論か自動型変換か

実装していた感じたのは、型推論と自動型変換の両方を実装するのは、ちょっと無理があるということだった。

最初はgoのような型推論と、Javaのような自動型変換を両方実装していたのだけれど、両方実装するのは、きつい(というか無理?)という感じになってきたので、型推論の方を採用した。

# 型推論
my $num = 5;
バーチャルマシン

次は、バーチャルマシン。最初は、コンパイル型がよいのか、バーチャルマシンが良いのかということを考えた。でも、コンパイルしてマシン語にするのはないなというのを考えて、バーチャルマシンでも、最終的にJITで最適化すれば、かなり速くなるんじゃないかなということを思った。

Java HotSpotは、かなり速くってC言語にかなり近い速度がでるようだし、バーチャルマシンにしておけば、後で、性能は何とかなるかなと考えた。

GC

開発中は、Javaのように世代別GCにしようかなぁとも考えていたのだけれど、Perlとの相互運用を考えるとどうもうまくいかない。

Perlは、メモリをがばがばと使えるだけ使う。Perlのメモリ管理は、いわばフリーリストのようなもので、リファレンスカウント式のGCだ。

Javaのメモリ管理は、ヒープが拡大していって、あるところで止める。ヒープの中で、世代別GCをする。

Perlとの相互運用をやるために、結論としては、リファレンスカウント方式のGCにした。循環参照の解決は、オブジェクトどうしの相互参照をできないようにした。単独のプログラミング言語であれば、、だめなんだけど、Perlから運用するから、とりあえず、これでいってみようという形。

オブジェクトの配列は作れる、配列の配列も作れる、オブジェクトは、数値型と数値の配列型を所有できる。オブジェクトは、オブジェクトと、オブジェクトの配列を所有できない。これで、循環参照を回避。うまくいかなかったときはまた考えればいいさ。

技術

技術的な話題を。SPVMは、C言語で書いています。理由は、PerlがC言語で書かれているので、C言語で書いておけば、もっとも安全で、問題が起こりにくいから。

字句解析

字句解析は自前で書いてます。spvm_toke.c

構文解析

構文解析はbison(yaccのラッパ)を使ってます。spvm_yacc.y。

構文木の作成

抽象構文木の作成はspvm_op.c。

部分的に解析するのではなくって、すべてのソースコードを読み込んでから、解析するような実装です。すべてのパッケージ、フィールド、サブルーチンを読み込んでから、型チェック、構文チェックを行う。

定数プールの生成

環境として定数プールを持っています。constant_pool.c

バイトコードの生成

バイトコードは90%がJava VMを参考にしています。spvm_bytecode_builder.c。

ランタイム - サブルーチンの実行

サブルーチンの実行は、ランタイムで。spvm_runtime.c。ここで、バイトコードが解析されて実行されます。ダイレクトスレッディドコードになっています。

グローバル変数、マクロ関数と定数のマクロ定義なし

C言語で、使わないほうがよいとされている、グローバル変数、マクロ関数、定数のマクロ定義を0個にしました。使ったら負けだという意思を貫くと、複雑なソースコードでも、なしで行けるという確信が持てた。

唯一使っているのは、インクルードガードだけ。

辛かったこと・発見したこと

字句解析はまぁまぁ簡単。yaccは、reduce/reduceとの闘い。どうやったら、reduce/reduceしないか試行錯誤。構文木の構築は、辛い。ツリー構造というのは、きつい。それが、やたら多い。再帰処理は、精神的にきつい。

If else文を作るのが意外と難しい。どこでどうジャンプするバイトコードに置き換えるか。for文のほうが難しそうだけど、If elseのほうが実装がちょっと難しい。

ランタイムは、バイトコードと定数プールだけの情報で、実行できるようにしなくちゃいけなかったこと。解析機とランタイムは、完全に分離させて、解析機のメモリを解放したとしても、ランタイムを動かさないといけない。

C言語はメモリ管理との戦い。すべてのメモリ管理を自前で実装しないといけない。動的配列、連想配列、メモリプールというデータ構造も、自前で実装。コードを書けば書くほど、作らないといけない部品が、あらたにあることがわかってくる。

今後

今後は、バグをなくすのと、Perlからの型変換を実装するのと、SPVMのデータ仕様を書くのと、やります。

Macを持っていないので、Macで、インストールできるか、確認していただけるとありがたいです。

nekomiminekomimi 2017/06/29 10:02 最近、Perlで書いた処理の高速化を検討していまして、こちらのブログ記事を拝見しインストールしてみようと思いました。
しかしながら、私の環境(Mac)ではcpanでのインストールができませんでした。
makeまでは通っているようですが、make testでこけているようです。(マニュアルでやってもやはり、make testで同様の結果となります)

Test Summary Report
-------------------
t/SPVM.t (Wstat: 11 Tests: 1 Failed: 0)
Non-zero wait status: 11
Parse errors: No plan found in TAP output
Files=1, Tests=1, 0 wallclock secs ( 0.02 usr 0.00 sys + 0.03 cusr 0.00 csys = 0.05 CPU)
Result: FAIL
Failed 1/1 test programs. 0/1 subtests failed.
make: *** [test_dynamic] Error 255
KIMOTO/SPVM-0.0204.tar.gz
/usr/bin/make test -- NOT OK
//hint// to see the cpan-testers results for installing this module, try:
reports KIMOTO/SPVM-0.0204.tar.gz
Failed during this command:
KIMOTO/SPVM-0.0204.tar.gz : make_test NO

ちなみに、私はMacBook Air (Mid2012) Core i7-3667U / macOS Sierra (10.12.5) にHomebrewでインストールしたPerl 5.24.1を使っています。
コンパイラーはデフォルトのclangおよびHomebrewでインストールしたgcc-6で試しましたが、どちらもmake testでコケました。

perlcodesampleperlcodesample 2017/06/30 22:48 試してくださってありがとうございます。

本日バグを修正した「0.0207」をリリースしたので、再度お試しください。

Perlの情報を得られるため「perl -V」の情報を張っていただけるとありがたいです。

nekomiminekomimi 2017/07/03 11:46 アップデートご苦労様です。
早速、cpanコマンドで0.0207のインストールを行いました。
今度はmake testでこけることもなく、無事インストールできたことを確認しました。(perl -MSPVM -e '' でメッセージが表示されなくなりました)
ありがとうございました。
ちなみに、こちらのPerlの情報は次のようになっています。
macOSにデフォルトで入っているPerl (5.18.2)ではなく、HomebrewでインストールしたPerl (5.24.1) ですのでご注意ください。

Summary of my perl5 (revision 5 version 24 subversion 1) configuration:

Platform:
osname=darwin, osvers=16.6.0, archname=darwin-thread-multi-2level
uname='darwin nekomimi-no-macbook-air.local 16.6.0 darwin kernel version 16.6.0: fri apr 14 16:21:16 pdt 2017; root:xnu-3789.60.24~6release_x86_64 x86_64 '
config_args='-des -Dprefix=/opt/homebrew/Cellar/perl/5.24.1 -Dprivlib=/opt/homebrew/Cellar/perl/5.24.1/lib/perl5/5.24.1 -Dsitelib=/opt/homebrew/Cellar/perl/5.24.1/lib/perl5/site_perl/5.24.1 -Dotherlibdirs=/opt/homebrew/lib/perl5/site_perl/5.24.1 -Dperlpath=/opt/homebrew/opt/perl/bin/perl -Dstartperl=#!/opt/homebrew/opt/perl/bin/perl -Dman1dir=/opt/homebrew/Cellar/perl/5.24.1/share/man/man1 -Dman3dir=/opt/homebrew/Cellar/perl/5.24.1/share/man/man3 -Duseshrplib -Duselargefiles -Dusethreads'
hint=recommended, useposix=true, d_sigaction=define
useithreads=define, usemultiplicity=define
use64bitint=define, use64bitall=define, uselongdouble=undef
usemymalloc=n, bincompat5005=undef
Compiler:
cc='cc', ccflags ='-fno-common -DPERL_DARWIN -mmacosx-version-min=10.12 -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include -DPERL_USE_SAFE_PUTENV',
optimize='-O3',
cppflags='-fno-common -DPERL_DARWIN -mmacosx-version-min=10.12 -fno-strict-aliasing -pipe -fstack-protector-strong -I/usr/local/include'
ccversion='', gccversion='4.2.1 Compatible Apple LLVM 8.1.0 (clang-802.0.42)', gccosandvers=''
intsize=4, longsize=8, ptrsize=8, doublesize=8, byteorder=12345678, doublekind=3
d_longlong=define, longlongsize=8, d_longdbl=define, longdblsize=16, longdblkind=3
ivtype='long', ivsize=8, nvtype='double', nvsize=8, Off_t='off_t', lseeksize=8
alignbytes=8, prototype=define
Linker and Libraries:
ld='cc', ldflags =' -mmacosx-version-min=10.12 -fstack-protector-strong -L/usr/local/lib'
libpth=/opt/homebrew/lib /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include/libxml2 /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/include/apache2 /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/../lib/clang/8.1.0/lib /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/lib /Applications/Xcode.app/Contents/Developer/Platforms/MacOSX.platform/Developer/SDKs/MacOSX10.12.sdk/usr/lib /usr/local/lib /usr/lib
libs=-lpthread -lgdbm -ldbm -ldl -lm -lutil -lc
perllibs=-lpthread -ldl -lm -lutil -lc
libc=, so=dylib, useshrplib=true, libperl=libperl.dylib
gnulibc_version=''
Dynamic Linking:
dlsrc=dl_dlopen.xs, dlext=bundle, d_dlsymun=undef, ccdlflags=' '
cccdlflags=' ', lddlflags=' -mmacosx-version-min=10.12 -bundle -undefined dynamic_lookup -L/usr/local/lib -fstack-protector-strong'


Characteristics of this binary (from libperl):
Compile-time options: HAS_TIMES MULTIPLICITY PERLIO_LAYERS
PERL_COPY_ON_WRITE PERL_DONT_CREATE_GVSV
PERL_HASH_FUNC_ONE_AT_A_TIME_HARD
PERL_IMPLICIT_CONTEXT PERL_MALLOC_WRAP
PERL_PRESERVE_IVUV PERL_USE_SAFE_PUTENV
USE_64_BIT_ALL USE_64_BIT_INT USE_ITHREADS
USE_LARGE_FILES USE_LOCALE USE_LOCALE_COLLATE
USE_LOCALE_CTYPE USE_LOCALE_NUMERIC USE_LOCALE_TIME
USE_PERLIO USE_PERL_ATOF USE_REENTRANT_API
Built under darwin
Compiled at Jun 21 2017 00:28:55
@INC:
/opt/homebrew/Cellar/perl/5.24.1/lib/perl5/site_perl/5.24.1/darwin-thread-multi-2level
/opt/homebrew/Cellar/perl/5.24.1/lib/perl5/site_perl/5.24.1
/opt/homebrew/Cellar/perl/5.24.1/lib/perl5/5.24.1/darwin-thread-multi-2level
/opt/homebrew/Cellar/perl/5.24.1/lib/perl5/5.24.1
/opt/homebrew/lib/perl5/site_perl/5.24.1

perlcodesampleperlcodesample 2017/07/04 00:25 ありがとうございます。

nekomiminekomimi 2017/07/05 17:30 細かいですが、spvmファイルが見つからない時に出るエラーメッセージで、「SPVM」が「SVPM」になっていましたので報告します。
[SPVM COMPILE ERROR]Can't locate SVPM/hoge.spvm @INC

perlcodesampleperlcodesample 2017/07/10 23:21 nekomimiさん

masterのほうで修正しました!

mtakanobumtakanobu 2017/08/04 03:14 Ubuntu Desktop 16.04.3 LTS(x86_64)の環境でインストールできました。

perlcodesampleperlcodesample 2017/08/05 19:48 mtakanobuさん

インストールありがとうございます。現状は、ループが6倍程度Perl5.26より速いみたいです。今後もアップデートしてまいります。

takasukenichitakasukenichi 2017/08/17 07:32 C#を練習したいんですけどC#を覚えられるようなフリーソフトはありますか?

perlcodesampleperlcodesample 2017/09/05 22:44 すいません。C#については全然詳しくないのでわかりません。

投稿したコメントは管理者が承認するまで公開されません。

スパム対策のためのダミーです。もし見えても何も入力しないでください
ゲスト


画像認証