「ボトルネック」の辞典作ってみた。システム効率化のための諸概念を整理する

「このシステムのボトルネックは…」それどういう意味?

ボトルネックとは「隘路(あいろ)」とも呼ばれ…という説明に興味がある方は、あまり本ブログを覗かないものと思いますが、一応はじめに書き添えておきますと、 システム開発におけるボトルネックは「システム全体の処理においてもっとも負荷がかかり、全体の処理速度を決めてしまう要素」を指します。文字通り、瓶から水を出そうと考えたとき、「ボトルネック」(細くなっている部分)の太さが流量を決めることからこの名がつきました。

本記事では「ボトルネック」が生まれうるポイントを整理していくことで、「どこにボトルネックがあるのか?」技術者が調べる際のヒントになればと考えています。

CPU利用率とボトルネック

CPU(Central Processing Unit)はコンピュータアーキテクチャにおける心臓部で、抽象化された命令に対応して実質的な処理を行う「実働部隊」のようなものです。

まずCPU利用率の確認方法としては、Windowsではタスクマネージャ、Macではアクティビティモニタで確認できます。 単一のアプリケーションだけでなく、PC上で動くすべてのアプリケーションが共通のCPUリソースを共有するので、システムを実装する際は、CPU利用率を安定させることが求められます。

CPU利用率が低い時の考えうる原因 - 処理の並列化ができていない

近年はマルチコアCPUが一般的になってきているため、CPU利用率は「すべてのコアを最大まで利用している場合」を100%とした場合の数値で出力されます。 8コアのCPUの場合、一つのコアだけがフル稼働している状態の場合は、CPU利用率が12.5%…といった具合です。

処理の並列化を前提として実装しないと、サーバーリソースを十分活用できないということもありえますので、この点に注意が必要です。

メモリ容量に関連するボトルネック

ここでメモリとは「プログラムそのもの」と「利用するデータ」が保持されるRAM(Random Access Memory)のことで、HDDメモリのことではありません。RAMの容量が足りなくなったときは、RAM内の使われていないデータを HDD内に退避させる「スワップ」が起きることはご存知のとおりです。 スワップ処理はRAMへのアクセスに比べて遥かに大きな時間がかかるので、スワップが頻発している時は、処理速度に影響が出ることになります。これが「メモリ容量に関連するボトルネック」として考えられる主な原因です。

スワップが頻発している時は、RAMが「これ以上は覚えられない」状態に陥っているということなので、記憶領域のメンテナンスを行う必要があります。

ディスクI/Oのボトルネック

ハードディスクなど外部記憶装置にアクセスする頻度が高い・一回に要求するデータ量が大きいシステム(特にSQLやRDBMSが絡むもの)は、ディスクI/Oに関するボトルネックが生じやすくなります。 ディスクI/Oのディスクはハードディスクのこと、I/Oは入出力ですので、ディスクI/Oのボトルネックとは、ハードディスクの読み書きにまつわるボトルネックということになります(単に『I/Oボトルネック』とも呼ばれます)。

ディスクI/O負荷には、クエリの処理に対する応答時間「レスポンス」と、単位時間あたりの処理量「スループット」に分かれており、多数のクエリが飛ぶ時はレスポンス、クエリあたりで処理するデータサイズが大きい時はスループットにボトルネックが生じやすいと言われています。

ディスクI/Oのボトルネックの特定・把握方法については、後述する参考4.によくまとめられていますので、ご確認ください。

ネットワークのボトルネック

ネットワークを通じて外部サーバーとやり取りするシステムは、通信路(ex. TCPコネクション)、いわゆる回線にボトルネックが生じることがあります。ネットワークI/Oのボトルネックとも呼ばれます。

近年はシステムのクラウド化が進んでいるため、これまでネットワークにボトルネックが生じ得なかったシステムについても、この項目を考慮しなくてはならなくなってきています。

諸概念のボトルネック解消はそれぞれ競合しあう

