02.なぜCPUが複数あっても処理が早くならないのか

●ソフトウェア側の都合

 CPUが複数あっても処理が早くならない。
 これは、ソフトウェア側の都合があるからだ。
 これが意外と厄介なのである。


2006/01/17
●プログラムをマルチスレッド化しなければならない

 複数のプロセッサを使って、並列処理をするためには、Windowsではプログラムをスレッドという実行単位に分割する必要がある。通常のプログラムでは、スレッド分割が意識されていない(スレッドを分けていない)ため、Windowsは並列に実行しようとしない。

 なお、適時 DoEvents 命令を挟んでやることで、処理中に別の処理を割り込ませることはできる。しかし、残念ながら1プロセスあたりに割り当てられるCPUは同時に1つだけであり、CPU使用率の合計は常に1CPU分である。 DoEventsを実行したとき、他の処理が割り込むだけであって、並列には動かない。

 ちなみに、Aの処理中にBの処理が割り込み、さらにその途中でCの処理が割り込んでくると、処理が終わる順番はC→B→Aとなる。割り込まれた方の処理を完全に止めて、割り込んだ方を優先するためだ。処理CでDoEventsをやっても処理AやBが中断したところから再開することはない。
 タイマーイベントを使ってもやはり1CPUしか使わない。

 Windowsで並列処理をするにはプログラムをスレッドで分割しないといけないのである。

 そこで、スレッド分割という話になるのだが、ここに、根本的に広まらなかった原因の1つがある。スレッド分割ができる高級言語が過去ほとんど存在しなかったのである。

 とりわけマルチスレッドが流行しなかったのは、VisualBasicが対応していなったことが大きいと思われる。

 しかし、近年、C#やVB.NETなどがThreadクラスを搭載したことでスレッド生成周りが格段に組みやすくなっている。トレンドがマルチコアへと向かっていることもあり、特集記事をよく見かけるようになった。


2006/03/19
●設計問題

 さて、並列処理をするにはプログラムをいくつかのスレッド(並列実行単位)に分割する訳だが、これは基本設計段階で処理ブロックを明確に分けておく必要がある

 既にあるプログラムを無理に並列動作させると、メモリ競合などの予期しない不具合が起きやすく、予想通りのパフォーマンスも得にくい。(特にグローバル変数を使っての制御をしているプログラムのマルチスレッド化は、まず不可能と言っていいだろう。)

 マルチスレッドプログラミングそのものはたいして難しくないが、期待通りに動かすには、それなりのスキルが必要である。そう簡単に既存のプログラムを並列化できるものではない。


2006/12/30
●同期管理問題

 スレッド分割して、各スレッドを実行させると、それぞれ独立して並列動作を始める。因果関係の問題で、必要なデータが揃ったのを見計らって、後に続くスレッドをスタートさせる必要がある。これを同期管理という。
 互いのスレッドがバラバラに、かつ、物理的に並列で動くので、同期管理は意外と面倒である。

 シングルスレッドでは、単に必要な処理を手順通りに並べれば良かったが、マルチスレッドでは同期管理という処理が入ってくる。

 従来のプログラムと比べて一手間増えてしまうわけで、これがそのままソフトウェアの開発費を圧迫する。
 しかも、同期管理がマズイとプログラムがまともに動かないので厄介である。


2006/03/18
●リソースの競合管理問題

 同期管理よりさらに厄介なのがリソース(資源)管理である。
 コンピュータシステムに1つしかないもの(ファイル、共有メモリ、デバイスなど)を複数のプログラムが同時に利用しようとした時、競合を防いだり、因果関係を維持する管理のことだ。

 スレッド分割されたプロセスは、デュアルプロセッサ以上では、各スレッドが物理的に並行動作する。このため、なにもしていないと物理的に同じ領域にアクセスしに行って、リソースの競合が起きる。

 シングルプロセッサでも仮想的にマルチスレッドで動作するが、競合の起きやすさはマルチプロセッサの方が物理的に並列動作することもあり、格段に確率が高い。
 また、互いのスレッドが資源の空きを待ち続けてデッドロックという状態に陥ったりもする。

 うまく管理しているつもりでも、リソース管理に手落ちがあると何分の一〜何万分の一かの確率で落ちたりロックしてしまったりする。

 これは、テストが非常にやりにくいことを示す。互いのスレッドが非同期に動作するため、「たまたま特定の状態になっておかしくなる」というのが発生することがあるが、困ったことにそれをなかなか再現できない。

 商用の普通のアプリケーションプログラムならば、テスト項目は増える、テスト期間はかかる、不可解な問題が多発する、問題の原因がつかめない、対応に時間がかかる…ということになる。
 (現状では)下手にマルチスレッドに手を染めない方が賢明だろう。


