パフォーマンスチューニングが目指す性能とボトルネック

## パフォーマンスチューニングとは

パフォーマンスチューニングは、次のように説明されていました。

> **パフォーマンスチューニング**とは，システムの処理性能や信頼性を高めるために，システムの動作環境を最適化することです。パフォーマンスチューニングを実施することで，システムの性能を最大限に生かし，安定稼働させられるようになります。
>
> [Cosminexus V9 アプリケーションサーバ システム構築・運用ガイド](http://itdoc.hitachi.co.jp/manuals/link/cosmi_v0970/03Y0260D/EY020046.HTM#:~:text=%E3%83%91%E3%83%95%E3%82%A9%E3%83%BC%E3%83%9E%E3%83%B3%E3%82%B9%E3%83%81%E3%83%A5%E3%83%BC%E3%83%8B%E3%83%B3%E3%82%B0%E3%81%A8%E3%81%AF%EF%BC%8C%E3%82%B7%E3%82%B9%E3%83%86%E3%83%A0,%E3%82%89%E3%82%8C%E3%82%8B%E3%82%88%E3%81%86%E3%81%AB%E3%81%AA%E3%82%8A%E3%81%BE%E3%81%99%E3%80%82)

パフォーマンスチューニングに関して、私自身はほぼ経験がないのですが、ざっくりと概要を知っておきたいと思い、次のようなことを調べてみました。

* 性能とは何か
* 性能の測定方法
* ボトルネックになりうる箇所

よく調べたつもりですが、間違った記載などがありましたら、ご指摘いただけると嬉しいです。

## 性能とは何か

パフォーマンスチューニングとは、システムの処理性能を高める施策のことのようなので、まずは性能という言葉の意味を正確に知る必要があると思いました。

一般的には、次のようなものを性能と呼ぶようです。

* レスポンスタイム（ユーザー応答時間）
* スループット（データ処理能力）

また、システムの品質や運用性、操作性、安全性なども性能と呼ばれることもあるようです。

しかし、自分が調べた範囲では、レスポンスタイムとスループットの改善を指してパフォーマンスチューニングという言葉が使われることが多いようです。

### レスポンスタイム

レスポンスタイムという名前から、「リクエストされてからレスポンスが返るまでの時間かな」となんとなく想像がつきましたが、似た言葉にターンアラウンドタイムというものがあって、よく両者の違いがわからなくなります。

レスポンスタイムは、システムや装置に要求や入力を与えてから、反応を返すまでにかかる時間のことのようです。

例として、次のような処理の流れが挙げられていました。

1. 処理の依頼開始
   * 端末からコンピュータに処理を依頼する
2. 依頼完了
   * 処理依頼がサーバに届く
3. 処理待ち時間
   * 他の処理が終わるまで処理待ち状態になる
4. サーバ処理時間
   * 依頼された処理をサーバが実行する
5. 処理の応答開始
   * 処理結果がサーバから送られ始める
6. 応答完了
   * すべての結果が返り処理が完了する

ここでいう3.〜4.がレスポンスタイムだそうです。

レスポンスタイムが短いことは、ユーザーや他のシステムなど外部にとっては待ち時間が短いことにつながります。

ちなみにターンアラウンドタイムは、システムに処理要求を送ってから結果がすべて返されるまでの時間のことで、上記の例でいうと1.〜6.を指すようです。システム間の入出力に時間がかからない場合は、ターンアラウンドタイムはレスポンスタイムに近づきます。

余談ですが、この2つの言葉は紛らわしいので、「名前が長いほう（ターンアラウンドタイム）が長いほう」という覚え方が本で紹介されていました。私自身は、リクエスト生成 => 通信 => レスポンス生成 => 通信という流れのうち、レスポンス生成に関わる部分がレスポンスタイム、と覚えることにしています。

### スループット

スループットは、コンピュータやネットワーク機器が単位時間あたりに処理できるデータ量のこと、と説明されていました。単位は1秒あたりのデータ量（ビット数）で、「bps」です。

スループットという言葉は、文脈によって少し意味が変わるようです。

データ処理におけるスループットは、単位時間あたりの処理能力のことで、CPUのクロック周波数やハードディスクの回転速度、OSなどの要因が影響する、と説明されていました。一方、ネットワークにおけるスループットとは、単位時間あたりのデータ転送量のことで、無線LAN機器などに「実行伝送速度：50Mbps」などと表記されるようです。

### トランザクション処理能力

性能について調べていたときに、レスポンスタイムやスループットと並んで、トランザクション処理能力もシステム性能の指標として挙げられているのを見ました。

トランザクション処理とは、いくつかの関連ある処理をまとめて管理する処理方式のことで、処理結果がすべて成功したことを保証するために利用されます。トランザクション内の処理がひとつでも失敗した場合は、俺以前の完了済みの処理を取り消して、トランザクション開始前の状態に戻します。

トランザクション処理能力とは、単位時間あたりに実行できるトランザクションの件数のことで、TPS（Transactions Per Second）という単位が使われるようです。DBMSやDBサーバの性能指標としてよく用いられる、と書いてありました。

### レイテンシ

知らなかったのですが、レイテンシとレスポンスタイムは同じ意味らしいです。つまり、サーバでの処理待ち時間とサーバでの処理時間の合計、ということでしょうか。

レイテンシを通信の遅延時間と説明している記事が多かったです。Google翻訳では「待ち時間」と翻訳されました。ですので、ユーザや他のシステムから見たときのレスポンスタイムをレイテンシと呼ぶ、と自分は解釈しました。

### 信頼性

この記事の冒頭で引用した文章には、パフォーマンスチューニングの目的はシステムの処理性能や信頼性を高めること、と書いてあったので、信頼性の意味も軽く確認してみました。

障害に強いシステムを説明するとき、「信頼性が高い」と言ったり、「可用性が高い」と言ったりします。しかし、信頼性と可用性は違う意味の言葉だそうです。ここで整理してみます。

* 信頼性（Reliability）：障害発生のしにくさで、システムやサービスが使えなくなる頻度やその間隔のこと
* 可用性（Availability）：稼働率で表現される、システムやサービスが利用できる時間の割合のこと

わかりやすい例があったので紹介します。

「1000時間故障しないで稼働できるが、故障すると1000時間使えない機器」と「99時間稼働すると、故障して1時間使えなくなる機器」があったとすると、前者は信頼性が高い機器で、後者は可用性が高い機器となります。信頼性の指標である平均故障間隔は、前者が1000時間で後者は99時間です。可用性の指標である稼働率は、前者が50%で後者は99%です。

## 性能の目標値

システム性能の目標値は、現在の性能値や要件などをふまえてシステムごとに設定するようです。

> - レスポンスタイム(平均, 最大)
> - スループット(平均, 最大)
> - CPUやメモリ等のコンピューティングリソースの使用率(平均, 最大)
> - ディスク容量等のコンピューティングリソースの消費量
> - エラーの発生頻度
> - その他 (ガベージコレクションの発生頻度等)
>
> https://qiita.com/s_yokoi/items/4e21aed46e561badce94

上記のように、実際の業務では設定しなければならない目標値はたくさんあるのですが、今回は特に重要そうな項目にしぼって、目安となる目標値はどのくらいかを調べました。

### CPU使用率

CPU使用率は、それ自体は性能指標ではないと思いますが、パフォーマンスチューニングについて調べていると、よくCPU使用率の目標値について書かれていました。

CPU使用率が90%というと、「やばそう」という感覚を私も持っていたのですが、パフォーマンスチューニングにおいてはそうではないようです。むしろ、CPU使用率が100%に近いほうが、リソースを有効に活用できている好ましい状態で、CPU使用率が低いとオーバスペック、という評価になるようです。

しかし、通常の負荷でCPU使用率が100%になるようなチューニングは、ピーク時の負荷を処理するキャパシティがないということになるので推奨されません。CPU使用率の目標値は、レスポンスタイムの増大に敏感なシステムでは70〜80%、そうでない場合は90%程度に設定すると良い、と説明されていました。

### レスポンスタイム

レスポンスタイムはユーザの待ち時間のことでした。

こちらも、現在の数値や性能要件によって様々だと思いますが、3秒以内を目標にするように、と書いている記事が多かったです。

ユーザの待ち時間が3秒を過ぎると、57%がしびれを切らして訪問を諦める、という有名な調査結果があります。

また、読み込み時間が1秒遅くなると

* ページビューは11%減少
* 顧客満足度は16%減少
* コンバージョン率は7%減少

という数字も載っていました。

Amazonのような巨大なサイトでは、1秒遅いことが年間16億ドルの機会損失になるそうです。

### スループット

ストレスのない通信速度は10Mbps（スマホは5Mbps）～30Mbps程度といわれているようです。

ユーザは、データを送信・アップロードするときよりも、受信・ダウンロードするときのほうが速度に敏感らしいです。また、用途によっても求められるスループットが異なるようです。

| 用途               | スループットの目安 |
| ------------------ | ------------------ |
| メールやLINEを受信 | 128kbps〜1Mbps     |
| Webサイトを閲覧    | 1Mbps〜10Mbps      |
| 動画を視聴         | 5〜20Mbps          |

### ロードアベレージ

ロードアベレージとは、（実行中のプロセスの数 + 処理待ち状態のプロセスの数）の平均値のことで、CPUにどのくらいの負荷がかかっているかを示す指標だそうです。

topコマンドにより確認ができます。

```bash
$top
top - 23:01:13 up  9:38,  2 users,  load average: 0.00, 0.01, 0.04
Tasks:  71 total,   1 running,  70 sleeping,   0 stopped,   0 zombie
Cpu(s):  0.0%us,  0.0%sy,  0.0%ni, 99.8%id,  0.0%wa,  0.0%hi,  0.0%si,  0.1%st
Mem:   1019444k total,   450140k used,   569304k free,    16368k buffers
Swap:        0k total,        0k used,        0k free,   369504k cached
```

`load average: 0.00, 0.01, 0.04`という数字の左から、直近1分間、5分間、15分間のロードアベレージを表します。

ロードアベレージの基準は、CPUのコア数が1の場合は、1.00以下といわれているそうです。

ロードアベレージはシステム全体の実行中および処理待ちのプロセスから計算されるため、CPUのコア数が2でロードアベレージも2.00ならば、各CPUが平均して1つずつプロセスを抱えていることになります。ですので、CPUのコア数よりもロードアベレージが低ければ問題ない、と考えられるようです。

## ボトルネックの特定

ボトルネックとなるうる箇所や、性能解析のツールについて簡単にまとめました。

### ボトルネックとは

ボトルネックとは、全処理工程のうち処理速度が最も遅い箇所のことです。

砂時計でいうと中央の最も細いところがボトルネックにあたり、他の部分をいくら広げても、ボトルネックの直径が変わらなければ、砂が落ち切るまでの時間は変わりません。このことから、ボトルネックは全体の処理速度を決めている箇所と考えられるため、有機化学などの分野では律速段階と呼ばれていました。（ボトルネックという言葉はエンジニアになってから知りました）

### 理想は全体をボトルネックにすること

ボトルネック以外の処理を改善しても、全体の処理速度にはあまり影響しないため、パフォーマンスチューニングはボトルネックを見つけることから始まります。

調べていると、「理想はすべての処理段階が同じくらいのボトルネックになっている状態」という意味の説明があって、面白い表現だなと感心しました。「ボトルネックをなくす」というと、問題のある箇所を修正するというニュアンスを感じますが、「全体をボトルネックにする」というと、それぞれの要素が最大限のパフォーマンスを発揮している状態を想像します。

ボトルネックは、他の箇所と比べたときの相対的な評価なので、最も処理速度の遅い箇所が改善されれば、次は二番目に処理速度が遅かった箇所がボトルネックになります。大きな問題を解決することで、それまで見えなかった問題が顕在化します。

1. ボトルネックを見つける
2. 実装を修正する
3. パフォーマンスをテストする

パフォーマンスチューニングは、上記の1〜3の手順を繰り返しながら行うそうです。

### 負荷テストとプロファイリング

ボトルネックを特定する方法について検索すると、負荷テストとプロファイリングについての記事がヒットすることが多かったので、それらについてまとめてみました。

#### 負荷テストとは

負荷テストは、システムに通常時やピーク時に想定される負荷をかけ、性能や耐久性を検証するためのテストのことです。通常は問題なく稼働しているシステムも、トラフィックが増加したり、負荷の高い処理を要求されたりすると、停滞やハングアップなどの異常が発生することがあるようです。

コンピュータについて学ぶまえは知らなかったのですが、負荷の増加と性能の劣化は必ずしも比例しないようです。

たとえば、メモリなどのリソースを使い切ってしまった場合、通常時と違う手順で処理が実行されるようになるため、あるときを境に急激に性能が悪化します。また、多重ループなども、負荷の増加に対して性能の劣化が急激です。

このように、想定される負荷を実際にかけてみないと稼働時の性能を検証できないため、負荷テストを実施する必要があるのだと理解しました。負荷テストには、ピーク時の負荷に耐えられるかテストするピークロードテスト、想定以上の高負荷がかかったときの挙動を調べるストレステストなどの種類があるようです。

#### プロファイリングとは

プロファイリングというと、FBIが犯人の特徴を推測することを連想してしまいましたが、ソフトウェア工学においては動的プログラム解析の一種だそうです。

プロファイリング（性能解析）とは、プログラム実行時の関数呼び出し頻度や、それにかかる時間を計測すること、と説明されていました。逆にプログラムを実行せずに解析することを静的コード解析と呼びます。こちらは文法的におかしい部分を指摘してくれるものです。

負荷テストとプロファイリングが、同じような文脈で使われている記述も多かったです。プロファイリングはプログラム実行時の性能を検証することなので、その一部に負荷テストが含まれるのかな、と思いました。

## ボトルネックとなりうる主要項目

すべての処理工程がボトルネックになりえますが、しかし、比較的ボトルネックになりやすい箇所というのは決まっているようです。[こちらの記事](https://gihyo.jp/admin/serial/01/eng_knowhow/0019?page=1)にわかりやすく書かれていたので引用します。

>I/Oにはディスクやネットワーク，メモリなどがありますが，メモリがボトルネックになることはまずありません（メモリが小さくてスラッシングが，というのはメモリI/O自体に話ではないですね⁠）⁠。つまりインフラエンジニアが取り組むパフォーマンスチューニングはディスクとネットワークが対象のことが多い，ということになります。さらにさらにいえば，ネットワークよりはディスクのほうがネックになるケースのほうが格段に多いです。ここにCPUも入れて考えてみると，ディスク，CPU，ネットワーク，メモリという順にボトルネックになりやすい気がします（もちろんケースバイケースです⁠）⁠。つまり，まず最初に習得するべきはディスクI/Oについてです。
>
>https://gihyo.jp/admin/serial/01/eng_knowhow/0019?page=1

ここで紹介されていたボトルネックを箇条書きにすると、次のようになります。

1. ディスク
2. CPU
3. ネットワーク
4. メモリ

ここでCPUといっているのは、ループが深すぎるといったプログラムの問題のことのようです。

### ディスクI/O

参考にさせていただいた記事の中で、最もボトルネックになりやすい箇所と紹介されていたのがディスクI/Oです。

ディスクI/Oとは、ハードディスクなどの外部記憶装置に対するデータの読み書き操作のことですが、これがボトルネックになりやすい理由は、読み書きに時間がかかり、かつ使用頻度が高いからだそうです。メモリに対する読み書きが電子的な動作なのに対し、ディスクに対する読み書きは物理的な動作のため、圧倒的な速度差があります。

> | 名前                        |    読込速度 | 読込速度(MB/s) |
> | :-------------------------- | ----------: | -------------: |
> | メモリ                      |      10GB/s |      10000MB/s |
> | 有線ネットワーク            |       1GB/s |       1000MB/s |
> | SSD(シーケンシャルアクセス) |     100MB/s |        100MB/s |
> | HDD(シーケンシャルアクセス) |     100MB/s |        100MB/s |
> | SSD(ランダムアクセス)       |      10MB/s |         10MB/s |
> | HDD(ランダムアクセス)       |       1MB/s |          1MB/s |
> | 4Gネットワーク              |  1MB-10MB/s |     1MB-10MB/s |
> | 3Gネットワーク              | 100kB-1MB/s |    0.1MB-1MB/s |
>
> https://qiita.com/awakia/items/c8ada6c8101efe2de561

おおまかな測定方法と、代表的なチューニング方法について調べてみました。

ディスクI/Oを測定するには、iostat、vmstatなどのコマンドが使えるようです。下記はiostatコマンドの実行結果です。

```bash
% iostat
              disk0       cpu    load average
    KB/t  tps  MB/s  us sy id   1m   5m   15m
   21.45   15  0.31   7  4 89  1.31 1.88 1.87
```

ディスクI/Oをチューニングする方法としては、次のような方法が紹介されていました。

* 回転数の高いディスクに交換する
* キャッシュを活用する
* RAIDを導入してデータ書き込みを分散する

回転数の高いディスクに交換する方法として、HDDをSSDに置き換えることが推奨されていました。SSDの場合、HDDのように目的のトラックまでヘッドを移動させるシーク時間や、目的のセクタに到達するまでの回転待ち時間がかからないため、非常に高速です。

また、キャッシュの活用も費用対効果の高いチューニング方法のようです。キャッシュは、ディスク自体にも搭載されているようですし、OSやファイルシステムやプログラムなど、いろいろなレイヤで活用できるようです。

RAIDは、Redundant Array of Inexpensive Disksの略で、複数のHDDをひとつのドライブのように認識させる技術です。RAIDはストレージ冗長化のイメージが強かったのですが、書き込みを分散させて高速化できるRAID 0やRAID 5はパフォーマンスチューニングにも使われるようです。

## まとめ

パフォーマンスチューニングにおける性能や目標値について、自分が調べたことを書いてきました。

* 性能とは、主にレスポンスタイムとスループットのこと
* CPU使用率は高いほうがよいが、余裕をもたせて80%程度を目標にする
* レスポンスタイムは3秒を超えると致命的で、多くの記事では1秒以内を目標にしていた
* ストレスのないスループットは、用途によって違うが10Mbps～30Mbps程度といわれる
* ロードアベレージはCPUにかかる負荷の指標で、1コアあたり1以下が望ましい
* ボトルネックになりやすい箇所としてディスクI/Oがある

完全な初心者が書いていますので、間違っている箇所がありましたら、教えていただけると嬉しいです。

本当は、性能の測定方法や具体的なチューニング方法についても書きたかったのですが、ネットで検索するだけではなかなか体系的に理解することが難しく、途中で断念しました。また機会があれば挑戦したいと思います。