連載:C# 4入門

第2回 タスク並列ライブラリ

株式会社ピーデー 川俣 晶
2010/08/20
Page1 Page2 Page3

 さる事情で、最低限BDメディアを再生できる機材が欲しいと思った。そして、自宅で使っていたノートPCに複数のハード的な不具合が出ていて、対処を迫られていた。しかし、修理代もかなり高くつくので、可能であれば代替パソコンの購入資金に充てた方がよいと思っていた。そういうある日、とあるAVノートがかなり値引きして売られているのを発見した。悩みながらさらに見ていると、値引き率は拡大した。2010年夏モデルが出たので、2010年春モデルは在庫整理の対象になったのだろう。結局、安い春モデルをオーダーした。

 このノートPCはそういう事情で買ったものなので、CPU性能などまったく気にしていなかった。単に64bit版Windows 7が使用でき、BDドライブが付いていて再生できるということしか知らない。

 だから、届いたマシンを起動してびっくり。「初期状態でデスクトップに表示されるCPU利用率のモニタに、ゲージが4個もある!」

 しかし、さらに驚いた。

 この4個のゲージはいくら使っても0%のまま動かない。それもそのはず。実は、このゲージはCell技術をベースに東芝が開発したSpursEngine(スパーズ・エンジン)という独自CPU専用のゲージであり、メインCPUであるCore i5の利用率はまた別であったのだ。では、そちらはどうかといえば、タスク・マネージャを開くとCPU利用率のグラフが4つ出てくる。SpursEngineの4コアとはもちろん別である。

 「このマシン、いったいいくつコアがあるのだ!」

 はい。SpursEngineが4コア。Core i5は2コア4スレッドで、コア数は6(HT技術で仮想的には8)というのが正しい解釈のようだ。

 そこで思った。

 コア数が多くなっていく「メニー・コア時代」というのは、まだまだ一部に限られると思っていた。ハイエンドのマニアや、サーバの世界でこそ、多数のコアが並列するシステムは出てきているものの、ローエンドの世界では、まだまだ1〜2コアだと思っていた。しかし、すでに型落ちで、しかも最上位機種ですらないノートPCでも、このコア数である。

 「メニー・コア時代は単なる未来予測でも、机上の空論でもない。すでに片足はその世界に入りつつある」

 もちろん、まだ独自プロセッサのコア数が多いマシンがあるというだけで、これらは直接C#などでプログラミングできる状況ではない。しかし、メインCPUのコア数の増加傾向も著しく、平均的ユーザーが使用するPCが本格的なマルチコアの時代に入ることを見越して開発を始めねばならない時期はもう来ているのだろう。

 しかし、問題はソフトウェアの方だ。2コアぐらいまでなら、それほどコア数を意識する必要はない。たいていのプログラムはOS側とアプリ側で負荷を分散する程度で快適に動くわけで、コア数が2というのは、そのために十分な数字である。特に計算量が多いプログラムでは並列処理を行う価値はあったが、比較的少数派といえる。

 ところが、この水準を踏み越えて先に行くと、どうしてもコア数を意識したプログラミングを行う必要が出てくる。クロックを上げて高速化するよりも、コア数を増やして性能をアップするタイプのCPUは、従来型のプログラミングでは性能が出ないのだ。実際に、簡単なベンチマークを実行してみたが、2コア4スレッドのCore i5でも、複数コアを意識したプログラミングを行うだけで2〜3倍のスピードアップは当たり前であった(もちろん、I/O待ちなどの処理を含まない純粋な計算処理の場合である)。

 さて、真の問題はこの先にある。それはコア数を意識したプログラミングは意外と面倒くさいという点である。これまで、このような問題に対して、ソフトウェア側の対応は遅れがちであったといえる。しかし、Visual Studio 2010と.NET Framework 4、そしてC# 4は着実にそのリアルな現実に対応する一歩を踏み出したといえる。これは空想の話でも、かくあるべきというベキ論でもなく、現実についての話である。

