【40】プロセス間通信とアプリケーションの応答時間の関係
応答時間は、ソフトウェアの使いやすさを大きく左右します。何か操作をして、その応答を長く待たされることほどストレスのたまることはないからです。特にその性質上、使用中に何度も「入力と応答」を繰り返すソフトウェアの場合は応答時間が重要になります。応答時間が長ければ、ソフトウェアのせいで自分の時間を無駄にされ、生産性も下がったとユーザは思うでしょう。しかし、なぜ応答時間が長くなるのか、その原因は十分には理解されていません。最近はその傾向が顕著なようです。ソフトウェアのパフォーマンスについて触れた文献は多くありますが、その多くがいまだにデータ構造とアルゴリズムに注目しています。もちろんその 2 つが大きく影響することもありますが、最近増えてきたマルチティアのエンタープライズアプリケーションの場合、そういうことは少ないのです。
私の経験から、この種のアプリケーションでパフォーマンスが問題になった時、データ構造やアルゴリズムを調べて改善を図るのは得策ではないと言えます。この種のアプリケーションの場合、応答時間を大きく左右するのは、リモートプロセス間通信(IPC:Inter-process Communication)の数です。入力への応答に IPC がいくつ必要になるかで、パフォーマンスが大きく変わるのです。他にも個々のソフトウェアに特有のボトルネックはあるでしょうが、通常はリモート IPC の影響が最も大きくなります。リモート IPC が行われると、たとえそれが 1 度だけでも、ソフトウェア全体の応答時間を無視できないほど遅延させます。リモート IPC がいくつも続けて発生すれば、それが積み重なって遅延が極端に大きくなってしまうのです。
その代表例が、オブジェクトリレーショナルマッピングを利用したアプリケーションで行われる「リップルローディング」です。リップルローディングとは、オブジェクトグラフを作成する際に、グラフ作成に必要なデータを取得するために何度もデータベース呼び出しを繰り返すことです(Martin Fowler 著『エンタープライズアプリケーションアーキテクチャパターン』の「遅延ロード」の頁を参照)。データベースのクライアントが Web ページをレンダリングするミドルティアのアプリケーションサーバである場合、通常、データベース呼び出しはシングルスレッドでシーケンシャルに実行されます。その一つ一つで発生した遅延が積み重なり、全体の応答時間に大きな影響を与えるのです。個々のデータベース呼び出しの所要時間が 10 ミリ秒だとしても、1,000 回の呼び出しが必要になれば(そのくらいは珍しくありません)、全体の応答時間は 10 秒を超えてしまう計算になります。他に同様の遅延が起きる処理の例としては、Web サービスの呼び出し、Web ブラウザからの HTTP リクエスト、分散オブジェクトの呼び出し、要求-応答メッセージング、カスタムネットワークプロトコルを通じたデータグリッドとのやりとりなどがあげられます。入力への応答に必要になるリモート IPC が多ければ多いほど、応答時間も長くなります。
リモート IPC の数はどうすれば減らせるでしょうか。そのための方法は様々ですが、比較的簡単でよく知られている方法もいくつかあります。その 1 つは、「思考節約の法則(Principle of Parsimony)」を応用する方法です。これは、具体的には、プロセス間のインタフェースを最適化し、本当にいま必要なデータだけを必要最小限のインタラクションで取得するという方法です。他には、IPC をできるだけ並列化するという方法もあります。そうすれば、全体の応答時間は、主に最も遅延の大きい IPC によって決まることになります。また、以前の IPC の結果をキャッシュするという方法もあります。キャッシュを利用することで、その後に必要になる IPC の数を減らそうというわけです。
アプリケーションの設計にあたっては、入力への応答に必要な IPC の数が多くならないよう配慮すべきでしょう。パフォーマンスの悪いアプリケーションをよく調べてみると、1 つの入力への応答に、IPC が数千も必要になっているということがよくあるのです。データ構造やソートアルゴリズムを工夫するよりも、キャッシュや並列化などのテクニックによって、入力に対する IPC の数を減らす方がはるかに効果的でしょう。