本記事で紹介する諸概念について、それぞれ生じるボトルネックを改善しようとしたとき、「あちらを立てるとこちらが立たず」のトレード・オフ関係になることがあります。 後述する「ディスクI/O」のボトルネックを解消するためにデータ圧縮を施すと、CPUリソース要求が高まって、「CPUに関連するボトルネック」が顕在化する…といった具合です(詳細については後述する参考記事2.などで紹介されています)。

そのため技術者は、システム全体を俯瞰した上でボトルネックの所在を明らかにし、ボトルネックの解消の手段もまた、システム全体を見渡して検討する必要があります。

ここまでの参考記事

理論上の「ボトルネック」 (はみだし話)

ここまではエンジニアリングの実務に近い話題として「ボトルネック」の切り分けを行ってきました。 本記事では、「アルゴリズムとボトルネック」の話にも触れてみたいと思います。

アルゴリズムの分野では、処理速度は「時間複雑性」という尺度、メモリ効率は「空間複雑性」という尺度で測ることになっています。 これは例えば数列を昇順・降順に並べるソートアルゴリズム、最短路を検索するためのダイクストラ法といった、「入出力が明快に定義できて、処理内容が1本の擬似コードで書けるような処理(アルゴリズム)」に関する、処理速度面・メモリ効率面の性能を測る尺度です。

具体的な例を用いて説明してみましょう。

例えばソートには幾つかのアルゴリズムがあることが知られていますが、データサイズ(ソートの場合、ソートの対象となる整数がいくつあるか)をnとするとき、バブルソートの時間複雑性はO(n^2)、マージソートやクイックソートではO(nlogn)です(後者の方が速い)。 例えばほかの処理の時間複雑性が高々O(n)であるようなシステムにおいては、バブルソートを使うかマージソートを使うかが、全体の処理速度に効いてくるということになります。

下のグラフは、時間複雑性がO(n^2)のアルゴリズム1種類と、時間複雑性がO(nlogn)のアルゴリズム2種類について、データサイズnに対する実行時間の推移の例を示したものです。

参考:https://www.toptal.com/developers/sorting-algorithms

理論的な「アルゴリズムの性能」から分かることとしては、

  • データサイズがある程度大きくなると、時間複雑性が大きなオーダのアルゴリズムは、時間複雑性が小さいオーダのアルゴリズムよりも、時間がかかるようになる

という点です。この処理がCPUリソースを使う場合、

  • データサイズがある程度大きくなると、時間複雑性が大きなオーダのアルゴリズムは、時間複雑性が小さいオーダのアルゴリズムよりも、CPUの利用率を引き上げるか、処理全体の速度を落としてしまう

ことがわかります。 理論的な処理速度・メモリ効率性については、直接触れることは少ないものの、エンジニアがユーザーとして接するライブラリが設計される際、こう言った「理論的性能」を踏まえてアルゴリズムが選択されています。

なお、今回例として扱った「ソートアルゴリズムの性能」については、わかりやすく可視化されているアニメーションページがありますので、興味があればご覧ください。

参考

「ボトルネックとは?」をまとめる意義

「ボトルネック」という言葉は非常に濫用されがちな用語です。ここまで見てきた通り、現実的には様々なところに「ボトルネック」が生まれうるので、 一度棚卸しをしておくことは、特に初級レベルのエンジニアにとっては有用なのではないか、ということでまとめてみました。

また、システムのユーザーさんからフィードバックやご要望を頂く際には、様々な症状が「重い」「遅い」「応答がない」という言葉一つに集約されてしまうので、技術者がリードする形で原因を探らなければなりません。 その際、「ボトルネックの辞典」があれば、調査の視野が広がったり、思わぬヒントを得られることがあるかも…ということで、概観程度ではありますが、今回まとめてみた次第です。

また、もう一つ「開発フローにおけるボトルネック解消」というテーマについても記述のしがいがあります。 本記事とは少しテーマが離れてしまいますので、こちらは、別の記事において、簡単に触れたいと考えています。

最近の記事タグ

関連記事

\(^▽^*) 私たちと一緒に働いてみませんか? (*^▽^)/

少しでも興味をお持ちいただけたら、お気軽に、お問い合わせください。

採用応募受付へ

(採用応募じゃなく、ただ、会ってみたいという方も、大歓迎です。)