Visual Studio 2010 の 優れた機能(3)マルチコア プログラミングへの対応

  今ではすっかりマルチコアCPUが普及し、ノートPCなどでも普通にマルチコア、いいものになればクアッドコアを搭載するマシンもあります。
  こうなってきますと、プログラムを開発する側としては複数コアの能力を生かして、より高速に処理を行うことができるアプリケーションを作り、エンドユーザーさんにより快適に使っていただけるようにしたいところです。
 
  もちろんこれまでも、スレッド分割やプロセス分割の技術は多用されてきました。
  ただし、それらを行うには設計時からスレッド分割などを意識したデザインが必要になり、コーディング時にはさらに大変な思いをする必要がありました。
 
  しかし、Visual Studio 2010 および .NET Framework 4 では、最初から複数コアのマシン上で最適に動作するアプリケーションを効率よく開発するための環境が整っています。
  この機能を活用すれば、開発者のみなさんがあまりスレッド分割などのために特殊なコーディングをすることなく、シングルコア向けのプログラムを書く際とあまり変わらないコストで、複数コアを有効活用するアプリケーションを作成することができます。
 
  それでは早速作ってみます。
 
  まずは比較対象とするため、従来の方法でシングルコアのみを使用するプログラムを用意しました。
  Visual Studio 2010 を立ち上げて、C#で コンソールアプリケーションを作成し、以下のようなコードを書きます。
  

  
  
  
  これは 100000回の空ループ処理(temp_loop() )をMainから 100000回呼び出す、というものです。
  ループ処理は、PCのプログラムでは常時発生しているといっても過言ではない処理であり、当然実行時間がかかります。
 
  このプログラムの実行中に、タスクマネージャーで CPUの負荷を見てみると、以下のように2つあるCPUコアのうちの片方しかフル稼働していないことがわかります。
  

  
  
  
  そこで、今回 .NET Framework 4 から実装された、System.Threading.Tasks.Parallel クラスを活用してみます。
  先に Main の中で使用していた通常の for文の代わりに、System.Threading.Tasks.Parallel.For というメソッドを使用しましょう。
  この Parallel.For メソッドは、3つの引数をとります。1つ目の引数がループの開始値、2つ目がループの終了値、3つ目がループ毎に実行されるデリゲートです。
 
  例えば以下のように書くと、画面に「0回目」「1回目」・・・「4回目」と表示されます
  Parallel.For(0, 5, i => { Console.WriteLine( i.ToString() + "回目"); });
 
  では、このParallel.Forを使って先のループ処理を書きなおしてみます。
  書き直すといっても、for文の部分を Paralle.Forにすること、そしてプログラムの先頭の方に using System.Threading.Tasks; を追加するだけです。
  

  
  
  
  これを実行して、先と同様にタスクマネージャーで CPUの負荷を見てみると、今度は2つのCPUコアがフル稼働していることがわかります。
  また、プログラムの実行に要した時間も半分近くになりました。
  
 
  
  このように、Parallel.For を使用するとこれまでforループで行っていた処理を Frameworkの方でCPUコアの数に応じてスレッド分割し、効率よく処理するようにしてくれます。
 
  また、この実行中のスレッド分割の様子は、Visual Studio 2010 のデバッグ機能に含まれている新機能を使って見ることができます。
  プログラムのデバッグ中に、Visual Studio 2010 のメニューの Debug | Windows | Parallel Tasks を選択すると、以下のようにタスクの詳細が見られます。
  上の「No task to display」と表示されている方が Parallel.Forを使用していないプログラムの実行中に表示させたもので、下の複数のタスクが実行されている方が Parallel.For を使用した場合の画面です。

通常の for を使ったプログラム  

 
Parallel.For を使ったプログラム

  さらに、同じくデバッグ中にメニューから Debug | Windows | Parallel Stacks を選択すると、以下のような形でスレッドの図解が表示されます。上がParallel.Forなしの場合で、下がParallel.Forを使用した場合です。
  当然、Parallel.For を使用した方が多数のスレッドに分割されていることが視覚的にわかります。
  
  通常の for を使ったプログラム  

  
  Parallel.For を使ったプログラム

  このように、Parallelクラスを使用してループ処理などをマルチコア対応にすることで、パフォーマンスの改善が期待できます。
  参考までに、私の環境で上記のテストプログラムを実行したところ、Parallel.For を使用していない方が約29秒、そしてParallel.For を使用した方が16秒ということで、大幅なパフォーマンスアップを体感することができました。
  もちろん、この例はParallel.For の効果が最も期待できるようなタイプのプログラムですから、どんなプログラムでも実行時間が半分近くに短縮されるわけではありませんが、なんといっても手軽に導入できるところが魅力です。
 
  過去に作成したアプリケーションをマルチコア対応に書き直すのも、このParallelクラスを使用すればそれほど大変な作業ではないと思われます。
 
  ぜひ、ご活用ください。