[小ネタ] GoとRustでLambdaを比較してみた
はじめに
こんにちは!株式会社ブロードエッジ・ウェアリンク CTOの高丸です。
今回は、Qiita Advent Calendar 2024の21日目の記事です。
今回も小ネタではありますが、技術を楽しく実験してみたというお話です。弊社のワインのマッチ度計算ロジックにおいて、その値を返すAPIをGoやRustを使って既存のAPIと比較してみたという話です。
弊社のマッチ度計算
wine@ONLINESTORE(ワインアットオンラインストア)では、ユーザーの好みの味わいタイプとワインの特徴を照らし合わせ、そのマッチ度を数値化して提供しています。ユーザーは味わいタイプだけでなく、マッチ度をもとにさらに自分にあったワインを探すことができます。
我々は、このマッチ度計算のロジックをAWS Lambda上でAPIとして提供しており、現在はPythonで実装しています。様々なサービスから利用できるよう、APIとして切り出していました。
ところが、1日目の記事で紹介したデザインリプレイスプロジェクトを進める中で、一つの課題が浮き彫りになりました。
それは、複数のワインのマッチ度を一度に計算する際のパフォーマンスです。おすすめワインの表示数を増やすという要件に対して、現行のPython実装がボトルネックとなってしまっていました。
単なる実験的な取り組みではありますが、今回はGoとRustという二つの言語でマッチ度計算を実装してみることにしました。
Go? Rust?
この検証は、実は我々のチームメンバーの一人がGo経験者であり、Goでの実装を始めてくれたことがきっかけでした。
現行のPythonによる実装を見直してみると、マッチ度計算のロジック自体は複雑ではないものの、DBの接続やメモリ使用量を考慮すると、より効率的な実装の余地があることが見えました。
例えば、現行実装ではSQL Alchemyを使用してORMでデータを扱っていますが、大量のデータを処理する際にオブジェクト化のオーバーヘッドが発生していました。また、マッチ度の計算以外にも様々な処理が含まれており、純粋な計算処理以外の部分で時間を消費していることも分かりました。
そこで我々は、AWS Lambdaでカスタムランタイムを使用すれば、GoやRustでも実行可能であることに着目しました。両方の言語で実装してみることで、より良いパフォーマンスが得られるのではないかと考えたのです。
いきなり結果ですが、Go/RustともにPythonの約10倍の速度という結果が出ました。
おそらく計算量が多くはないため、Go/Rustでそこまで差が出なかったのかと思います。
また、ウォームアップにかかる時間は、わずかながらGoの方が速かった印象です。
RustをLambdaで使った印象
今回の検証で、私は初めてRustをLambdaにデプロイする経験をしましたが、Cargo Lambdaというツールの使用感がものすごく体感が良かったです。
通常、Lambdaへの自前デプロイでは、コードをzip化してアップロードするという手順を踏む必要があります。
あるいは、Serverless Application Model(SAM)を使用する方法もありますが、これはLambda以外のAWSリソースも含めた包括的なツールであるため、Lambdaだけを扱う場合には設定ファイルやテンプレートファイルの管理が些か過剰に感じることがあります。
その点、Cargo lambdaは本当にちょうど良い塩梅でした。
デプロイ作業が非常にスムーズなだけでなく、ローカルでのLambda環境の再現機能も備えているので、ローカルでの動作確認から本番環境へのデプロイまでシームレスに進めることができ、「ローカルでは動いたのに本番では動かない」というよくある問題に悩まされることもありませんでした。
さいごに
完全に同じ条件での比較ではないですが、言語を変えるだけでパフォーマンス改善できる幅が大きいことに気づけたので、我々は新しい技術にもチャレンジしていく必要があります。
Discussion