単純に2コアを使う

 1980年代、Occamというプログラミング言語を見て驚いたことがある。これはトランスピュータという並列実行を前提とするCPUのためにプログラミング言語だ。以下のように、この言語では組み込まれたPAR命令を使うだけで、2つの計算を並列して実行できる。

PAR
   x := x * 2
   y := y * z
リスト1 PAR命令による並列処理(Occam言語)

 しかし、C#で正攻法によるスレッド・プログラミングを行おうとすると、けっこう面倒だ。例えば、単にスレッドを1つ作って、メインのスレッドと並行に実行するだけでこうなる(リスト2)。

using System;
using System.Threading;

class Program
{
  static void Main(string[] args)
  {
    var thread = new Thread(()=>Console.WriteLine("on sub thread!"));
    thread.Start();
    Console.WriteLine("on main thread!");
    thread.Join();
  }
}
リスト2

on main thread!
on sub thread!
リスト2の実行結果
順番は相前後するかもしれない。

 開始させるためのStartメソッドや、終了を待つJoinメソッドの使用などが必要となり、あまりスマートでも簡単でもない。

 しかし、C# 4はOccam言語の水準にかなり接近した。スレッドではなく、新しい「タスク(Task)」という機能と、それに含まれるParallelクラスを使用すると、たったこれだけである(リスト3)。

using System;
using System.Threading.Tasks;

class Program
{
  static void Main(string[] args)
  {
    Parallel.Invoke(
                ()=>Console.WriteLine("1st task!"),
                ()=>Console.WriteLine("2nd task!"));
  }
}
リスト3

2nd task!
1st task!
リスト3の実行結果
順番は相前後するかもしれない。

 ここでは、「using System.Threading;」が「using System.Threading.Tasks;」に変化したことに注目していただきたい。かつて、コレクションが「System.Collections;」から「System.Collections.Generic;」に変化したのと同様の事態が、スレッドの世界に起こっているわけだ。

 ちなみに、Parallel.Invokeメソッドの引数は可変なので、いくつでも並べてラムダ式などを書くことができ、それらはコアの数だけ並列に実行できる。ここでは、あくまで「2個で事例としては十分であり、3個以上書いても冗長」という考えにより、2つしか書いていないだけである(極めて不正確な説明をするなら、説明から不必要な要素をそぎ落とすことを「オッカムの剃刀(Occam's razor)」という)。

 

 INDEX
  C# 4入門
  第2回 タスク並列ライブラリ
  1.単純に2コアを使う
    2.本当にコアを活用しているの?/foreach文をパラレルに/for文をパラレルに
    3.パラレルへの発想の転換/まとめ
 
インデックス・ページヘ  「C# 4入門」


Insider.NET フォーラム 新着記事
  • 第2回 簡潔なコーディングのために (2017/7/26)
     ラムダ式で記述できるメンバの増加、throw式、out変数、タプルなど、C# 7には以前よりもコードを簡潔に記述できるような機能が導入されている
  • 第1回 Visual Studio Codeデバッグの基礎知識 (2017/7/21)
     Node.jsプログラムをデバッグしながら、Visual Studio Codeに統合されているデバッグ機能の基本の「キ」をマスターしよう
  • 第1回 明瞭なコーディングのために (2017/7/19)
     C# 7で追加された新機能の中から、「数値リテラル構文の改善」と「ローカル関数」を紹介する。これらは分かりやすいコードを記述するのに使える
  • Presentation Translator (2017/7/18)
     Presentation TranslatorはPowerPoint用のアドイン。プレゼンテーション時の字幕の付加や、多言語での質疑応答、スライドの翻訳を行える
@ITメールマガジン 新着情報やスタッフのコラムがメールで届きます(無料)

注目のテーマ

Insider.NET 記事ランキング

本日 月間