C++製の経路探索プログラムをARMに対応させた話
こんにちは、果汁です。
ナビタイムジャパンの研究開発部門にて、主に経路探索エンジンにおけるサーバーサイドの機能改善に取り組んでいます。
今回は当社の経路探索エンジンとそれを扱うサーバープログラム(以下エンジンサーバーと呼称します)をx86_64からARMに対応させ、Gravitonベースのインスタンス上で稼働させた話をさせていただきます。
主に体験談を交えたARM化の対応内容と、実際のパフォーマンス結果を展開します。
今回のお話がGravitonの利用を検討している方の参考になれば幸いです。
ARM化の背景
エンジンサーバーを取り巻く構成はx86_64のアーキテクチャ向けに構築されており、c5、m6などのEC2インスタンス上で稼働させています。
サービスによっては、エンジンサーバー用のインスタンスを相当数稼働させるものもあるため、コスト削減は大きな課題となっていました。
コスト削減の解決策として、Gravitonというプロセッサを搭載したインスタンスが社内で注目を浴びました。
こちらはアーキテクチャにARMを採用している点が特徴です。
Gravitonベースのインスタンスはx86_64系と比較し、コスト(金額)・性能において優れているようです。
特にインスタンスのコスト削減の効果は大きく、第2世代であるGraviton2では約20%、最新の第3世代であるGraviton3では約15%の効果がありそうでした。(2023/06 の情報です。最新はこちらをご確認ください。)
インスタンスコストが安くなるだけでなく処理も速くなるなんて、魅力を感じずにはいられません!
社内でもJavaなどのアーキテクチャ非依存なサーバーは既にGraviton上で稼働を始めており、コスト削減を実現していました。
享受できるメリットが大きいと判断し、エンジンサーバーでもARM化を進めることとなりました。
対応内容
ARM化のために行った対応を簡単にお話しします。
といいましても、実施することは基本的にシステムをARM環境下で構築し直すことになります。今回のケースで言えば、エンジンサーバーとそこから利用されるライブラリのことを指します。
本記事のタイトルにもありますが、エンジンサーバーとその依存ライブラリはC++製です。
つまり、コンパイル後の成果物はアーキテクチャに依存した作りとなってしまうため、ARM環境で動作させるにはARM環境下での構築が必要になります。
「じゃあ全ライブラリをリビルドすればいいだけ。な~んだ、大したことないのでは?」と対応開始時の私は思っていました。
しかし、言うは易く行うは難しとはまさにこのこと。自分の思慮の浅さを後悔することになります。
というのも、エンジンサーバーは社内においてもトップクラスに巨大なシステムであり、利用しているライブラリの数は社内謹製からサードパーティー製を含め70以上に上ります。
予想を超える多さに目がくらみました。。
ましてや、これらをx86_64と動作差分が無いように構築しなければならないのですから、ARM化は長く険しい道のりだということを痛感しました。
それでは、ARM化で特に躓いたポイントを2つご紹介します。
他にも対応時に詰まった箇所はあるのですが割愛します。
OSの移行(コンパイラのアップデート)
ARM化するにはただビルドをかければいいように思えますが、いくらインスタンスの性能が良いとの触れ込みであっても、本当にパフォーマンスが改善するかは分かりません。
そこで、Gravitonの性能を引き出すためにプロセッサに合わせた最適化を実施する必要がありました。
AWSによるC++のガイドによりますと、コンパイラのバージョンは少なくともGCC-9以上が必要とのことです。
ここで大変なことに気づきます。
ビルドにはDockerを用いていますが、エンジンサーバーとその依存ライブラリではイメージに CentOS7、コンパイラに GCC-4.8 を使用していました。
つまり、諸々のライブラリでOSの移行とコンパイラのアップデートが必要ということになります。
対応の規模の大きさに再度目がくらみます。。
対応時はまだ AmazonLinux2023 が正式リリースされていなかったこともあり、今回はOSに Amazon Linux2、コンパイラに GCC-10 を採用しました。
(ビルドエラー・動作不良など盛りだくさんでしたが、これはまた別のお話
です。)
ともあれ、この対応による恩恵は絶大です。
CentOS7 は 2024/06にEOL となるため、先んじて移行を進められました。また、glibc と GCC のバージョンが上がることから、C++のバージョンも上げることが可能になりました。
開発者にとってかなり嬉しいアップデートができたと思います。
外製ライブラリのアップデート
エンジンサーバーでは自社製だけでなく外製のライブラリもいくつか使用しています。
ここで注意しなければならないことは、それらがARM版も提供されているかどうかです。また、提供していたとしても比較的最新のバージョンに限る場合があります。
幸いにも、エンジンサーバーで使用している物はバージョンアップすることでARM版を利用することができました。
その一例をご紹介します。
v7.8にアップデート
v3にアップデート
v8にアップデート
(MySQLをv8に更新した際、DBの接続時にパフォーマンスが悪化する現象が発生しました。こちらの調査・修正に手こずることとなりましたが、これもまた別のお話です。)
ともあれ(2回目)、古いバージョンを使い続けていたライブラリはこのタイミングで更新をかけられたため、大きな改善となりました。
パフォーマンス測定
やっとのことでARM版エンジンサーバーが出来上がりました!
それでは、Graviton上で動作させたときのパフォーマンスを確認してみます。
計測対象は以下3つのインスタンスです。エンジンサーバーではインスタンスタイプにc5を使用することが多いため、こちらをGravitonの比較対象とします。
c5.2xlarge
vCPU:8(物理コア:4)
c6g.2xlarge(Graviton2)
vCPU:8(物理コア:8)
c7g.2xlarge(Graviton3)
vCPU:8(物理コア:8)
計測には『乗換NAVITIME』で使用されるような経路探索リクエストを使用しました。
また、エンジンサーバーには同時に複数の経路探索リクエストが流れてきますので、同時リクエスト数を変えながら確認を行いました。
結果になります。
特に注目すべきは3並列時の結果で、c7gはc5よりも約23%の速度改善が確認できました。
また、c5では並列度が上がるにつれパフォーマンスが大きく悪化する傾向がありますが、c7gはほぼ悪化していません。
結果からの予測にすぎませんが、c5ではSMT(同時マルチスレッディング)がうまく機能することにより、並列リクエスト数が少ない状態では高いパフォーマンスをマーク。しかし負荷が上がるにつれてCPUに待ちが発生しやすくなり、パフォーマンスの低下に繋がったのかもしれません。
反対にGravitonインスタンスでは物理コアが豊富なため、並列度にあまり影響を受けず、高いパフォーマンスをキープできたのではないかと思います。
また、c6gとc7g間でも結果に大きく差が出ているため、Gravition3が優秀なことがよく分かりました。
c6gがc5よりもパフォーマンスが悪い結果は意外でしたが、並列数が増えるにつれ差が縮まっているため、もっと負荷が高い状態であればc6gのほうが良い結果を出せるのだと思います。
続いてc5、c7gのCPU使用率も確認してみます。
c7gが約10%改善した結果となりました。
c7gであればスループットが高いだけでなくサーバーの負荷も軽い。さらにはインスタンスのコストも安い。いいことづくめです!
今回はARM用拡張命令セットであるSVEを使用した実装等は行いませんでしたが、十分な高速化が得られました。
まとめ
Graviton導入の個人的なメリット・デメリットを記載します。
メリット
インスタンスコストの低下
パフォーマンスの改善
OSの更新等が必要になる場合があり、システムの見直し
をさせられるができる
デメリット
システム構成によっては対応コストが大きい
対応コストの大きさをデメリットとして上げましたが、こちらはケースバイケースだと思います。
今回対応したエンジンサーバーはC++製と、アーキテクチャに依存するだけでなく大きなシステムだからこそ、無視できないデメリットとなりました。
Javaなどのアーキテクチャ非依存なシステムや構成が軽めなものであれば、比較的簡単に導入できるのではないかと感じています。
また、AWSから今年の始めにGraviton移行をサポートするソースコード分析ツールなるものが出ているようです。
対応コストが懸念の場合、まずはこちらを試してみてもいいかもしれません。
終わりに
ARM化の対応開始が 2021/04 のことでしたので、既に2年以上が経過しました。紆余曲折ありましたが、ARM化がやっと形になりほっと胸をなでおろせそうです。
対応内容に関しては特に躓いた2点をご紹介しました。他にも詰まった箇所はいくつかあったのですが、内容がかなり細かくなってしまいそうなため割愛しています。
Gravitonでのエンジンサーバーの稼働はまだ一部のサービスのみですので、
さらにたくさんのサービスにも展開できるように今後も対応を進めていきます。
最後までお読みいただきありがとうございました。