こんにちは、@kojiisdです。
この間AWS Summit Tokyoに参加してきたのですが、皆一様に「AWS Lambda」を、
これからのサーバレスアーキテクチャを支える技術として紹介していましたね。
資料でも言葉でも多分に見聞きしており、軽いゲシュタルト崩壊を起こしている今日この頃、
皆さんはいかがお過ごしでしょうか。
さて、今回はAWS Lambdaです。
AWS Lambdaの処理はJavaやNode.js、Pythonなどの言語で記述することができますが、その性能差がどの程度あるのか?測ってみました。
構成
今回の構成は次の様なシンプルなものにしています。
[計測対象(言語)]
[計測対象(カテゴリ)]
- 処理速度
- 使用メモリ
[Lambdaでの処理内容]
図で示すとこんな感じです。
[計測条件]
- 次に示すサンプルコードをそれぞれLambdaで定義して実行する。
- クライアントにはApache JMeterを利用。
- 各言語とも200回計測し、それぞれ100、90、80、50パーセンタイル値、最小値(もっとも速い/軽い)を取る。
- メモリは192MB指定。(Javaが192MBでないと動かないので、他も合わせてスペックアップ)
[サンプルコード]
各言語のソースコードは次の通りです。
[Python]
#!/usr/bin/python2.7 # -*- coding:utf-8 -*- # Python import boto3 import logging from boto3.dynamodb.conditions import Key logger = logging.getLogger() logger.setLevel(logging.INFO) dynamodb = boto3.resource('dynamodb') table = dynamodb.Table('mytest') def handle_request(event, context): response = table.query( KeyConditionExpression=Key('id').eq('test') ) logger.info(response) logger.info("SUCCESS")
[Node.js]
// Node.js var AWS = require('aws-sdk'); var dynamodb = new AWS.DynamoDB(); exports.handler = function(event, context) { var params = { Key: {id: {"S": "test"}}, TableName: "mytest" }; dynamodb.getItem(params, function (err, data) { if (err) { console.log(err); context.fail('Failed'); } else { console.log(data); context.succeed('Succeeded'); } }); };
[Java]
package jp.co.acroquest.aws.bench; import com.amazonaws.auth.EnvironmentVariableCredentialsProvider; import com.amazonaws.regions.Region; import com.amazonaws.regions.Regions; import com.amazonaws.services.dynamodbv2.AmazonDynamoDBClient; import com.amazonaws.services.dynamodbv2.document.DynamoDB; import com.amazonaws.services.dynamodbv2.document.Item; import com.amazonaws.services.dynamodbv2.document.Table; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; import com.amazonaws.services.lambda.runtime.RequestHandler; public class DynamoDBBench implements RequestHandler<Object, Object> { AmazonDynamoDBClient client; Table table; DynamoDB dynamoDB; public DynamoDBBench() { client = new AmazonDynamoDBClient( new EnvironmentVariableCredentialsProvider()); client.setRegion(Region.getRegion(Regions.AP_NORTHEAST_1)); dynamoDB = new DynamoDB(client); table = dynamoDB.getTable("mytest"); } public String handleRequest(Object input, Context context) { LambdaLogger logger = context.getLogger(); Item item = table.getItem("id", "test"); if (item == null) { logger.log("Item is null."); return "Failure"; } logger.log("Item obtained: " + item); return "Success"; } }
結果
200回計測した結果の値を取ってみたところ、このようになりました。
[100パーセンタイル(最大)]
[90パーセンタイル]
[80パーセンタイル]
[50パーセンタイル]
[最小]
- Javaは初回アクセスからしばらくの間、オーバーヘッドが大きいことがわかります。
- 使用メモリは予想通りJavaが一番大きく、PythonとNode.jsはほぼ同程度の使用率であることがわかります。
- 実行回数を重ねるごとに、Javaのパフォーマンスが上がっていることがわかります。
- 処理時間が最小の結果では、Javaが他の言語に追いついていることがわかります。
今回の結果の生データは次のリンクに置いておきました。
taste-of-tech-topics/measure_result.xlsx at master · kojiisd/taste-of-tech-topics · GitHub
Java性能のパフォーマンス向上における確認実験
ちなみに回数を重ねるごとにJavaの性能が大きく上がりはするものの、実行開始時の処理時間がかかっている部分をもう少し詳しく知るために、
AWS Lambda上でフィボナッチ数列を求める計算を簡易的にですが各言語毎に調べてみました。今回は40番目の数値を求める計算をしました。
たった5回の計測でしたが、次の様な平均値が取れました。Javaが他言語に比べてオーバーヘッドなど関係なく圧倒的に早いことがわかります。
Pythonは2分のタイムアウト設定を設けてもタイムアウトしてしまったので、今回はそれ以上は計測していません。
Java | Node.js | Python | |
平均処理時間[ms] | 4308.3 | 12387.0 | 2分以上 |
個人的には(シンプルな実装なので最適とは言えないが)V8のJavaScriptエンジンを搭載しているNode.jsの3倍もの性能をJavaが出せたのは意外でした。
以上の結果から、今回のDynamoDBにアクセスして値を取得してくる処理に関しては、Javaライブラリの読み込みに時間がかかってしまっており、実行を繰り返す中で最適化がされていったため、初動は遅いが繰り返すたびにJavaの性能が上がってきた、と推測することはできそうです。
今回のフィボナッチ数列計算を試した際のコードは以下になります。
[Java]
package jp.co.acroquest.aws.bench; import com.amazonaws.services.lambda.runtime.Context; import com.amazonaws.services.lambda.runtime.LambdaLogger; public class Fibonacci { private static final int INDEX = 40; LambdaLogger logger; public String handleRequest(Object input, Context context) { logger = context.getLogger(); long calcResult; calcResult = calcFib(INDEX); logger.log("result: " + calcResult + "\n"); return "Success"; } public long calcFib(int number) { if (number == 1 || number == 2) return 1; return calcFib(number - 2) + calcFib(number - 1); } }
[Node.js]
exports.handler = (event, context, callback) => { var result = calcFib(40); context.succeed('result: ' + result) context.succeed('Success'); }; function calcFib(num) { if(num == 1 || num == 2) { return 1; } return calcFib(num - 1) + calcFib(num - 2); }
[Python]
import logging def lambda_handler(event, context): result = calcFib(40) logging.info('result: ' + result) return 'Success' def calcFib(num): if num <= 2: return 1 else : return calcFib(num - 1) + calcFib(num - 2)
言語毎の実行ログは以下になります。
[Java] REPORT RequestId: b0afcdb2-5715-11e6-8d3c-537f664ac4cd Duration: 4626.36 ms Billed Duration: 4700 ms Memory Size: 128 MB Max Memory Used: 34 MB REPORT RequestId: b77533b9-5715-11e6-8d3c-537f664ac4cd Duration: 4211.39 ms Billed Duration: 4300 ms Memory Size: 128 MB Max Memory Used: 34 MB REPORT RequestId: bc0c9611-5715-11e6-8d14-ed04d7d6b375 Duration: 4239.18 ms Billed Duration: 4300 ms Memory Size: 128 MB Max Memory Used: 34 MB REPORT RequestId: c2ecb0b3-5715-11e6-ac28-fd403fb9707b Duration: 4229.15 ms Billed Duration: 4300 ms Memory Size: 128 MB Max Memory Used: 34 MB REPORT RequestId: c8944a4a-5715-11e6-b5a7-bb8b3c4b93fe Duration: 4235.18 ms Billed Duration: 4300 ms Memory Size: 128 MB Max Memory Used: 34 MB [Node.js] REPORT RequestId: e3125256-5714-11e6-a4f7-85ff9fd7c6c7 Duration: 12458.36 ms Billed Duration: 12500 ms Memory Size: 128 MB Max Memory Used: 16 MB REPORT RequestId: 04db011d-5715-11e6-b7c7-15170a43b8ce Duration: 12367.85 ms Billed Duration: 12400 ms Memory Size: 128 MB Max Memory Used: 16 MB REPORT RequestId: 72e8fccb-5715-11e6-9523-3f458c891180 Duration: 12367.47 ms Billed Duration: 12400 ms Memory Size: 128 MB Max Memory Used: 16 MB REPORT RequestId: 7c31b1ac-5715-11e6-8ff0-2d80e2cd4b43 Duration: 12374.82 ms Billed Duration: 12400 ms Memory Size: 128 MB Max Memory Used: 16 MB REPORT RequestId: 842b529d-5715-11e6-ad82-7103149e1efd Duration: 12366.68 ms Billed Duration: 12400 ms Memory Size: 128 MB Max Memory Used: 16 MB [Python] REPORT RequestId: 474c2f4b-571a-11e6-8a89-4bca1cc5593f Duration: 120000.15 ms Billed Duration: 120000 ms Memory Size: 128 MB Max Memory Used: 15 MB 2016-07-31T12:30:25.971Z 474c2f4b-571a-11e6-8a89-4bca1cc5593f Task timed out after 120.00 seconds
考察
今までの実験を踏まえると、AWS Lambdaで言語を選択する際には、次のようなことを考えるとよいのかと思いました。
- 継続的にアクセスされず、かつCPUもあまり使わない処理であれば、Pythonが速い。
- 継続的にアクセスされ、かつCPUをあまり使わない処理であれば、どの言語も大差ない。
- CPUを大量に使うのであれば、Javaが速い。
実際には上記に加えて書き慣れている言語であるかなど開発者側のスキルなども多分に関係すると思いますが、一旦結果のみから判断しようとすると、こうなるのかな、という感じです。
今回はシーケンシャルかつシングルスレッドで処理されるプログラムで実験をしましたが、複数同時リクエストの場合や、内部で複数スレッドを使って処理をするような場合での性能比較も、どこかで実験してみたいと思います。
それでは!
Acroquest Technologyでは、キャリア採用を行っています。
- 日頃勉強している成果を、AWS、Hadoop、Storm、NoSQL、Elasticsearch、SpringBoot、HTML5/CSS3/JavaScriptといった最新の技術を使ったプロジェクトで発揮したい。
- 社会貢献性の高いプロジェクトに提案からリリースまで携わりたい。
- 書籍・雑誌等の執筆や対外的な勉強会の開催を通した技術の発信や、社内勉強会での技術情報共有により、技術的に成長したい。
- OSSの開発に携わりたい。
少しでも上記に興味を持たれた方は、是非以下のページをご覧ください。
データ分析で国内に新規市場を生み出す新サービス開発者WANTED! - Acroquest Technology株式会社の新卒・インターンシップ - Wantedlywww.wantedly.com