Python標準実装より速い！？Oracleが作ったGraalPythonをJython・CPythonとベンチマークしてみた！！

#GraalVM・GraalPythonとは

![image.png](https://qiita-image-store.s3.amazonaws.com/0/139643/018360f3-49ff-1de6-402e-029dc5a058b8.png)

GraalVMは2018年4月27日にOracleから公開された仮想マシンです．

> GraalVM is a universal virtual machine for running applications written in JavaScript, Python, Ruby, R, JVM-based languages like Java, Scala, Clojure, Kotlin, and LLVM-based languages such as C and C++.

> GraalVMはJavaScript,Python,Ruby,R,JavaやScala,Clojure,KotlinのようなJVMベースの言語，CやC++のようなLLVMベースの言語で書かれたアプリケーションを動かすための仮想マシンです．

ちょっと何を言ってるかわからないと思いますが，私も分かりませんでした．
GraalVMは本当にこの通りで，jsやPythonのようなスクリプト言語も動き，ScalaやKotlinのようなJVM言語も動き，LLVMベースのバイナリも動く仮想マシンです．

[公式サイト](https://www.graalvm.org/)を見てもらうと一番早いのですが，　

![image.png](https://qiita-image-store.s3.amazonaws.com/0/139643/62921368-dc51-c963-1a16-e309a809a219.png)

本当に信じられないぐらいの数の言語が動く環境が公開されています．
これらの中に，GraalPythonというGraalVM上で動くPython実装が公開されています．

**GraalPythonはCPythonよりどれくらい早いのか？**

ということをベンチマークしていきたいと思います．

#GraalPythonの導入

非常に簡単です．
公式サイトの[ダウンロード](https://github.com/oracle/graal/releases)に行き，tar.gzをダウンロードしていきます．
あとは

```sh
# 解凍
$ tar xvf graalvm-ce-1.0.0-rc8-linux-amd64.tar.gz

# Pythonバインディングのダウンロード
$ ./graalvm-ce-1.0.0-rc8/bin/gu intall python

# GraalPythonの実行
$ ./graalvm-ce-1.0.0-rc8/bin/graalpython 
Please note: This Python implementation is in the very early stages, and can run little more than basic benchmarks at this point.
>>> 
```

これだけで導入できます！！

# Jythonって何？

以前に書いた記事で，Jythonについては取り上げたことがあります．

[Pythonマイナー環境列伝 ～あなたはいくつのPython環境を知っているか？～](https://qiita.com/kotauchisunsun/items/e4f65ad392cd00b60495#_reference-2a34d1855da3d0d23f45)

![image.png](https://qiita-image-store.s3.amazonaws.com/0/139643/bfb98dbc-76b7-48b1-23ec-c83aaf655f0c.png)


いわゆるJVM上で動くPythonの実装です．普通のPythonと違い，Javaのライブラリが使えたりします．
**過去に作られたJVM上で動く，Java実装のPythonはどこまで健闘できるのか？**
ということも検証してみます．

導入方法もUbuntuなら簡単でこんな感じで導入できます．

```sh
$ apt-get install jython
$ jython --version
Jython 2.7.1
```

しかし，現在，Jythonは開発が停止しているらしく，Pythonの2.7系までしか対応していません．


#ベンチマーク

今回，比較対象とするCPythonは

```sh
$ python3 --version
Python 3.6.3
```

実行する内容は前の記事を参考にやっていこうと思います．

[GoとPythonとGrumpyの速度ベンチマーク ～Googleのトランスパイラはどれくらい速い？～](https://qiita.com/kotauchisunsun/items/db28d14f7f13fb29e5f9)

##モンテカルロ法による円周率の求解によるベンチマーク

###実験方法
プログラミングの教本でもよくある円周率のモンテカルロ法による計算をベンチにしました．
よい解説があったので引用で貼っておきます．
試行回数により，処理時間がどのように変化するかを観察します．

![image.png](https://qiita-image-store.s3.amazonaws.com/0/139643/f8b6f9e2-5387-3ae9-688f-5b1e5e526037.png)

引用：[モンテカルロ法と円周率の近似計算](http://mathtrain.jp/montecarlo)

###実装

```python:monte_py.py
#coding:utf-8
import random
import sys

if __name__=="__main__":
    num = int(sys.argv[1])
    c = 0
    for i in range(num):
        x = random.random()
        y = random.random()
        if x * x + y * y <= 1.0:
            c += 1

    print(4.0*c/num)
```

###結果

|繰り返し回数	|Python[sec]|	Jython[sec]|	GraalPython[sec]|
|------:|------:|------:|----:|
|1000000	| 0.941|	7.139|	4.105|
|2000000	| 1.858|	9.553|	4.816|
|3000000	| 2.988|	13.913|	4.969|
|4000000	| 4.022|	14.959|	5.141|
|5000000	| 5.435|	21.144|	6.349|
|6000000	| 7.026|	25.413|	6.774|
|7000000	| 8.701|	34.169|	7.361|
|8000000	|13.682|	61.787|	13.847|
|9000000	|15.613|	40.517|	7.044|
|10000000	|10.684|	33.798|	5.451|
|11000000	|11.394|	26.569|	5.402|
|12000000	|12.661|	38.839|	6.846|
|13000000	|17.158|	63.671|	7.655|
|14000000	|16.572|	47.804|	6.156|
|15000000	|19.187|	57.862|	7.904|
|16000000	|25.037|	87.531|	7.333|
|17000000	|19.235|	54.170|	6.315|
|18000000	|18.775|	68.362|	8.148|
|19000000	|29.085|	68.262|	6.802|
|20000000	|21.175|	65.696|	6.590|

![image.png](https://qiita-image-store.s3.amazonaws.com/0/139643/5cacca72-4297-e793-cea6-17ebcc3ea753.png)

**GraalPython > CPython >> Jython**

###考察

繰り返し回数が小さいときにはCPythonが速いですが，繰り返し回数が大きくなってくるとGraalPythonのほうが速い．という傾向がみられました．一方でJythonはすごく遅い．
こう見るとGraalPythonは中でJITによる高速化が効いているようですね．それによりこのように高速化されているのではないでしょうか．JVMにもJITが入っているので速くなっても不思議ではないのですが，Jythonのほうは速くなりませんね・・・


##竹内関数によるベンチマーク
###竹内関数とは
竹内関数（たけうちかんすう）は、プログラミング言語処理系のベンチマークなどに使われる、再帰的に定義された関数である。

![image.png](https://qiita-image-store.s3.amazonaws.com/0/139643/834d00b7-61f8-b8f1-04b5-c51483710fb6.png)

参考：[竹内関数](https://ja.wikipedia.org/wiki/%E7%AB%B9%E5%86%85%E9%96%A2%E6%95%B0)

###実験方法
Tarai(n,n-1,0)という形で竹内関数を呼び出し，nの大きさと処理時間にどのような変化があるのかを観察する．

###実装

```python:tarai_py.py
#coding:utf-8
import sys

def tak(x,y,z):
    if x<=y:
        return y
    else:
        return tak(tak((x - 1),y,z), tak((y - 1),z,x), tak((z - 1),x,y))

if __name__=="__main__":
    a,b,c = map(int,sys.argv[1].split())
    print(tak(a,b,c))
```

###結果

|n|	CPython[sec]|	Jython[sec]|	GraalPython[sec]|
|------:|------:|------:|----:|
|10|   0.370| 19.500| 1.679|
|11|   2.167| 23.383| 2.448|
|12|  11.929| 16.079| 2.062|
|13|  58.507| 24.814| 5.823|
|14| 453.833|115.069| 43.099|

![image.png](https://qiita-image-store.s3.amazonaws.com/0/139643/ae9dcdd9-cdd8-4e54-1cae-66f146d0be73.png)

nが小さいとき
**CPython > GraalPython > Jython**

nが大きいとき
**GraalPython >> Jython > CPython**

###考察

面白いグラフになりました．nが小さいときはCPythonが速いですが，nが大きくなるにつれ，JythonやGraalPythonが速くなる．という結果になりました．これはJITが効いているように思います．
以前にも似たような検証をしたことがあって，C言語で作った竹内関数のベンチマークとJavaで作った竹内関数のベンチマークをしたことがあります．その時も，nが大きくなった時，Javaのほうが速くなる．といったことが起こりました．このような再起が多く行われる関数だと，Javaのほうが速くなる．ということはままとしてあるようです．そのため，JVMベースのJythonや，それを踏襲したGraalVMを使ったGraalPythonがCPythonを凌駕する速度になることは，割とありえることなのかもしれません．


#まとめ

もともとJVM周りのスクリプトの対応について調べていたところ，GraalPythonを見つけました．そこでGraalVMを知り，あれ？これってJVMと何が違うんだっけ？という疑問から，過去にあったJythonとベンチマークの比較をしてみました．
GraalPythonを少し触ってみましたが，やはり少し機能が足りないな．という気持ちもあります．例えば，python3にはビルトインの関数で，inputがありますが，GraalPythonにはありません．(おそらくinput関数の実装をGraalVM上で実装するのは大変そうですが・・・)
それにしても，時間がかかるような処理．Pythonで5秒以上かかるようなCPUバウンドな処理をすると，GraalPythonのほうが速くなる．というのは驚きでした．
まだいろいろなライブラリ周りと合わせて使っていないので，どんなバグが起こるかわかりませんが，一度いろんなライブラリを試してみるのはいかがでしょうか？