ベンチマークの採取は昔から度々やってますが、オンプレ時代と比べるとクラウドではより効率的に試行できるようになりました。
聞けば当然のような内容ではあるかもですが、一度整理整頓してみるのもタメになるかと思い、ベンチ付近でマーキングする画像を用意してみた次第です。
目次
短くと心がけた結果、長くなったので。概要
ベンチマークの目的・課題・全体の流れと整理していき、どのようにアプローチしていくのが最適か、ということを考察してきます。ベンチマークには色々あります。Network, Hardware, Middleware, Software, Application ……など調べたい単位があるわけですが、ここではベンチマークの処理そのものは既に存在するものとします。具体的にはコマンドラインを実行すれば、結果が標準出力で返される仕組みがある、ような前提です。
で、その試行パターンが複数存在したり、実行時間がそれなりに長いとしたら、リソースやデータの取り扱いをどうすれば無駄なく安定して完遂できるでしょうか。という話。
目的
ベンチマークは1つ実行して結果を得ても、それはただの数値でしかありません。2つ以上の条件下における結果を比較することで、優劣を確認することが目的です。シンプルには、パラメータの調整であったり、異なる環境において、復数の結果を比較して考察したり、性能上限を見極めます。
ユーザに提供するようなアプリケーションなどの場合は、一定以上のパフォーマンスを満たすことを目指して、リファクタリングを重ねて納得行くまで試行することもあり、これも条件の変化と言えるでしょう。
これらについてひとつ大まかに言えるのは、結果について語る上で、ベンチマークの条件数や試行回数が少ないほど、信憑性や確信度は薄いということです。もちろん、多ければ良いというわけではなく、それ以上は無駄と判断できる境界はあるのですが、一定以上の条件数や多角的な視野なしに結果を確信することは、逆に危険になりうる代物でもあります。
ベンチマークで堅実に目的を達成するためには、それなりの知識や経験が重要になってくるのですが、それら練度がどの程度であろうと必ず登場するのが『試行回数』『再試行』です。以降、これを軸の1つとして考えていきます。
課題
それなりの量のベンチマーク試験をしてみると、どうにもイラついてくる場面に出くわします。試験は本質を捉える重要な作業ですが、実質的な成果そのものが進捗するわけではないので、サクッと終わらせたい心境は常にあります。しかし、然うは問屋が卸さないのはどういう時かというと……
- 調整による再試行
-
想定外のデータ傾向・美しくないデータ変化
条件の見直し、修正からの再試行
-
データ不足・確信不足
さらに先の採取、異なる軸の検討
- エラーによる再試行
-
プログラム・エラー や 想定外の事象/返り値
試験外の自動化プログラム修正など
-
リソースのキャパシティ・オーバー
CPUの検証でストレージ制限、
アプリのスループット計測でネットワーク・トラフィック上限など
- 長時間による再試行
-
時間未定
進捗出力がない、数十分以上音沙汰なし
-
規模調整
序盤で本番想定の規模が必要か
準備も計測時間も長くなりすぎていないか
-
タイムアウト
プログラムに潜むデフォルト値、想定外の長い処理
-
その他
T系インスタンスのCPUバースト、スポットインスタンスの強制終了
- 汎用性の低さ
-
手動
最初は手動で実行、いつの間にか何度も実行と結果のコピペを繰り返す己
ワンミスあればまた同じ量のやり直し
-
別件
似た試験の追加、別環境での試験、また同じ量をカチカチ
指針
思い出せたイラつきというか課題を挙げてみましたが、これらから得た反省、翻って指針をまとめると意外とシンプルに。言うまでもなく、結果から目的となる結論を得られる上で、という条件付きです。
指針を達成できるほどに、体力と時間に余裕ができます。余裕ができれば、より多くの試験をすることもできますが、柔軟な思考をもってより多角的な試験にトライしたり、結果の整理整頓や考察に時間を割けるなど、一段上を望めるかもしれません。
手順
中規模以上のベンチマーク採取を行うとき、そこそこ細かく分割して手順を整理すると、だいたいこのような流れになります。( )でくくったところは、不要な場合アリです。- 準備
- (パッケージ整理)
- (インストール判断)
- インストール実行
- スクリプト配置
- イメージ保存
- 試験
- リソース選定
- (分割)
- 起動
- 実行
- 結果データ保存
- 集計
- 結果ダウンロード
- 数値抽出
- 成形データ保存
- データ整理作業
- 考察
- 傾向観察
- 課題抽出
長ったらしく見えますが、実際には大半をスクリプトで自動化するので、人間は茶ぁシバきながらインターネット徘徊してればいいようにしていきます。
最適化 ~ 準備
手順と指針を元に、どのような工夫で最適化していけばよいか、を整理していきます。ところどころで前記事で使ったスクリプト例を出していきますが、改善余地のある状態であり、それを見てここで改善案を込めて書いていっています。
(※subprocess と pexpect が楽しい言いたいだけかもなやつ)
パッケージ整理
前回使った Phoronix Test Suite 固有の例ですが、復数のテストを扱う上で、各テストに必要な yum パッケージがあります。OSによってすぐ扱えるパッケージがあったりなかったりなので、1つ1つ丁寧に対処するのを止め、無いものは潔くテストしないことにしました。その時の対応は、インストールの際に処理を続行せずに、いったん必要とされるパッケージ・存在するパッケージの整理のみを行いました。
多数パッケージのインストールを1回にまとめることで、何がどのくらいの容量入ったのかを確認しやすくなります。また、どのテストが有効で、どのテストを無効にすることになったのか、の整理をすることで、次の判断へ役立てることができます。
インストール判断
そもそもOSアーキテクチャに対応していないテストや、パッケージ不足なテストを除外して、インストールを行うテストの情報を保存します。なぜ単純なインストールのループにしないかというと、成功確信度が低い状態だと途中でループをやり直すことになり、仮に成功済みのテストがあっても installed と判断する処理の時間が無駄だからです。
インストール対象のテストの予定時間や容量とともに、いったんJSONにして保存し、次の実行へと移ります。そうすることで内容が明確で、早く、安定します。
インストール実行
ここからは一般的なベンチマークも対象になります。例えば openssl speed を使いたければ、既に入っていることが多いので何もすることはありません。並列圧縮がしたければ、pigz や pbzip2 パッケージをインストールする。アプリケーションならソースコード配置とミドルウェア準備。がここでの作業になります。
普段の運用だと、できるだけパッケージ管理しますが、ベンチマークではソースからコンパイルして多少グチャっても動けばいいので、手順だけキチンと残しておけば問題ありません。
さきほどの Phoronix の続きでいえば、インストール確定情報を元に、必要容量などでカットしつつ実行を走らせます。テストによってオプション応答など文字列が異なるので、柔軟に自動対応しつつ、失敗したログの確認をしやすくファイルに残しています。
簡単に対処できそうな失敗は、ログを見て対処後にまたインストールを走らせる、を無理のない範囲で繰り返します。
イメージ保存
必要なものが揃ったら、ベンチマークが問題なく動くかを単発で確認します。実際には自動化スクリプトから実行するので、それを通した動作確認も済ませます。一通り終わったら、いったんイメージに保存します。
自動化スクリプト一式は、最初はイメージに保存していましたが、それは止めたほうがよいです。インストール関連は完了後にほぼなにも修正しませんが、スクリプトは細かい修正が入るので、そのたびにイメージ化をするのは効率が悪いからです。
解決策として、スクリプト一式はGitやS3に更新するとよいです。インスタンス起動後にユーザーデータで実行する一連の処理として、それらからダウンロードし、任意の実行をするほうが柔軟です。
最適化 ~ 試験
ここからが工夫の宝庫です。リソース選定
試験において目的は色々あるものの、基本的に大リソース・大容量データによる試験は不要である、という考え方がよいと思います。理由は大きく2つあり、節約の観点と、結果精度の観点になります。節約
費用・時間の両面において大規模な準備は無駄が大きいため、小~中規模程度で結果を得られる工夫をすべきです。例えば、本番で64vCPUsを20台使うからと、テストでも同様のインスタンス量でやるべきなのか?本番データの想定が5TBなら、テストでもそれを用意すべきなのか? と問いかけてみます。小さすぎるのも別の問題が発生しそうですが、もし知りたい結論をより小さい規模で傾向を知れそうであれば、まずは費用を少なく、データ作成時間を短く始めるほうが、心境的にも入り込みやすいでしょう。
精度
結果の精度は条件数が多いほど高くなります。例え本番同様の規模で納得いく結果が得られたとしても、それは点の結果でしかなく、その前後の予測がつきません。また、点のための準備や実行は手作業で済ませてしまいがちのため、条件を広げたり、再準備・再試行に弱いです。vCPUs, 台数, データ容量と3軸が主要素だとして、他2条件を最小そのままに 1, 2, 4, 8, 16vCPUs …と採取、同様に 1, 2, 4台 …と採取、同様に 10, 20, 40GB …と細かく試験し、次段階として3条件を同時に倍々に…… とすれば、障害要素を発掘しつつ、最終目標とする大規模での結果も推測できるのではないでしょうか。
もちろん、大規模でなければ現れない事象がないか確認したり、提出物として納得させる場合など、必要に応じて行うものですが、それはあくまで最終確認であるべき、ということです。
アプローチの基本は、『小さく、細かく、早くじゃ!!』
分割・起動
もし1つ10分の試験が100個あるとしたら、素直に1台で実施すると半日以上かかってしまいます。物理サーバーならそうするしかない、時代もありましたが、クラウドなのでゴリ押し短縮します。単純に、50台の(スポット)インスタンスを起動し、2個ずつ処理を任せれば20分程度で済みます。1台で1000分やろうと、50台で20分やろうと、インスタンス費用はほぼ同じなので、やらない手はありません。
問題があるとしたら、最大vCPUs制限に引っかかったり、スポットが一部起動できなくて── ってのはありますが、起動・実行できた分の結果が残って、不足分だけ再試行する仕組みにしておけば、あとでポチり直すだけなのでたいしたことはありません。
- 例)処理を分割してインスタンスを起動してみる
※1 こいつは既存結果の分を省略した台数にできていない
※2 S3からのスクリプトコピーもせずイメージ埋め込み
スクリプトをS3からコピーし、分割したコマンドを EC2のUserDataで実行して shutdown -h now して自死することで全台なくなったら終了という形。結果数を確認して足りなければ、原因確認と調整をして再試行ポチリ。
もし試験内容に Input -> Output みたいな連続性があるなら、その区切りで分割すればよく、それくらいはベンチマークごとにコーディングする必要はあります。
実行・結果データ保存
ベンチマーク実行部分のキモは、1つのベンチマークごとに結果を外部に保存することです。なにかしらで途中で停止しても、そこまで実行された結果さえ残してあれば、再試行したときに同条件の結果が存在していれば試行をスルーすることができます。Phoronixの例だとちゃんと1コマンド1テストになっていますが、他2つは1スクリプトで全部実行しており分割していないので効率が悪いです。そんなに時間がかかりそうでなく、こうしちゃいましたが、終わってみれば分割したほうがやはりよかったです。
結果を残す部分はシンプルで、余計な文字列処理をせず、標準出力をそのままS3に保存しています。標準出力がないテストもあるので、その場合は自分で時間計測を書くわけですが、たいしたコーディングでもないので改善点としては、標準出力をそのまま保存するのではなく、
1 2 3 4 5 6 |
{ "name": name, "time": float, "stdout": stdout, "stderr": stderr } |
こういうJSON形式で統一して残したほうが使い勝手がよいだろう、と思いました。で、それを条件値を含めたユニークなファイル名になっていれば、結果の存在確認ができるという流れ。
- 例)s3://example-bucket/compress/c5.9xlarge/pbzip2_compress_32.json
s3://example-bucket/compress/c5.9xlarge/pbzip2_decompress_32.json
ド素人の頃はこういう処理において、ループ処理で全部のベンチマークを実行して結果を配列に格納しておいて、そのあとに正規表現で結果を抜いて、最終出力まで。を1スクリプトに収めてたりしましたが、悪手です。なぜなら、せっかくベンチマークが全て成功しても、文字列処理でエラーが出ただけでも全部失うからです。完全にただのアホですけど、そういう時代もあったなぁと:-)
最適化 ~ 集計
ここまでくれば、あとは楽しい〆作業です。結果ダウンロード
S3から手元にでも結果ファイルを落としてきます。カテゴリの異なるテストでも、ファイルパスの規則を統一しておけば、オプション変更だけで使いまわしできます。こちらも、既にダウンロード済みのファイルはスルーしています。時間のかかる分散ベンチマーク中に、途中でもファイルを落としてデータの成形をコーディングするので、ダウンロードの再試行も気軽にポチポチできると良いです。
全ての試験終了後は、想定の結果数があるか確認し、なければ調整と再試行をしにいきます。異なる環境── Phoronix での x86 vs arm とかだと、そもそもテスト数が異なったり完遂できなかったりするので、同ファイル名の結果が揃ったテストのみを有効とします。
数値抽出・成形データ保存
結果ファイルをガーッと読み込んで、標準出力から結果数値をぶっこ抜いたり、time float をそのまま結果とします。一通り配列に収めたら、次のデータ整理のために成形し、ファイルに保存します。今回はスプレッドシートにコピペで貼り付けたかったので、横軸に比較項目、縦軸にテスト項目としてTSVとして吐き出しました。
この辺は処理に時間がかかるものじゃないので、気ままにコーディングを楽しむ休憩タイムです。
データ整理作業
できた結果ファイルを手元PCにダウンロードし、開いてコピーし、スプレッドシートにペーストします。わざわざファイルに残したのは、残すほうがよいという意味だけじゃなく、シェル上で出力してもタブをコピーできないからです。表に貼り付けたら、まずはデータ型の整理と、集計関数でのまとめづくりです。比較をする場合は、結果単位が時間単位そのもの(seconds, ms, ns など)ならば値が小さいほど優秀で、それ以外はだいたいスループット単位になるので大きいほど優秀である、と判断します。
基本データ構成ができたら、罫線や幅、色で見た目を整えて、共有・公開して完成です。
最初はこの作業に時間がかかりますが、1種類できれば雛形となるので、次からは項目数だけ合わせてデータを貼り付けるだけで完成な手順にできます。
考察
ベンチマークそのものとは外れますが、考察完了までがベンチマークです。と怒られないために軽く書いておきます。あとついでに、関連記事を置いておきます。傾向観察
今回のように一通り自動化すると、十分な条件数を手軽に採取できるので、何かしらの傾向を掴むことができます。比較ならば単純な優劣をつけられますし、性能具合ならばスループット値をみて要件に十分であるか判断できます。
より複雑で楽しい傾向としては、こんなものがあります。
- 特定の条件下のみ、他と数値が大きく異なる
- リソースの比率に対して、急に値の伸び比率が落ちるポイントがある
- クラスタ台数と性能値が比例しない
- インスタンスタイプごとに見えない上限値が存在する
基本的には、用意したサーバーの性能やクライアント数に比例する形で計測値が変化する、という前提で数値を観察します。そして比例しなかったり、上限値にぶつかるような動きをみつけたら、それが何故なのか頭をかしげるタイムです。
課題抽出
特定のハードウェアやミドルウェアといった部位の検証の場合、多くは比較するために行います。比較の場合は、優劣を観察し、それぞれの性能において、では本番で取り扱ったときに要件を満たせるのか、という考察をします。どちらも満たせるならば、費用や運用コストなどを踏まえて総合的に判断しますし、満たなければ他の手段や工夫へ舵を切ることになるでしょう。複雑なアプリケーションの場合、レスポンスタイムが一定以下に落ちない かつ 一定以上のスループットが出ること を要件にするとします。試験の結果、性能が条件を守りつつ比例するのであれば、計算上は本番想定の規模でも動くと仮定し、実証へと動きだします。
もし想定より比例しない場合は、本番想定の規模を可能とするには、どのような障壁を取り除いたり、工夫をすればよいか、を詰めていきます。設定や上限値を変更するだけなら簡単ですが、アーキテクチャ的なオーバーヘッドや、アプリケーションそのものの品質が原因ならば骨を折ることになるでしょう。
なんにせよ結果から、想定する動作環境や規模での稼働、その先の未来を見据える、推測できるようにすることが肝要です。点の結果だけで喜ぶのではなく、線を描いて広い範囲で可能性に対応する姿勢が基本となり、それでも起きる想定外の事象の対応がエンジニアの腕の見せ所といったところでしょうか。
あとがき
長々と整理しましたが、これは所詮、私の個人技の域を出ないかもしれません。参考のために提示したプログラムも、こ汚いままですし、フレームワーク的に落とし込みたい欲求はあるものの、そこまでするシステム課題じゃないでしょう。そもそもベンチマーク関連って、要件によって必要とする内容が異なるので、あまり形式張らずに、個人技や部署単位の知見として抱えていることが多いと思います。
──そして、それくらいで良いとも思います。なので、そういう個人技を少しでも効率よくするための、考え方のベースにでもなれば十分かな。という期待を込めて書いてみました。
とか言いながら、『ベンチマークと監視を制するものはシステムを制す!』って思ってるところがあるので、これを元にもーちょい研鑽したくもあり。攻守のバランスが難儀なお年頃であります:-)