コンピュータの物理CPUの数

アプリの一部でソフトリアルタイムな処理を行うとき、コンピュータに搭載されたCPUの数は非常に重要だ。なぜなら、リアルタイム処理用にCPUを一つ専有してしまうだけで、かなりの性能改善が見込めるから。もちろんそれでリアルタイム制約を満たせることの保証は何にもないが、まあ実用上十分な感じになる。

で、そのCPU数を取得するために Environment.ProcessorCount というプロパティが用意されているが、実はこれで取得できるのは「論理CPUの数」なのであまり良くない。論理CPUというのはIntel HT (Hyper-threading) などの SMT (simultaneous multi-threading) 技術によって仮想的に複製されたCPUのこと。物理的には1つしかない計算機構(CPUコア)で、ちょっとがんばって2つのスレッドを実行できる。

こいつは計算機のパワーを生かし切るという点では優れているが、実行時間を保証するという観点では非常によろしくない。一番ひどかった初期のHTでは、論理CPUの片方のプログラムが暴走すると、もう片方もお亡くなりになっていたりした。ダメ。

というわけで、「リアルタイム処理を実行するためにCPUを専有できるか」という問いに答えるためには、CPUコア数(物理的なCPUの数)が必要になる。これは GetSystemInformation というAPIで取得できる、らしい。

らしい、というのは、該当部分 (dwNumberOfProcessors) の説明が

The number of physical processors in the system. To retrieve the number of logical processors, use the GetLogicalProcessorInformation function.

となっているから。はっきり「物理CPU数です」とは述べていないものの、わざわざ「論理CPU数を取得するにはコレコレ使え」ということは、こちらで取れるのは物理CPU数だろう。(2010/8/10 という推測は間違いで、論理CPU数みたい。SYSTEM_INFO.dwNumberOfProcessors に注意を参照。)

で、GetSystemInformation だが、これはWin32 APIなので、C#から叩くときにはP/Invokeを使う。以下コード。まずP/Invokeの定義。

[DllImport("kernel32.dll", ExactSpelling = true)]
public static extern void GetSystemInfo(out SystemInfo ptmpsi);

構造体の定義。

[StructLayout(LayoutKind.Sequential)]
public struct SystemInfo {
public uint dwOemId;
public uint dwPageSize;
public IntPtr lpMinimumApplicationAddress;
public IntPtr lpMaximumApplicationAddress;
public uint dwActiveProcessorMask;
public uint dwNumberOfProcessors;
public uint dwProcessorType;
public uint dwAllocationGranularity;
public uint dwProcessorLevel;
public uint dwProcessorRevision;
}

これでマルチコア対応のアプリ開発も余裕です。