日別アーカイブ: 2007年6月11日

CPU情報を調べる MultiCore/Thread

Windows 上でどれだけ CPU Core があっていくつの Thread をハードウエアで
同時に実行できるのか取得する方法を調べてみました。
CPU または Core の数だけなら

GetSystemInfo()

を使って取得することが可能です。また GetCurrentProcessorNumber() で現在の
Thread の Processor が識別できますし、割り当て可能な CPU のマスクは
GetProcessAffinityMask() を使って取得可能になっています。

ただしこれだけでは HyperThread なのか独立した CPU Core なのか区別が
付きません。そこまできちんと識別するには GetLogicalProcessorInformation()
を使います。
・MSDN GetLogicalProcessorInformation
ただしこの API がサポートされているのは Vista 以降か XP x64 Edition 以降、
Windows Server 2003 以降など比較的新しい OS に限定されているようです。

GetLogicalProcessorInformation() で得られた構造体リスト
SYSTEM_LOGICAL_PROCESSOR_INFORMATION
には、各プロセッサの状態や Numa の情報、Cache 情報なども含まれます。
Core の情報だけ調べるには

SYSTEM_LOGICAL_PROCESSOR_INFORMATION のメンバ Relationship の値が
enum 定数 RelationProcessorCore の場合だけ参照します。
このとき ProcessorMask に各 Core に対応する実行可能なハードウエアスレッド
が bitmask として格納されているので、この bit 位置を調べることで対応する
HyperThread がわかります。

(1) GetLogicalProcessorInformation() で複数の
  SYSTEM_LOGICAL_PROCESSOR_INFORMATION のリストを得る。

(2) リストのうち Relationship の値が RelationProcessorCore のものが
  本来の CPU Core に相当する。(HT含まず)
  ただし本当に CPU が複数のってるのか MultiCore なのかはここでは
  わからない。

(3) (2)の場合のみ SYSTEM_LOGICAL_PROCESSOR_INFORMATION の ProcessorMask
  を調べると、その Core が対応する CPU ID (同時実行可能な Thread 数)
  がわかる。

例えば HT 可能な Pentium4 CPU が 2つ乗ってる場合、同時に実行可能な
ハードウエアスレッドは 4つであり、GetSystemInfo() でも CPU が 4つある
ものとしてみなされます。

GetProcessAffinityMask() は 4つ分の bit が立った 0x0000000f を返します。

このとき GetLogicalProcessorInformation() で得られる Core 情報の構造体は
2つで、それぞれが

SYSTEM_LOGICAL_PROCESSOR_INFORMATION.ProcessorMask= 0x00000005;
SYSTEM_LOGICAL_PROCESSOR_INFORMATION.ProcessorMask= 0x0000000a;

の値をとります。このことから GetSystemInfo() や GetCurrentProcessorNumber()
で得られるプロセッサの番号は下記のように対応していることがわかります。

0= Core0 の HT0
1= Core1 の HT0
2= Core0 の HT1
3= Core1 の HT1

実際のコードにしてみるとこんな感じです。

static int GetThreadInformation( DWORD* cpuinfo )
{
 const int MAX_INFOBUFFER= 32;
 SYSTEM_LOGICAL_PROCESSOR_INFORMATION infobuffer[MAX_INFOBUFFER];

 DWORD retlen= 0;
 GetLogicalProcessorInformation( NULL, &retlen );
 if( retlen > sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION)*MAX_INFOBUFFER ){
  return 0; // error
 }

 if( !GetLogicalProcessorInformation( infobuffer, &retlen ) ){
  return 0; // error
 }

 SYSTEM_LOGICAL_PROCESSOR_INFORMATION *infop= infobuffer;
 int coreCount= 0;
 int cpuCount= 0;
 for( DWORD size= 0 ; size < retlen ;
    size+= sizeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION), infop++ ){

  switch( infop->Relationship ){
  case RelationProcessorCore:
   {
    int htCount= 0;
    for( int i= 0 ; i< 32 ; i++ ){
     if( infop->ProcessorMask & (1<       cpuinfo[i]= ( htCount++ << 8) + ( coreCount );
      cpuCount++;
     }
    }
    coreCount++;
   }
   break;
  default:
   break;
  }
 }
 return cpuCount;
}