2006/03/19
●マルチスレッドがレスポンスアップに結びつかないという問題

 仮に、プログラムを最適にマルチスレッド実行できるようにチューニングしたとしよう。たとえばクワッド(4)CPUのコンピュータのためにスレッドを4個に分割することで、4秒かかる処理が1秒に短縮できるような場合である。
 しかし、必ずしも「4秒かかる処理が1秒に短縮」する訳ではない。

 多くの場合は1秒で終わるが、何パーセントかの確率で希に3スレッドだけ先行して実行され、残り1スレッドが後追いで実行され、2秒程度の時間がかかることがある。
 そうなってしまう直接の理由はWindowsが非リアルタイムOSであるからだ。

 ゲームなどでマルチプロセッサ対応が遅れているのもこの辺の事情がある。
 詳しくは後で述べることにする。


2006/01/01
●速度問題

 これは、根本的かつ初歩的な問題である。

 CPUを2つ用意するということは、ユーザーは単純計算で2倍のパフォーマンスを期待している。2倍は甘い考えだとしても、せめて1.5倍ぐらいのパフォーマンアップは期待するだろう。

 マルチスレッドのアプリケーションは、シングルCPU前提の場合に比べ、同期管理やらリソース管理やらヤヤコシイ管理が増える。おまけにスレッドの生成は関数呼び出しに比べて、オーバーヘッドもかなり大きく、むやみにスレッドを作ると、その分パフォーマンスに響く。

 高負荷スレッドが多数走るとスレッド切り替えが多発してパフォーマンスがジリジリ落ちてくる。(少なくともシングルCPUでマルチスレッド動作させると、ほとんどの場合遅くなる。)

 プログラム内容にもよるが(将来的な10コア、20コアといったマルチコアを見越して)50や100ものスレッドを一気に発行すると、シングルコアではシングルスレッドで処理した場合に比べてはるかに遅くなる。

 スレッド分割の方法や、管理のやりかたがマズイと、シングルスレッド動作させた方が速く処理が終わるという場合が充分起こり得るのである。

 必ずしもマルチスレッドによる実行が常に優れたソリューションになる訳でもないのだ。


2006/03/19
●トポロジー・ボトルネック問題

 ネットワークの接続形態をトポロジーと言う。マルチプロセッサの場合には、CPU間の結合形態を示す。

 昔のマルチCPUシステムは特殊用途向けに専用で組まれている場合がほとんどであり、用途に応じて様々なトポロジーが考え出されてきた。本来は、そういったトポロジーごとに適性を議論すべきなのだが、民生用に汎用で使えるマルチCPUシステムなど皆無に等しかった。

 民生用途でマルチCPUになったのは比較的最近のことである。トポロジーとしては、メモリ共有型の対称型マルチプロセッサということになる。(厳密に言えば、IntelとAMDではトポロジーが違うが…)
 また、数十台、数百台のプロセッサを使う大規模なものは、高速LANで結ぶ場合が多い。

 一般に、1つの筐体内に複数のプロセッサを積む形態を「マルチプロセッサ」、LAN接続などで複数の筐体を結んで並列処理をする形態を「グリッドシステム」と呼ぶ。

 並列演算は、複数のプロセッサで同時処理することで計算時間を短縮するのが目的だが、並列演算をすれば何でも速くなる訳ではない。
 計算そのものの時間が短かく、データ配信・結果収集に時間がかかれば、別のCPUにわざわざ処理依頼するよりも、1CPU(シングルスレッド)で黙々と全部処理をやってしまった方が速くなる。

 マルチコアでもファイルのI/Oが多い場合も同じ現象が起きる。CPUコアを増設し、プログラムから発行するスレッドをいくつ増やしても、ファイルI/Oがボトルネックになって、全く速くならない場合も起きる。

 並列演算をすれば何でも速くなる訳ではない。
 CPU数と速度は比例しないし、CPU間通信速度が遅ければ、並列処理をしない方が速い場合だってあり得る。


2006/12/30

[戻る]