AWS LambdaでJavaとNode.jsとGoの簡易ベンチマークをしてみた。
あけましておめでとうございます!
現場でいつも締め切りに追われデスマーチ化している皆様方におかれましては、年賀状がデスマーチ化していたのではないかと憂慮しておりますが、いかがお過ごしですか。
エンジニアの鑑みたいな僕としましては、年始の挨拶はSNSでサクッと済ませ、年末年始は紅白など見ながらAWS Lambdaのソースコードを書いていました。
ということで、Lambda。
Lambdaを書く時に最初に悩むのは、どの言語を選択するか、なのです。
まず手を出すのは、サクッと書けるNode.js。
ただNode.jsの非同期なプログラミングになかなか馴染めず、わからん殺しをされてうんざりしていました。みんなどうしているのよとtwitterで問いかけたところ [twitter:@making] からPromiseを使うべしと教わり、なるほど確かにこれは便利ですナと思いつつも、これが標準で使えないぐらいLambdaのNode.jsが古いところにまたうんざりしました。
では手に馴染んでるJavaを使ってみたらどうかと思ったら、メモリイーターだし、なんとなくパフォーマンスが悪い感じでした。詳しいことは後ほどベンチマーク結果で明らかになるわけですが。
それなら消去法で残ったPythonなのですが、僕マジPython触ったことないレベルであり、これは若者たちに任せることにしているので、Pythonも選択肢から消えて。
本来、Lambdaみたいな用途にはGoが向いているはずなのだけど、いつ使えるようになるんだろうなぁなどと思って調べてみたら、Node.jsからGoを呼び出すというテクを見つけて、こりゃいいやとなりました。
http://blog.0x82.com/2014/11/24/aws-lambda-functions-in-go/
ただGoを呼ぶにしてもNode.jsを経由するためのオーバーヘッドはあるわけで、じゃぁ実際にどれぐらいパフォーマンス影響があるのか、調べてみることにしました。
処理の中身
簡単に試したいだけだったので、数行で済む程度のごく簡単な処理を書いてベンチマークすることにしました。
1. 引数で渡されたJSONをパースして、中身を表示する
2. DynamoDBから1件のデータを取得する
当初は1だけだったのですが、あまり性能に差が出なかったので2を追加した感じです。そんな経緯のベンチマークなので、実装も雑ですが、とりあえずソースをGitHubに置いておきました。
https://github.com/cero-t/lambda-benchmark
実行結果
回数 | Java(1) | Java(2) | Node.js | Go |
---|---|---|---|---|
1回目 | 21800 | 6700 | 900 | 600 |
2回目 | 1300 | 7300 | 800 | 500 |
3回目 | 19000 | 500 | 200 | 500 |
4回目 | 1000 | 1300 | 200 | 400 |
5回目 | 500 | 200 | 400 | 500 |
6回目 | 400 | 100 | 200 | 500 |
7回目 | 400 | 300 | 400 | 500 |
時間はいずれもミリ秒のBilled duration。
Java(1) : メモリ192MB。処理中にAmazonDynamoDBClientを初期化
Java(2) : メモリ192MB。処理前に(コンストラクタで)AmazonDynamoDBClientを初期化
Node.js : メモリ128MB
Go : メモリ128MB
メモリの実使用量は
Java : 68MB
Node.js : 29MB
Go : 15MB
でした。
考察など
正味の話、Lambdaで行っている処理などほとんどないので、性能的には大差ないかと思っていたのですが、思ったより特性が出ました。これは処理時間というよりは、関連ライブラリのロード時間な気がしますね。
Javaは最初の数回が20秒、実装を改善しても7秒とかなり遅かったのですが、ウォーミングアップが終わった後は200msぐらいまで早くなりました。呼ばれる頻度が高い処理であれば良いのでしょうけど、たまに呼ばれるような処理では、この特性はネックになる気がします。
ちなみにJavaだけは192MBで計測しましたが、最初にメモリ128MBで試したところ、30秒ぐらいで処理が強制的に中断されたり、60秒でタイムアウトするなど、計測になりませんでした。こういう所を見ても、Javaを使う時にはメモリを多め(CPU性能はメモリと比例して向上)にしなくてはいけない感じでした。
Node.jsも少しはウォーミングアップで性能が変わりますが、最初から性能は良い感じです。
Goを使った場合は性能が安定しており、ウォーミングアップしても性能が変わりません。メモリ使用量が少ないのも良いですね。Node.jsから呼び出す際のオーバーヘッドがあるせいか、性能的にはウォーミングアップ後のJavaやNode.jsに一歩劣る感じでした。
なお、Pythonの実装をしてくれる人がいらっしゃれば、プルリクしていただければ、データを追加したいと思います。