More Related Content Similar to GraalVMの多言語実行機能が凄そうだったので試しにApache Sparkに組み込んで動かしてみたけどちょっとまだ早かったかもしれない(Open Source Conference 2021 Online/Nagoya 発表資料)
Similar to GraalVMの多言語実行機能が凄そうだったので試しにApache Sparkに組み込んで動かしてみたけどちょっとまだ早かったかもしれない(Open Source Conference 2021 Online/Nagoya 発表資料) (20) More from NTT DATA Technology & Innovation
More from NTT DATA Technology & Innovation (20) GraalVMの多言語実行機能が凄そうだったので試しにApache Sparkに組み込んで動かしてみたけどちょっとまだ早かったかもしれない(Open Source Conference 2021 Online/Nagoya 発表資料)1. © 2021 NTT DATA Corporation
GraalVM の多言語実行機能が凄そうだったので試しに Apache Spark に
組み込んで動かしてみたけどちょっとまだ早かったかもしれない
2021年5月29日
株式会社NTTデータ 技術革新統括本部 技術開発本部
先進コンピューティング技術センタ
2. © 2021 NTT DATA Corporation 2
自己紹介
• 名前
刈谷 満(かりや みつる)
• 所属
(株)NTTデータ
技術革新統括本部
技術開発本部
先進コンピューティング技術センタ(略して先進コン
• 現在の業務
Java でごにょごにょやってます
(でもよくよく考えると Java 触ってる時間少ないかも…
3. © 2021 NTT DATA Corporation 3
先進コンに所属するオープンソース・コミッタ等
こんな人達がいる職場で働いてます。(先進コン総勢は 20 名ほど
• Hadoopの継続的な品質改善
• HTrace のモジュール開発
• HDFSにトレーシング機能を追加
• Hadoop→PostgreSQL高速ロード対応
• 非同期レプリケーションの実現
• 同期レプリケーションの実現
• カスケードレプリケーションの実現
• pg_bigm(全文検索モジュール)の開発
岩崎 正剛
Apache Sparkコミッタ
Apache Bahirコミッタ
• Spark内部タスクの可視化
(Timeline Viewer)
• Sparkメトリクス機能改善
• SparkとYARNの連係動作の改善
猿田 浩輔
PostgreSQLコミッタ
藤井 雅雄
Apache Bigtop PMC/コミッタ
Apache Yetus PMC/コミッタ
Apache Airflow PMC/コミッタ
Apache Thrift コミッタ
関 堅吾
• JVMデバッガの改善、拡充
• JVM運用機能の改善、拡充
• JVMロギング機能の改善
• HeapStatsの開発
末永 恭正
OpenJDK Reviewer
IcedTea Committer
阪田 浩一
Java Champion
OpenJDK Author
Apache Hadoop PMC/コミッタ
Apache Bigtop コミッタ
• 大規模Javaアプリケーション開発・運用での
技術課題対応
• 関西Javaユーザグループの創設、代表
(10年間)
4. © 2021 NTT DATA Corporation 4
Java チーム
センタに所属するオープンソース・コミッタ等
こんな人達がいる職場で働いてます。(センタ総勢は 20 名ほど
• Hadoopの継続的な品質改善
• HTrace のモジュール開発
• HDFSにトレーシング機能を追加
• Hadoop→PostgreSQL高速ロード対応
• 非同期レプリケーションの実現
• 同期レプリケーションの実現
• カスケードレプリケーションの実現
• pg_bigm(全文検索モジュール)の開発
岩崎 正剛
Apache Sparkコミッタ
Apache Bahirコミッタ
• Spark内部タスクの可視化
(Timeline Viewer)
• Sparkメトリクス機能改善
• SparkとYARNの連係動作の改善
猿田 浩輔
PostgreSQLコミッタ
藤井 雅雄
Apache Bigtop PMC/コミッタ
Apache Yetus PMC/コミッタ
Apache Airflow PMC/コミッタ
Apache Thrift コミッタ
関 堅吾
• JVMデバッガの改善、拡充
• JVM運用機能の改善、拡充
• JVMロギング機能の改善
• HeapStatsの開発
末永 恭正
OpenJDK Reviewer
IcedTea Committer
阪田 浩一
Java Champion
OpenJDK Author
Apache Hadoop PMC/コミッタ
Apache Bigtop コミッタ
• 大規模Javaアプリケーション開発・運用での
技術課題対応
• 関西Javaユーザグループの創設、代表
(10年間)
5. © 2021 NTT DATA Corporation 5
Java チーム
センタに所属するオープンソース・コミッタ等
こんな人達がいる職場で働いてます。(センタ総勢は 20 名ほど
• Hadoopの継続的な品質改善
• HTrace のモジュール開発
• HDFSにトレーシング機能を追加
• Hadoop→PostgreSQL高速ロード対応
• 非同期レプリケーションの実現
• 同期レプリケーションの実現
• カスケードレプリケーションの実現
• pg_bigm(全文検索モジュール)の開発
岩崎 正剛
Apache Sparkコミッタ
Apache Bahirコミッタ
• Spark内部タスクの可視化
(Timeline Viewer)
• Sparkメトリクス機能改善
• SparkとYARNの連係動作の改善
猿田 浩輔
PostgreSQLコミッタ
藤井 雅雄
Apache Bigtop PMC/コミッタ
Apache Yetus PMC/コミッタ
Apache Airflow PMC/コミッタ
Apache Thrift コミッタ
関 堅吾
• JVMデバッガの改善、拡充
• JVM運用機能の改善、拡充
• JVMロギング機能の改善
• HeapStatsの開発
末永 恭正
OpenJDK Reviewer
IcedTea Committer
阪田 浩一
Java Champion
OpenJDK Author
Apache Hadoop PMC/コミッタ
Apache Bigtop コミッタ
• 大規模Javaアプリケーション開発・運用での
技術課題対応
• 関西Javaユーザグループの創設、代表
(10年間)
プランクトン刈谷
6. © 2021 NTT DATA Corporation 6
本日の趣旨
GraalVM 上で動く Python を Apache Spark に
組み込んで試してみたが、結構ツラかった面白かったの
で、みなさんに GraalVM や GraalPython を知って
頂いて、同じ苦しみを味あわせたいその楽しさを是非味
わってもらいたい。
あ、あと、せっかくなのでついでに組み込んでみた結果ど
うなったかもちょっとお話しようかと…
8. © 2021 NTT DATA Corporation 8
そもそも GraalVM って何?
ざっくり言うと…
1.OpenJDK の JVM の JIT コンパイラを
Java で独自実装した物に差し替えて…
2.ネイティブイメージ作れるようにしたり…
3.多言語実行環境(Polyglot)作ったり…
した OSS の JVM。
9. © 2021 NTT DATA Corporation 9
そもそも GraalVM って何?
ざっくり言うと…
1.OpenJDK の JVM の JIT コンパイラを
Java で独自実装した物に差し替えて…
2.ネイティブイメージ作れるようにしたり…
3.多言語実行環境(Polyglot)作ったり…
した OSS の JVM。
コイツが「GraalVM コンパイラ」と呼ばれている。
以前はコイツを指して「Graal」って言ってたと
思うんだが…
10. © 2021 NTT DATA Corporation 10
多言語実行環境ってどういうの?
ざっくり言うと…
1.GraalVM 上で JVM 言語「以外」を動か
せるようにして…
2.更にそれらを相互呼び出し出来るようにした
もので…
3.Truffle と言う言語実装フレームワークがあ
る
11. © 2021 NTT DATA Corporation 11
多言語って言うけどどんな言語があるの?
21.1 時点で以下のような感じ
言語 名前 ステータス 備考
Java - - OpenJDK 8、11、16(ただし 16 は Experimental)
JavaScript graaljs GA ECMAScript 2020 準拠。node.js 14.16.1 のランタイムもあるよ
Ruby TruffleRuby Experimental Ruby 2.7.2 準拠
R FastR Experimental GNU R 4.0.3 準拠
Python GraalPython Experimental Python 3.8.5 準拠
WebAssembly GraalWasm Experimental 準拠バージョン不明。あと、バイナリフォーマットだけっぽい…
LLVM Sulong Experimental Clang 7 で出力された LLVM bitcode だけが対象っぽい…
Java on Truffle Espresso Experimental Truffle 上で Java を動かすらしい。OpenJDK 8、11。
ちなみに、サードパーティー製のものもいくつかある(grCUDA とか Smalltalk とか Squeak とか)
12. © 2021 NTT DATA Corporation 12
多言語って言うけどどんな言語があるの?
21.1 時点で以下のような感じ
言語 名前 ステータス 備考
Java - - OpenJDK 8、11、16(ただし 16 は Experimental)
JavaScript graaljs GA ECMAScript 2020 準拠。node.js 14.16.1 のランタイムもあるよ
Ruby TruffleRuby Experimental Ruby 2.7.2 準拠
R FastR Experimental GNU R 4.0.3 準拠
Python GraalPython Experimental Python 3.8.5 準拠
WebAssembly GraalWasm Experimental 準拠バージョン不明。あと、バイナリフォーマットだけっぽい…
LLVM Sulong Experimental Clang 7 で出力された LLVM bitcode だけが対象っぽい…
Java on Truffle Espresso Experimental Truffle 上で Java を動かすらしい。OpenJDK 8、11。
ちなみに、サードパーティー製のものもいくつかある(grCUDA とか Smalltalk とか Squeak とか)
_人人人人人人人人人_
> ほとんど <
> Experimental! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
13. © 2021 NTT DATA Corporation 13
Community Edition と Enterprise Edition
項目 Community Edition (CE) Enterprise Edition (EE)
ライセンス クラスパス例外付きGPL v2 GraalVM OTNライセンス
費用 無償 有償(試用は無償)
ソース オープンソース クローズドソース(ベースはCE)
EEの技術的優位性 • パフォーマンス向上 - JITコンパイラに最適化手法を追加、
メモリ消費量を削減
• あらゆるタイプのアプリケーションで高速化
• ScalaアプリケーションでOpenJDKより3倍高速と顕著
• セキュリティ強化 - マネージドモードでのネイティブコード実行(メモリアクセスの管理)
• ネイティブイメージの実行時にヒープダンプを生成可能
• ネイティブイメージにデバッグ情報を格納できる
• 現時点で正式サポートはEEのみ
• profile-guided optimizations (PGO)を利用可能
14. © 2021 NTT DATA Corporation 14
OpenJDK, GraalVM CE, GraalVM EEのベンチマーク(Renaissance Suite)
https://renaissance.dev/
Higher is better
15. © 2021 NTT DATA Corporation 15
GraalVM のリリースサイクル
• CE
• 3ヶ月ごとにバージョンアップする(2月、5月、8月、11月)
• Functional Release
• その年の最後のリリースがAnnual Releaseとして次の1年間サポートされる
• Maintenance Release
• EE
• 3年ごとにLTSバージョンが出る(商用サポート)
• 他CEと同じ
• バージョン番号は、yy.[0始まりの年内でのリリース番号] となる。
Functional 19.2------19.3------20.0------20.1-----20.2------20.3------21.0------21.1-----
Maintenance ---19.3.1---19.3.2---19.3.3---19.3.4 ---20.3.1---20.3.2---20.3.3--
16. © 2021 NTT DATA Corporation
Apache Spark について
16
17. © 2021 NTT DATA Corporation 17
そもそも Apache Spark って何?
ざっくり言うと…
1.大量のデータを…
2.複数台のサーバで構成されたクラスタで…
3.現実的な時間内に処理する…
ための、JVM 上で動作する OSS の
「並列分散処理フレームワーク」。
18. © 2021 NTT DATA Corporation 18
そもそも Apache Spark って何?
Driver
クライアント
もしくは
ワーカノード
ワーカノード
Executor
ワーカノード
Executor
ワーカノード
Executor
ユーザが記述した
データ処理プログラム
タスク
タスク
タスク
タスク
タスク タスク
・
・
・
ジョブ
②タスクのスケ
ジューリングと処理
するデータの指示
③割り当てられた
データを入力とし、
タスクを実行
①データ処理の
内容からジョブとタ
スクを生成
タスクに割り当てられるデータは処理対象のデータ
セット全体のサブセット(パーティション)を構成する。
19. © 2021 NTT DATA Corporation 19
そもそも Apache Spark って何?
もうちょっと知りたかったら、
ウチの猿田の講演動画&資料が
分かりやすいので是非見てください!
(実は人に説明できるほど良く分かってない…)
セッション概要:https://event.ospn.jp/osc2020-online-fukuoka/session/244758
セッション動画:https://youtu.be/Kv4x97qFuDw
セッション資料:https://www2.slideshare.net/nttdata-tech/spark-introduction-osc-
fukuoka-nttdata-saruta
20. © 2021 NTT DATA Corporation 20
そもそも Apache Spark って何?
個人的に思う、Spark のいいところ。
データ処理プログラムを、データ分析の分野で人気
の Python で書ける。(PySpark と呼ばれる)
特に、データに適用する関数を Python で書ける。
(ユーザ定義関数(UDF)と言う)
※ 個人の見解であり、所属する組織の公式見解ではありません
21. © 2021 NTT DATA Corporation 21
そもそも Apache Spark って何?
個人的に思う、Python のいいところ。
データ分析に有用な NumPy や Pandas
と言ったライブラリが充実している。
Python は遅いが、NumPy や Pandas はメイ
ンの計算処理が C 言語で書かれているため速い。
※ 個人の見解であり、所属する(ry
22. © 2021 NTT DATA Corporation
何で Spark で GraalVM を
使おうと思ったの?
22
23. © 2021 NTT DATA Corporation 23
何で Spark で GraalVM を使おうと思ったの?
Spark では「ユーザ定義関数(UDF)」なるものを
Python で書ける。
⇓
処理時には Python で処理するために
Python インタプリタが起動する
⇓
一方で Spark 本体は JVM 上で動く
⇓
Python が JVM 上で動けば速くなるんじゃね?(小並感
24. © 2021 NTT DATA Corporation 24
UDFをJVM言語で記述した場合と異なり、Pythonワーカとのソケット通信およびそれに伴うシリアライズ/デシリ
アライズのコストが発生する。ただし①、②、③の処理はそれぞれ非同期に実行され、パイプライン化されている。
何で Spark で GraalVM を使おうと思ったの?
・
・
・
UDF
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
0101
Executor Pythonワーカ
UDF適用後のレコード
③UDF適用済みのバッチ
をデシリアライズし、後続
の処理に渡す
デシリアライズ
バッチ
①レコードから必要なカラムを抽出
し、「バッチ」単位でシリアライズして
ソケット経由でPythonワーカに渡す
前段の処理から
渡されるレコード
②受信したバッチ
をデシリアライズし
て各レコードに
UDFを適用する。
UDFを適用した
結果は再びバッ
チ単位でシリアラ
イズして
Executorに返す
デシリアライズ
シリアライズ
シリアライズ
25. © 2021 NTT DATA Corporation 25
何で Spark で GraalVM を使おうと思ったの?
Executor
Javaコンテキスト Pythonコンテキスト
・
・
・
前段の処理から
渡されるレコード
UDF
UDFの処理に必要な
カラムを抽出したレコード
Javaコンテキストのレコードを
Pythonコンテキストから参照する
UDF適用後のレコード
Pythonコンテキストの処理結果
をJavaコンテキストから参照する
後続の処理へ
26. © 2021 NTT DATA Corporation
GraalPython を使ってみよう
26
27. © 2021 NTT DATA Corporation 27
GraalPython を実行してみる
GraalPython を起動するコマンドは以下の通り。 (下線に黄色は入力した箇所
あっさりと GraalPython の REPL(Read-Eval-Print Loop)が起動されてプロンプト(>>>)が出ると思
うので、試しに Python の命令を実行してみよう。 (下線に黄色は入力した箇所
これで Hello, GraalPython! と表示されれば万事 OK。
$ graalpython
Python 3.8.5 (Fri Apr 16 11:46:53 UTC 2021)
[Graal, GraalVM CE, Java 11.0.11] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>>
>>> print('Hello, GraalPython!')
Hello, GraalPython!
>>>
28. © 2021 NTT DATA Corporation 28
REPL から実行してるだけじゃ GraalPython の意味なくね?
と言うわけで、Java から Python を実行してみる。
ソースコードはこちら。
import org.graalvm.polyglot.Context;
public class Hello {
public static void main(String[] args) {
try (Context ctx = Context.create()) {
ctx.eval("python", "print('Hello, Polyglot!')");
}
}
}
Java から Python を実行してみる
29. © 2021 NTT DATA Corporation 29
import org.graalvm.polyglot.Context;
public class Hello {
public static void main(String[] args) {
try (Context ctx = Context.create()) {
ctx.eval("python", "print('Hello, Polyglot!')");
}
}
}
Java から Python を実行してみる
REPL から実行してるだけじゃ GraalPython の意味なくね?
と言うわけで、Java から Python を実行してみる。
ソースコードはこちら。
ゲスト言語に対する Context(実行環境)を構築する。
ゲスト言語は全て Context の内部で実行される。
ちなみに Context はいくつでも作れる。
30. © 2021 NTT DATA Corporation 30
import org.graalvm.polyglot.Context;
public class Hello {
public static void main(String[] args) {
try (Context ctx = Context.create()) {
ctx.eval("python", "print('Hello, Polyglot!')");
}
}
}
Java から Python を実行してみる
REPL から実行してるだけじゃ GraalPython の意味なくね?
と言うわけで、Java から Python を実行してみる。
ソースコードはこちら。
Context.eval メソッドで実行
31. © 2021 NTT DATA Corporation 31
import org.graalvm.polyglot.Context;
public class Hello {
public static void main(String[] args) {
try (Context ctx = Context.create()) {
ctx.eval("python", "print('Hello, Polyglot!')");
}
}
}
Java から Python を実行してみる
REPL から実行してるだけじゃ GraalPython の意味なくね?
と言うわけで、Java から Python を実行してみる。
ソースコードはこちら。
実行するゲスト言語の種別は
ここの文字列で指定する。
32. © 2021 NTT DATA Corporation 32
import org.graalvm.polyglot.Context;
public class Hello {
public static void main(String[] args) {
try (Context ctx = Context.create()) {
ctx.eval("python", "print('Hello, Polyglot!')");
}
}
}
Java から Python を実行してみる
REPL から実行してるだけじゃ GraalPython の意味なくね?
と言うわけで、Java から Python を実行してみる。
ソースコードはこちら。
実行するプログラムはここの文字列で指定する。
33. © 2021 NTT DATA Corporation 33
実行方法は普通の Java プログラムと一緒。 (下線に黄色は入力した箇所
$ javac Hello.java
$ java Hello
Hello, Polyglot!
Java から Python を実行してみる
34. © 2021 NTT DATA Corporation 34
実行方法は普通の Java プログラムと一緒。 (下線に黄色は入力した箇所
ちなみに、Java 11 以降であれば、当然コンパイルせずにちゃんと実行できる。便利!
$ javac Hello.java
$ java Hello
Hello, Polyglot!
Java から Python を実行してみる
$ java Hello.java
Hello, Polyglot!
35. © 2021 NTT DATA Corporation 35
Java から Python を実行してみる
ちなみに、ここでは Java から Python を
呼び出しているだけだが、
逆に Python から Java を呼び出したり、
他の言語、例えば JavaScript と Python
で相互に呼び出したりすることも可能
だけど時間が無いので割愛(ゆるして…
36. © 2021 NTT DATA Corporation 36
Java から Python を実行してみる(スクリプトファイルを実行)
Java のソースの中に Python のソースが混ざってるとメチャクチャ読みにくい!
と言うわけで、なんと Python のソースを別ファイルに書いておいて実行することもできる!(まぁ当然か…
import java.io.*;
import org.graalvm.polyglot.*;
public class HelloFile {
public static void main(String[] args) throws IOException {
File file = new File("hello.py");
Source src = Source.newBuilder("python", file).build();
try (Context ctx = Context.create()) {
ctx.eval(src);
}
}
}
37. © 2021 NTT DATA Corporation 37
Java から Python を実行してみる(スクリプトファイルを実行)
Java のソースの中に Python のソースが混ざってるとメチャクチャ読みにくい!
と言うわけで、なんと Python のソースを別ファイルに書いておいて実行することもできる!(まぁ当然か…
import java.io.*;
import org.graalvm.polyglot.*;
public class HelloFile {
public static void main(String[] args) throws IOException {
File file = new File("hello.py");
Source src = Source.newBuilder("python", file).build();
try (Context ctx = Context.create()) {
ctx.eval(src);
}
}
}
実行するファイルを File クラスの
インスタンスとして用意して…
38. © 2021 NTT DATA Corporation 38
Java から Python を実行してみる(スクリプトファイルを実行)
Java のソースの中に Python のソースが混ざってるとメチャクチャ読みにくい!
と言うわけで、なんと Python のソースを別ファイルに書いておいて実行することもできる!(まぁ当然か…
import java.io.*;
import org.graalvm.polyglot.*;
public class HelloFile {
public static void main(String[] args) throws IOException {
File file = new File("hello.py");
Source src = Source.newBuilder("python", file).build();
try (Context ctx = Context.create()) {
ctx.eval(src);
}
}
}
それを基にした Source クラスのインスタンスを生成する。
(インスタンス生成は Builder パターン)
39. © 2021 NTT DATA Corporation 39
Java から Python を実行してみる(スクリプトファイルを実行)
Java のソースの中に Python のソースが混ざってるとメチャクチャ読みにくい!
と言うわけで、なんと Python のソースを別ファイルに書いておいて実行することもできる!(まぁ当然か…
import java.io.*;
import org.graalvm.polyglot.*;
public class HelloFile {
public static void main(String[] args) throws IOException {
File file = new File("hello.py");
Source src = Source.newBuilder("python", file).build();
try (Context ctx = Context.create()) {
ctx.eval(src);
}
}
}
Source を Context.eval メソッドに渡して実行
40. © 2021 NTT DATA Corporation 40
Java から Python を実行してみる(スクリプトファイルを実行)
Java のソースの中に Python のソースが混ざってるとメチャクチャ読みにくい!
と言うわけで、なんと Python のソースを別ファイルに書いておいて実行することもできる!(まぁ当然か…
import java.io.*;
import org.graalvm.polyglot.*;
public class HelloFile {
public static void main(String[] args) throws IOException {
File file = new File("hello.py");
Source src = Source.newBuilder("python", file).build();
try (Context ctx = Context.create()) {
ctx.eval(src);
}
}
}
ちなみに、Source クラスを使用する場合は、
ゲスト言語の種別は Source クラスのインスタンス生成時に指定
こっちにゲスト言語の種別指定は無い
41. © 2021 NTT DATA Corporation 41
Java から Python を実行してみる(スクリプトファイルを実行)
当然 Python のソースファイル(今回は hello.py)も用意する必要がある。
print('Hello, Polyglot!')
42. © 2021 NTT DATA Corporation 42
Java から Python を実行してみる(スクリプトファイルを実行)
当然 Python のソースファイル(今回は hello.py)も用意する必要がある。
実行方法は先程と全く一緒。 (下線に黄色は入力した箇所
$ javac Hello.java
$ java Hello
Hello, Polyglot!
print('Hello, Polyglot!')
43. © 2021 NTT DATA Corporation 43
Java から Python を実行してみる(データの受け渡し)
単に Python を実行できるだけじゃ、JVM 上で動かす意味なくね?
と言うわけで、ここからが本題、Java と Python との間でデータの受け渡しをする。
44. © 2021 NTT DATA Corporation 44
Java から Python を実行してみる(データの受け渡し)
単に Python を実行できるだけじゃ、JVM 上で動かす意味なくね?
と言うわけで、ここからが本題、Java と Python との間でデータの受け渡しをする。
import org.graalvm.polyglot.*;
public class AttUQoLtUaE {
public static void main(String[] args) {
try (Context ctx = Context.create()) {
Value func = ctx.eval("python", "lambda x, y: x * y");
Value ans = func.execute(7, 6);
System.out.println("The answer is " + ans);
}
}
}
45. © 2021 NTT DATA Corporation 45
Java から Python を実行してみる(データの受け渡し)
単に Python を実行できるだけじゃ、JVM 上で動かす意味なくね?
と言うわけで、ここからが本題、Java と Python との間でデータの受け渡しをする。
import org.graalvm.polyglot.*;
public class AttUQoLtUaE {
public static void main(String[] args) {
try (Context ctx = Context.create()) {
Value func = ctx.eval("python", "lambda x, y: x * y");
Value ans = func.execute(7, 6);
System.out.println("The answer is " + ans);
}
}
}
実は eval メソッドは実行結果を Value と言うクラスで返す。
今回の例の場合は、Python のラムダ式が値として返る。
46. © 2021 NTT DATA Corporation 46
Java から Python を実行してみる(データの受け渡し)
単に Python を実行できるだけじゃ、JVM 上で動かす意味なくね?
と言うわけで、ここからが本題、Java と Python との間でデータの受け渡しをする。
import org.graalvm.polyglot.*;
public class AttUQoLtUaE {
public static void main(String[] args) {
try (Context ctx = Context.create()) {
Value func = ctx.eval("python", "lambda x, y: x * y");
Value ans = func.execute(7, 6);
System.out.println("The answer is " + ans);
}
}
}
返ってきた値はラムダ式なので、execute メソッドで実行できる。
その実行結果もまた Value クラスで返ってくる。
47. © 2021 NTT DATA Corporation 47
Java から Python を実行してみる(データの受け渡し)
実行方法は先程と全く一緒。 (下線に黄色は入力した箇所
ディープ・ソートが計算に750万年かかった疑問も、一瞬で答えが出る。そう、GraalVM ならね!
(たぶん GraalVM じゃなくても一瞬で出ると思う…
$ javac AttUQoLtUaE.java
$ java AttUQoLtUaE
The answer is 42
48. © 2021 NTT DATA Corporation 48
どんなデータでも受け渡しできるの?
ところで、なんか無造作に
Java ⇔ Python 間で
データ受け渡してるけど、
どんなデータでも受け渡しできるの?
49. © 2021 NTT DATA Corporation 49
どんなデータでも受け渡しできるの?
Java のプリミティブ型、プリミティブラッパー型の数値を Python に渡す場合
(byte、short、int、long、float、double、Byte、Short、Integer、Long、Float、Double)
50. © 2021 NTT DATA Corporation 50
どんなデータでも受け渡しできるの?
Java のプリミティブ型、プリミティブラッパー型の数値を Python に渡す場合
(byte、short、int、long、float、double、Byte、Short、Integer、Long、Float、Double)
単純に引数としてそのまま渡せば大丈夫。
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(Long.MAX_VALUE);
func.executeVoid(Double.valueOf(Double.MAX_VALUE));
51. © 2021 NTT DATA Corporation 51
どんなデータでも受け渡しできるの?
Java のプリミティブ型、プリミティブラッパー型の数値を Python に渡す場合
(byte、short、int、long、float、double、Byte、Short、Integer、Long、Float、Double)
単純に引数としてそのまま渡せば大丈夫。
結果は以下の通り。極めて順当。
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(Long.MAX_VALUE);
func.executeVoid(Double.valueOf(Double.MAX_VALUE));
<class 'int'>:9223372036854775807
<class 'float'>:1.7976931348623157e+308
52. © 2021 NTT DATA Corporation 52
Java から Python を実行してみる(その2)
Java のプリミティブ型、プリミティブラッパー型の数値を Python に渡す場合
(byte、short、int、long、float、double、Byte、Short、Integer、Long、Float、Double)
単純に引数としてそのまま渡せば大丈夫。
結果は以下の通り。極めて順当。
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(Long.MAX_VALUE);
func.executeVoid(Double.valueOf(Double.MAX_VALUE));
<class 'int'>:9223372036854775807
<class 'float'>:1.7976931348623157e+308
ちなみに、ここで使った executeVoid メソッドは、
execute メソッドとほぼ一緒だが、戻り値を返さない
53. © 2021 NTT DATA Corporation 53
どんなデータでも受け渡しできるの?
Java の真偽値、文字、文字列を Python に渡す場合
(boolean、Boolean、char、Character、String)
54. © 2021 NTT DATA Corporation 54
どんなデータでも受け渡しできるの?
Java の真偽値、文字、文字列を Python に渡す場合
(boolean、Boolean、char、Character、String)
この場合も、単純に引数としてそのまま渡せば大丈夫。
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(Boolean.TRUE);
func.executeVoid('a');
func.executeVoid("AttUQoLtUaE");
55. © 2021 NTT DATA Corporation 55
どんなデータでも受け渡しできるの?
Java の真偽値、文字、文字列を Python に渡す場合
(boolean、Boolean、char、Character、String)
この場合も、単純に引数としてそのまま渡せば大丈夫。
結果は以下の通り。
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(Boolean.TRUE);
func.executeVoid('a');
func.executeVoid("AttUQoLtUaE");
<class 'bool'>:True
<class 'str'>:a
<class 'str'>:AttUQoLtUaE
56. © 2021 NTT DATA Corporation 56
どんなデータでも受け渡しできるの?
Java の真偽値、文字、文字列を Python に渡す場合
(boolean、Boolean、char、Character、String)
この場合も、単純に引数としてそのまま渡せば大丈夫。
結果は以下の通り。
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(Boolean.TRUE);
func.executeVoid('a');
func.executeVoid("AttUQoLtUaE");
<class 'bool'>:True
<class 'str'>:a
<class 'str'>:AttUQoLtUaE
「文字(char)」も「文字列(str)」になっていることに注意。
57. © 2021 NTT DATA Corporation 57
どんなデータでも受け渡しできるの?
Java の 配列や List を Python に渡す場合
このあたりからちょっと雲行きが怪しく…
58. © 2021 NTT DATA Corporation 58
どんなデータでも受け渡しできるの?
Java の 配列や List を Python に渡す場合
このあたりからちょっと雲行きが怪しく…
単純に引数としてそのまま渡すだけだと…
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(new int[]{1, 2, 3});
func.executeVoid(List.of(4, 5, 6));
59. © 2021 NTT DATA Corporation 59
どんなデータでも受け渡しできるの?
Java の 配列や List を Python に渡す場合
このあたりからちょっと雲行きが怪しく…
単純に引数としてそのまま渡すだけだと…
一瞬それっぽい値は渡っているように見えるが…
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(new int[]{1, 2, 3});
func.executeVoid(List.of(4, 5, 6));
<class 'foreign'>:[1, 2, 3]
<class 'foreign'>:[4, 5, 6]
60. © 2021 NTT DATA Corporation 60
<class 'foreign'>:[1, 2, 3]
<class 'foreign'>:[4, 5, 6]
どんなデータでも受け渡しできるの?
Java の 配列や List を Python に渡す場合
このあたりからちょっと雲行きが怪しく…
単純に引数としてそのまま渡すだけだと…
一瞬それっぽい値は渡っているように見えるが…
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(new int[]{1, 2, 3});
func.executeVoid(List.of(4, 5, 6));
_人人人人人人_
> ナゾの <
> foregin! <
 ̄Y^Y^Y^Y^Y^Y^ ̄
61. © 2021 NTT DATA Corporation 61
どんなデータでも受け渡しできるの?
実はこれ、Java 側だけではどうにもならなくて、引数を受け取った Python 側で何とかする必要がある。
ただ、幸いなことに配列や Iterable な Java オブジェクトは Python 側でも Iterable と認識されるので、
Iterable を引数に受け取るコンストラクタがあれば、簡単に変換することができる。
62. © 2021 NTT DATA Corporation 62
どんなデータでも受け渡しできるの?
実はこれ、Java 側だけではどうにもならなくて、引数を受け取った Python 側で何とかする必要がある。
ただ、幸いなことに配列や Iterable な Java オブジェクトは Python 側でも Iterable と認識されるので、
Iterable を引数に受け取るコンストラクタがあれば、簡単に変換することができる。
Value func = ctx.eval("python", "def func(x):n"
+ " y = list(x)n"
+ " print(f'{type(y)}:{y}')n"
+ "func");
func.executeVoid(new int[]{1, 2, 3});
func.executeVoid(List.of(4, 5, 6));
63. © 2021 NTT DATA Corporation 63
どんなデータでも受け渡しできるの?
実はこれ、Java 側だけではどうにもならなくて、引数を受け取った Python 側で何とかする必要がある。
ただ、幸いなことに配列や Iterable な Java オブジェクトは Python 側でも Iterable と認識されるので、
Iterable を引数に受け取るコンストラクタがあれば、簡単に変換することができる。
Value func = ctx.eval("python", "def func(x):n"
+ " y = list(x)n"
+ " print(f'{type(y)}:{y}')n"
+ "func");
func.executeVoid(new int[]{1, 2, 3});
func.executeVoid(List.of(4, 5, 6));
ここで受け取った引数 x を list 型に変換
64. © 2021 NTT DATA Corporation 64
実はこれ、Java 側だけではどうにもならなくて、引数を受け取った Python 側で何とかする必要がある。
ただ、幸いなことに配列や Iterable な Java オブジェクトは Python 側でも Iterable と認識されるので、
Iterable を引数に受け取るコンストラクタがあれば、簡単に変換することができる。
Python の list 型に変換することができた!
Value func = ctx.eval("python", "def func(x):n"
+ " y = list(x)n"
+ " print(f'{type(y)}:{y}')n"
+ "func");
func.executeVoid(new int[]{1, 2, 3});
func.executeVoid(List.of(4, 5, 6));
どんなデータでも受け渡しできるの?
<class 'list'>:[1, 2, 3]
<class 'list'>:[4, 5, 6]
65. © 2021 NTT DATA Corporation 65
その他の方法として、あらかじめ引数を Python の list にしておく、と言う方法もある。
どんなデータでも受け渡しできるの?
66. © 2021 NTT DATA Corporation 66
その他の方法として、あらかじめ引数を Python の list にしておく、と言う方法もある。
Value bindings = ctx.getBindings("python");
Value list = bindings.getMember("list");
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(list.newInstance(new int[]{1, 2, 3}));
func.executeVoid(list.newInstance(List.of(4, 5, 6)));
どんなデータでも受け渡しできるの?
67. © 2021 NTT DATA Corporation 67
その他の方法として、あらかじめ引数を Python の list にしておく、と言う方法もある。
※ バインディング:ざっくり言うと、関数、変数、クラス、モジュールなどを、名前に紐づけた物、および、
その集合のこと。今回の場合は、「最上位」バインディングなので、グローバルなヤツ。
Value bindings = ctx.getBindings("python");
Value list = bindings.getMember("list");
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(list.newInstance(new int[]{1, 2, 3}));
func.executeVoid(list.newInstance(List.of(4, 5, 6)));
どんなデータでも受け渡しできるの?
Python の最上位バインディング(※)を取得して…
68. © 2021 NTT DATA Corporation 68
その他の方法として、あらかじめ引数を Python の list にしておく、と言う方法もある。
Value bindings = ctx.getBindings("python");
Value list = bindings.getMember("list");
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(list.newInstance(new int[]{1, 2, 3}));
func.executeVoid(list.newInstance(List.of(4, 5, 6)));
どんなデータでも受け渡しできるの?
そのバインディングから list クラスを取得しておいて…
69. © 2021 NTT DATA Corporation 69
その他の方法として、あらかじめ引数を Python の list にしておく、と言う方法もある。
Value bindings = ctx.getBindings("python");
Value list = bindings.getMember("list");
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(list.newInstance(new int[]{1, 2, 3}));
func.executeVoid(list.newInstance(List.of(4, 5, 6)));
どんなデータでも受け渡しできるの?
引数を渡す際に、newInstance メソッドを使って list クラスに変換してしまう。
ちなみに、少なくとも Python の場合は単なる execute メソッドを使っても大丈夫。
70. © 2021 NTT DATA Corporation 70
その他の方法として、あらかじめ引数を Python の list にしておく、と言う方法もある。
結果はさっきと一緒。
Value bindings = ctx.getBindings("python");
Value list = bindings.getMember("list");
Value func = ctx.eval("python", "lambda x: print(f'{type(x)}:{x}')");
func.executeVoid(list.newInstance(new int[]{1, 2, 3}));
func.executeVoid(list.newInstance(List.of(4, 5, 6)));
どんなデータでも受け渡しできるの?
<class 'list'>:[1, 2, 3]
<class 'list'>:[4, 5, 6]
71. © 2021 NTT DATA Corporation 71
どんなデータでも受け渡しできるの?
配列とかリストとかがネストしたデータ構造だと、
データ構造の中までしっかり変換して
やらないといけない。
これは結構面倒だった…
Spark の場合、データ構造はジョブ実行時にユーザが指定する
ものであって、あらかじめ決まってるわけではないので…
72. © 2021 NTT DATA Corporation 72
どんなデータでも受け渡しできるの?
その他、BigInteger、BigDecimal や、
LocalDate 等の日付型系も、
単純には渡せないので、小細工が必要。
73. © 2021 NTT DATA Corporation 73
どんなデータでも受け渡しできるの?
なお、Python から Java へ渡す場合も、
int とか String みたいな簡単な型以外は
同じようにいろいろな制約があって、
なかなか一筋縄ではいかない。
だけど時間が無いので割愛(ゆるして…
74. © 2021 NTT DATA Corporation 74
どんなデータでも受け渡しできるの?
どんなデータでも受け渡しできるの?
⇓
渡せるけど、いろいろ対処が必要…
75. © 2021 NTT DATA Corporation 75
どんなデータでも受け渡しできるの?
ちなみに、
Java のクラスインスタンスの public な
メソッドを Python で呼び出したり、
Python のクラスインスタンスのメソッドを
Java で呼び出したりすることも、
簡単に出来る。
76. © 2021 NTT DATA Corporation 76
各種パッケージをインストールする
Python と言えば、NumPy や Pandas 等
のライブラリが使えないと意味ないよね!
⇓
ライブラリインストールする時は、
仮想環境作るよね?
77. © 2021 NTT DATA Corporation 77
各種パッケージをインストールする
GraalPython では仮想環境作るのに
標準の venv モジュールを使う。
Python でも標準だしね…
78. © 2021 NTT DATA Corporation 78
と言うわけで、venv を使って仮想環境を構築してみる。
$ time graalpython -m venv venv-21.1.0.r11
We're not using symlinks in a Graal Python venv
real 0m53.775s
user 2m14.143s
sys 0m9.444s
仮想環境を構築する
79. © 2021 NTT DATA Corporation 79
と言うわけで、venv を使って仮想環境を構築してみる。
$ time graalpython -m venv venv-21.1.0.r11
We're not using symlinks in a Graal Python venv
real 0m53.775s
user 2m14.143s
sys 0m9.444s
仮想環境を構築する
_人人人人人人_
> 54秒!! <
 ̄Y^Y^Y^Y^Y^Y^ ̄
(Amazon EC2 t3a.xlarge)
80. © 2021 NTT DATA Corporation 80
と言うわけで、venv を使って仮想環境を構築してみる。
念のため、アクティベートしてから Python のバージョンを確認する。
ちゃんとできてはいる。
$ time graalpython -m venv venv-21.1.0.r11
We're not using symlinks in a Graal Python venv
real 0m53.775s
user 2m14.143s
sys 0m9.444s
仮想環境を構築する
$ . venv-21.1.0.r11/bin/activate
(venv-21.1.0.r11) $ python -V
Python 3.8.5 (GraalVM CE Native 21.1.0)
81. © 2021 NTT DATA Corporation 81
ちなみに、普通の Python の場合は…
$ time python3 -m venv venv
real 0m2.038s
user 0m1.880s
sys 0m0.158s
仮想環境を構築する
82. © 2021 NTT DATA Corporation 82
ちなみに、普通の Python の場合は…
$ time python3 -m venv venv
real 0m2.038s
user 0m1.880s
sys 0m0.158s
仮想環境を構築する
_人人人人人人人人人_
> たった2秒!! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
(Amazon EC2 t3a.xlarge)
83. © 2021 NTT DATA Corporation 83
Pandas をインストールする
続いて、NumPy、Pandas を
インストールする。
ただし、インストールには pip じゃなくて、
ginstall モジュールを使用する。
(GraalPython 専用のパッケージインストール用モジュール)
84. © 2021 NTT DATA Corporation 84
Pandas をインストールする
何で pip じゃなくて ginstall なの?
⇓
ソースをダウンロードしてきて、
GraalVM で動くようにパッチを当てて、
ビルドしてるから
85. © 2021 NTT DATA Corporation 85
Pandas をインストールする
ginstall が知ってるパッケージの
一覧はコマンドで表示できる。
$ python -m ginstall install -h
usage: ginstall.py install [-h] [--prefix PREFIX] package
Install a known package. Known packages are pytest, pytest_parallel, py, attrs,
pyparsing, packaging, more_itertools, atomicwrites, pluggy, zipp, wcwidth,
PyYAML, six, Cython, setuptools, pkgconfig, wheel, protobuf,
Keras_preprocessing, gast, astor, absl_py, mock, Markdown, Werkzeug, h5py,
sortedcontainers, hypothesis, setuptools_scm, numpy, dateutil, certifi, idna,
chardet, urllib3, requests, lightfm, pytz, pandas, scipy, cycler, cppy,
cassowary, Pillow, matplotlib
86. © 2021 NTT DATA Corporation 86
Pandas をインストールする
注意
ginstall が「知ってる」からと言って
インストールできる・動くとは限らない
例:SciPy 知ってる(動くとは言ってない
87. © 2021 NTT DATA Corporation 87
と言うわけで、Pandas をインストールしてみる。NumPy 等の依存パッケージは自動でインストールされる。
結果は以下の通り。
こちらも念のため、アクティベートしてから Python のバージョンを確認する。
16 分超かかった…(Amazon EC2 t3a.xlarge)
とても、ツラい…
(venv-21.1.0.r11) $ time python -m ginstall install pandas
・
・
・
real 16m12.857s
user 34m33.650s
sys 3m59.179s
Pandas をインストールする
88. © 2021 NTT DATA Corporation 88
Pandas をインストールする
何でそんなにかかるの?
⇓
さっきも言ったように、
ソースをダウンロードしてきて、
GraalVM で動くようにパッチを当てて、
ビルドしてるから
89. © 2021 NTT DATA Corporation 89
Pandas をインストールする
注意
カレントディレクトリに、インストールしたい
パッケージと同名のディレクトリがあると、
インストールしてくれずに黙って終了してしまう。
(たまにハマる)
90. © 2021 NTT DATA Corporation 90
Pandas をインストールする
注意
NumPy とか Pandas は C 言語部分のビ
ルドのために、crt*.o 等のスタートアップルー
チンが必要。(最初まっさらな環境でハマった
gcc とかインストールしておけば多分大丈夫
(雑
91. © 2021 NTT DATA Corporation 91
Pandas を使ってみる
Pandas を使ってみる
92. © 2021 NTT DATA Corporation 92
GraalPython を起動して、Pandas を使ってみる。(下線に黄色は入力した箇所
結果は以下の通り。
こちらも念のため、アクティベートしてから Python のバージョンを確認する。
普通に動く。が、インポートにやたらと時間がかかる。
(venv-21.1.0.r11) $ python
Python 3.8.5 (Fri Apr 16 11:46:53 UTC 2021)
[Graal, GraalVM CE, Java 11.0.11] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> import pandas as pd
>>> pd.Series([1, 2, 3])
0 1
1 2
2 3
dtype: int64
>>>
Pandas を使ってみる
93. © 2021 NTT DATA Corporation 93
と言うわけで、import にどれぐらい時間がかかってるか測ってみる。(下線に黄色は入力した箇所
結果は以下の通り。
こちらも念のため、アクティベートしてから Python のバージョンを確認する。
約 34 秒!(Amazon EC2 t3a.xlarge)
(venv-21.1.0.r11) $ python
Python 3.8.5 (Fri Apr 16 11:46:53 UTC 2021)
[Graal, GraalVM CE, Java 11.0.11] on linux
Type "help", "copyright", "credits" or "license" for more information.
>>> from datetime import datetime
>>> start = datetime.now(); import pandas as pd; print(datetime.now() - start)
0:00:33.831000
>>>
Pandas を使ってみる
94. © 2021 NTT DATA Corporation 94
Pandas を使ってみる
何でそんなにかかるの?
⇓
現時点で分かってるのは…
1. import は一発モノなので JIT がかから
ず遅い(JVM 実行自体の弱点
2. C 言語部分の実行が遅い(Sulong の
弱点
95. © 2021 NTT DATA Corporation 95
Pandas を使ってみる
C 言語部分の実行が遅いのは何で?
⇓
GraalPython では、C 言語部分の実行は、
実はネイティブな機械語では動いていない。
LLVM bitcode と言う中間コード的な物を、
インタプリタ実行している。
(これが Sulong)
96. © 2021 NTT DATA Corporation 96
Pandas を Java から使ってみる
Pandas を Java から使ってみる
97. © 2021 NTT DATA Corporation 97
Pandas を Java から使ってみる
Java から Python のモジュールを使う場合、特に仮想環境のモジュールを使う場合は、余分なコードや設定が
必要となる。
try (Context ctx = Context.newBuilder()
.option("python.NoUserSiteFlag", "true")
.option("python.SysPrefix", "venv-21.1.0.r11")
.allowAllAccess(true).build()) {
ctx.eval("python", "import site");
ctx.eval("python", "import pandas as pd");
ctx.eval("python", "print(pd.Series([1, 2, 3]))");
}
98. © 2021 NTT DATA Corporation 98
Pandas を Java から使ってみる
Java から Python のモジュールを使う場合、特に仮想環境のモジュールを使う場合は、余分なコードや設定が
必要となる。
try (Context ctx = Context.newBuilder()
.option("python.NoUserSiteFlag", "true")
.option("python.SysPrefix", "venv-21.1.0.r11")
.allowAllAccess(true).build()) {
ctx.eval("python", "import site");
ctx.eval("python", "import pandas as pd");
ctx.eval("python", "print(pd.Series([1, 2, 3]))");
}
Context.create() だと、
生成する Context のカスタマイズができないので、
ビルダー(Context.Builder)で生成する。
99. © 2021 NTT DATA Corporation 99
Pandas を Java から使ってみる
Java から Python のモジュールを使う場合、特に仮想環境のモジュールを使う場合は、余分なコードや設定が
必要となる。
try (Context ctx = Context.newBuilder()
.option("python.NoUserSiteFlag", "true")
.option("python.SysPrefix", "venv-21.1.0.r11")
.allowAllAccess(true).build()) {
ctx.eval("python", "import site");
ctx.eval("python", "import pandas as pd");
ctx.eval("python", "print(pd.Series([1, 2, 3]))");
}
"python.NoUserSiteFlag" を true にしないと、
ユーザ環境にインストールしたパッケージが参照できてしまうので、
指定しておく。(そうしないと仮想環境を作った意味が無くなっちゃう)
100. © 2021 NTT DATA Corporation 100
Pandas を Java から使ってみる
Java から Python のモジュールを使う場合、特に仮想環境のモジュールを使う場合は、余分なコードや設定が
必要となる。
try (Context ctx = Context.newBuilder()
.option("python.NoUserSiteFlag", "true")
.option("python.SysPrefix", "venv-21.1.0.r11")
.allowAllAccess(true).build()) {
ctx.eval("python", "import site");
ctx.eval("python", "import pandas as pd");
ctx.eval("python", "print(pd.Series([1, 2, 3]))");
}
"python.SysPrefix" に、仮想環境のディレクトリを
指定すると、仮想環境をアクティベートした時と同じ状態になる。
101. © 2021 NTT DATA Corporation 101
Pandas を Java から使ってみる
Java から Python のモジュールを使う場合、特に仮想環境のモジュールを使う場合は、余分なコードや設定が
必要となる。
try (Context ctx = Context.newBuilder()
.option("python.NoUserSiteFlag", "true")
.option("python.SysPrefix", "venv-21.1.0.r11")
.allowAllAccess(true).build()) {
ctx.eval("python", "import site");
ctx.eval("python", "import pandas as pd");
ctx.eval("python", "print(pd.Series([1, 2, 3]))");
}
デフォルトだと Python からファイルにアクセスできなくて
外部パッケージを使えないので、とりあえず何でも許可しておく。(雑
もちろん、もうちょっと細かく制御することもできる。
(Context.Builder の Javadoc を参照)
102. © 2021 NTT DATA Corporation 102
Pandas を Java から使ってみる
Java から Python のモジュールを使う場合、特に仮想環境のモジュールを使う場合は、余分なコードや設定が
必要となる。
try (Context ctx = Context.newBuilder()
.option("python.NoUserSiteFlag", "true")
.option("python.SysPrefix", "venv-21.1.0.r11")
.allowAllAccess(true).build()) {
ctx.eval("python", "import site");
ctx.eval("python", "import pandas as pd");
ctx.eval("python", "print(pd.Series([1, 2, 3]))");
}
生成した Context で "import site" をやっておかないと、
独自に導入したパッケージが見つからない。
103. © 2021 NTT DATA Corporation 103
Pandas を Java から使ってみる
実行方法はいたって普通。
約1分15秒!
$ javac HelloPandas.java
$ time java HelloPandas
0 1
1 2
2 3
dtype: int64
real 1m14.630s
user 4m25.879s
sys 0m4.518s
104. © 2021 NTT DATA Corporation 104
Pandas を Java から使ってみる
ちなみに、Context 生成時に指定していた option は、プログラム内で指定せずに実行時に
Java のシステムプロパティで指定することもできる。
$ java -Dpolyglot.python.NoUserSiteFlag=true
-Dpolyglot.python.SysPrefix=venv-21.1.0.r11
HelloPandas
0 1
1 2
2 3
dtype: int64
105. © 2021 NTT DATA Corporation 105
Pandas を Java から使ってみる
ちなみに、Context 生成時に指定していた option は、プログラム内で指定せずに実行時に
Java のシステムプロパティで指定することもできる。
$ java -Dpolyglot.python.NoUserSiteFlag=true
-Dpolyglot.python.SysPrefix=venv-21.1.0.r11
HelloPandas
0 1
1 2
2 3
dtype: int64
プロパティ名の先頭に "polyglot." が付いてる事に注意!
他のプロパティ名と被らないようにそうなっているらしい…
106. © 2021 NTT DATA Corporation
Spark に組み込んでみた
106
107. © 2021 NTT DATA Corporation 107
がんばって Spark に組み込んでみた
と言うわけで(?)、
これらを踏まえて Spark に組み込んで、
JVM 内で GraalPython が動くように
パッチを当てて動かしてみた
が、勘のいい人は既にお気づきかもしれないが、
結果はなかなか厳しいものに…
(勘のいいガキは嫌いだよ勘が悪くても分かる気がしないでもない…)
108. © 2021 NTT DATA Corporation 108
がんばって Spark に組み込んでみた
動かしてみた内容
みんな大好きマイクロベンチマーク
1. 1000万件のランダムなデータに対して、
UDF で単純加算してみた。
2. 普通の Python UDF と、Pandas UDF と言う、Pandas の
機能を使ってベクトル化した UDF を比較してみた。
3. JIT 効くかなと思って 8 回繰り返してみた。
109. © 2021 NTT DATA Corporation 109
がんばって Spark に組み込んでみた
補足:Pandas UDF について
• 普通の Python UDF は、レコード件数分呼ばれる。
つまり、1000 万レコードに適用すれば 1000 万回呼ばれる。
• Pandas UDF の場合、Pandas の機能を使うことによって、
引数や戻り値がベクトル化され、「バッチサイズ」毎に呼ばれる。
(引数や戻り値は pd.Series や pd.DataFrame になる)
これによって、1000 万レコードに適用しても、
1000 万÷「バッチサイズ(通常10000)」回しか呼ばれない。
つまり、Pandas UDF は速い。
110. © 2021 NTT DATA Corporation 110
がんばって Spark に組み込んでみた
00:00
00:30
01:00
01:30
02:00
02:30
初回 2回目 3回目 4回目 5回目 6回目 7回目 8回目
引数の型が double の場合
GraalPython
Python UDF
GraalPython
Pandas UDF
CPython
Python UDF
CPython
Pandas UDF
処理時間
(mm:ss)
実行回数
こっちが GraalPython 組み込んだヤツ
こっちが普通のヤツ
111. © 2021 NTT DATA Corporation 111
がんばって Spark に組み込んでみた
00:00
00:30
01:00
01:30
02:00
02:30
初回 2回目 3回目 4回目 5回目 6回目 7回目 8回目
引数の型が double の場合
GraalPython
Python UDF
GraalPython
Pandas UDF
CPython
Python UDF
CPython
Pandas UDF
処理時間
(mm:ss)
実行回数
こっちが GraalPython 組み込んだヤツ
こっちが普通のヤツ
_人人人人人人人人人人人人人人人人人_
> むしろ遅い!!!1! <
> 初回なんかはとてつもなく遅い! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
112. © 2021 NTT DATA Corporation 112
がんばって Spark に組み込んでみた
初回がとてつもなく遅い理由
⇓
みなさんお察しの通り、import が遅いから
113. © 2021 NTT DATA Corporation 113
がんばって Spark に組み込んでみた
でも 3 回目あたりから
いい線いってるんじゃね?
⇓
初回ヒドすぎてグラフがよく見えないから、
初回無視してちょっとグラフ拡大
114. © 2021 NTT DATA Corporation 114
がんばって Spark に組み込んでみた
00:00
00:05
00:10
00:15
00:20
00:25
00:30
初回 2回目 3回目 4回目 5回目 6回目 7回目 8回目
引数の型が double の場合(縦軸拡大)
GraalPython
Python UDF
GraalPython
Pandas UDF
CPython
Python UDF
CPython
Pandas UDF
処理時間
(mm:ss)
実行回数
こっちが GraalPython 組み込んだヤツ
こっちが普通のヤツ
115. © 2021 NTT DATA Corporation 115
がんばって Spark に組み込んでみた
00:00
00:05
00:10
00:15
00:20
00:25
00:30
初回 2回目 3回目 4回目 5回目 6回目 7回目 8回目
引数の型が double の場合(縦軸拡大)
GraalPython
Python UDF
GraalPython
Pandas UDF
CPython
Python UDF
CPython
Pandas UDF
処理時間
(mm:ss)
実行回数
こっちが GraalPython 組み込んだヤツ
こっちが普通のヤツ
_人人人人人人人人人人人人人_
> 何の成果も!! <
> 得られませんでした!! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
116. © 2021 NTT DATA Corporation 116
がんばって Spark に組み込んでみた
00:00
00:05
00:10
00:15
00:20
00:25
00:30
初回 2回目 3回目 4回目 5回目 6回目 7回目 8回目
引数の型が double の場合(縦軸拡大)
GraalPython
Python UDF
GraalPython
Pandas UDF
CPython
Python UDF
CPython
Pandas UDF
処理時間
(mm:ss)
実行回数
こっちが GraalPython 組み込んだヤツ
こっちが普通のヤツ
しかもよく見ると、GraalPython
組み込んだヤツだと、
速いハズの Pandas UDF の方が
普通の UDF より遅い…
117. © 2021 NTT DATA Corporation 117
がんばって Spark に組み込んでみた
Pandas UDF の方が遅い理由
⇓
お察しの通り、Pandas の実行が遅いから
Pandas は大部分が C 言語で書かれているから、
LLVM bitcode をインタプリタ実行してる…
118. © 2021 NTT DATA Corporation 118
がんばって Spark に組み込んでみた
イイワケ
Pandas UDF はこれでも最初よりは
だいぶ速くなったんです信じてください!
119. © 2021 NTT DATA Corporation 119
がんばって Spark に組み込んでみた
Pandas UDF をちょっとだけ速くできた理由
Java 側から計算対象データを UDF に渡し
た後で、Pandas がデータを内部形式に変換
するのに時間がかかっていた。
(変換処理が C 言語で書かれていたから)
120. © 2021 NTT DATA Corporation 120
がんばって Spark に組み込んでみた
Pandas UDF をちょっとだけ速くできた理由
Java 側から計算対象データを UDF に渡し
た後で、Pandas がデータを内部形式に変換
するのに時間がかかっていた。
(変換処理が C 言語で書かれていたから)
変換を Java 側でやっちゃえば
いんじゃね?(本末転倒感…
121. © 2021 NTT DATA Corporation 121
がんばって Spark に組み込んでみた
証拠
string の場合、Pandas の都合上、
Java 側で変換することができずに
当初の実装のままなので、
同じような処理を string でやってみた。
122. © 2021 NTT DATA Corporation 122
がんばって Spark に組み込んでみた
00:00
01:00
02:00
03:00
04:00
05:00
初回 2回目 3回目 4回目 5回目 6回目 7回目 8回目
引数の型が string の場合
GraalPython
Python UDF
GraalPython
Pandas UDF
CPython
Python UDF
CPython
Pandas UDF
処理時間
(mm:ss)
実行回数
こっちが GraalPython 組み込んだヤツ
こっちが普通のヤツ
123. © 2021 NTT DATA Corporation 123
がんばって Spark に組み込んでみた
00:00
01:00
02:00
03:00
04:00
05:00
初回 2回目 3回目 4回目 5回目 6回目 7回目 8回目
引数の型が string の場合
GraalPython
Python UDF
GraalPython
Pandas UDF
CPython
Python UDF
CPython
Pandas UDF
処理時間
(mm:ss)
実行回数
こっちが GraalPython 組み込んだヤツ
こっちが普通のヤツ
_人人人人人人人人_
> 右肩上がり! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^ ̄
124. © 2021 NTT DATA Corporation 124
がんばって Spark に組み込んでみた
何でそんなことになってるの?
⇓
GC ログを見ると分かる…
125. © 2021 NTT DATA Corporation 125
GraalPython の Pandas UDF で string 型を使用した際の GC log
黒い縦棒がGC処理に
費やした時間。
後半は黒く塗りつぶさ
れており、ほとんどGCに
処理時間を奪われてい
ることが分かる。
紫の折れ線がOld領域
のオブジェクト量。
急峻に増加しており、
終盤にはほぼ上限に張
り付いており、GCが発
生しても減っていないこ
とがわかる。
縦軸
折れ線グラフ:Old領域のオブジェクト量(最大目盛8GB)
棒グラフ:GC処理時間(最大目盛23秒)
横軸:アプリケーション起動からの経過時間(最大目盛30分)
126. © 2021 NTT DATA Corporation 126
GraalPython の Pandas UDF で string 型を使用した際の GC log
黒い縦棒がGC処理に
費やした時間。
後半は黒く塗りつぶさ
れており、ほとんどGCに
処理時間を奪われてい
ることが分かる。
紫の折れ線がOld領域
のオブジェクト量。
急峻に増加しており、
終盤にはほぼ上限に張
り付いており、GCが発
生しても減っていないこ
とがわかる。
縦軸
折れ線グラフ:Old領域のオブジェクト量(最大目盛8GB)
棒グラフ:GC処理時間(最大目盛23秒)
横軸:アプリケーション起動からの経過時間(最大目盛30分)
_人人人人人人人人人人人人人_
> 圧倒的メモリリーク感! <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
127. © 2021 NTT DATA Corporation 127
がんばって Spark に組み込んでみた
ちな、対処した方はこんな感じ
128. © 2021 NTT DATA Corporation 128
GraalPython の Pandas UDF で double 型を使用した際の GC log
紫の折れ線がOld領域
のオブジェクト量。
string型のような急峻
な増加は見られないが、
緩やかな上昇傾向にあ
る。
黒い縦棒がGC処理に
費やした時間。
細すぎて見えず、処理
時間にはほぼ影響のな
いレベルであることが分
かる。
横軸:アプリケーション起動からの経過時間(最大目盛3分半)
縦軸
折れ線グラフ:Old領域のオブジェクト量(最大目盛8GB)
棒グラフ:GC処理時間(最大目盛0.33秒)
129. © 2021 NTT DATA Corporation 129
GraalPython の Pandas UDF で double 型を使用した際の GC log
紫の折れ線がOld領域
のオブジェクト量。
string型のような急峻
な増加は見られないが、
緩やかな上昇傾向にあ
る。
黒い縦棒がGC処理に
費やした時間。
細すぎて見えず、処理
時間にはほぼ影響のな
いレベルであることが分
かる。
横軸:アプリケーション起動からの経過時間(最大目盛3分半)
縦軸
折れ線グラフ:Old領域のオブジェクト量(最大目盛8GB)
棒グラフ:GC処理時間(最大目盛0.33秒)
_人人人人人人人人人人人人_
> ちょっとメモリリーク <
> してるかも… <
 ̄Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^Y^ ̄
130. © 2021 NTT DATA Corporation 130
結論
結論
少なくとも、現状では PySpark に
GraalPython を組み込んで
常用することは厳しい
132. © 2021 NTT DATA Corporation 132
この取り組みをしていた時(21.0)からの変化
• SSL 使えません
• Socket 使えません
• Java の byte[] を Python の bytes に変換で
きません
• マルチスレッド関連バギーです
この取り組みをしていた時(21.0)からの変化
• SSL 使えません ⇒ 使えるようになりました
• Socket 使えません ⇒ 使えるようになりました
• Java の byte[] を Python の bytes に変換で
きません ⇒ 出来るようになりました
• マルチスレッド関連バギーです ⇒ GIL が導入され
たのでマトモに使えるようになった…かも…
結論
133. © 2021 NTT DATA Corporation 133
結論
GraalPython はまだまだ育ちざかり!
(まだ Experimental です)
これからも GraalVM/GraalPython は
どんどん良くなる!
(多分…)
134. © 2021 NTT DATA Corporation 134
結論
みんなで
GraalVM/GraalPython を
使おう!
(巻き添え)
135. © 2021 NTT DATA Corporation
本資料に記載されている会社名、商品名、又はサービス名は、各社の登録商標又は商標です。