#topicpath
-----
*マルチコアの検出方法の調査 (準備編) [#l89a7c14]

*完成図 [#ieadd902]

*はじめに [#b4f3acd6]

*対象読者 [#kfc30400]

*必要な環境 [#nf22e1b4]

-----
*背景:様々なマルチプロセッサ環境 [#u8ab28f1]
'05/11 現在、Intel 系の CPU には以下のマルチプロセッサ環境が存在しています。

+''SMP (Symmetric Multiprocessing)''~
1台のシステムに物理的な CPU を複数搭載することでマルチプロセッサ環境を実現しています。最も単純なマルチプロセッサ環境です。
+''SMT (Simultaneous Multi-Threading)''~
1つのCPU で複数のプロセッサとして振舞います。Intel は Hyper-Threading として SMT を実装しています。
+''Multi-core''~
1つのCPU に複数のプロセッサが搭載されています。AMD/Intel 社が採用している技術です。

現在のマルチプロセッサ環境は上記の技術が複数絡み合った状況になっています。

例えば Pentium 4 の最高峰である Pentium Processor Extreme Edition は 2.(x2) と 3.(x2) をサポートしており、計 4 つのプロセッサが使用できます。最近発表された Multi-core 対応 Xeon を 2 つ使用した構成の場合、1.(x2) と 2.(x2) と 3.(x2) となり合計 8 つのプロセッサが使用できます。

今回、CPUID 情報より上記の環境を判別する方法について調査しました。


*前準備:プロセッサ毎の CPUID 取得方法についての調査 [#oe6d284c]
マルチプロセッサ環境を判別するには各プロセッサ毎の CPUID の情報が必要です。[[GetProcessAffinityMask():http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/jpdllpro/html/_win32_getprocessaffinitymask.asp]] / [[SetProcessAffinityMask():http://www.microsoft.com/japan/msdn/library/default.asp?url=/japan/msdn/library/ja/jpdllpro/html/_win32_setprocessaffinitymask.asp]] という関数を使用することにより、実行中のプロセスを任意のプロセッサに設定することが可能のようですので、この関数を使用することにしました。

まず、本当に動作するのかについてイマイチ信用が無かったので確かにプロセッサが切り替わっているかどうかを視覚的に確認するためのサンプルプログラムを作成しました。

	#include <stdio.h>
	#include <windows.h>
	int main(int argc, char* argv[])
	{
		HANDLE hCP;
		DWORD pamask, samask, patmp = 0;
		DWORD dwStart;
		int i, numcpu = 0;
		hCP = GetCurrentProcess();
		if( GetProcessAffinityMask(hCP, &pamask, &samask) ){
			printf("GetProcessAffinityMask lpProcessAffinityMask=[%d], lpSystemAffinityMask=[%d]\n", pamask, samask);
		}else{
			printf("ERR:GetProcessAffinityMask [%d]\n", GetLastError());
			return -1;
		}
		if( pamask == 0 ){
			printf("ERR:pamask is 0\n");
			return -2;
		}
		numcpu = 0;
		for(;;){
			patmp = pamask>>numcpu;
			// no more processor?
			if( 0 == patmp ){
				numcpu = 0;
				continue;
			}
			// Is this processor work?
			if( !(patmp&0x1) ){
				numcpu++;
				continue;
			}
			// Change processor...
			if( !SetProcessAffinityMask(hCP, 1<<numcpu) ){
				break;
			}
			Sleep(1);
			// Looper!
			dwStart = GetTickCount();
			printf( "processor(%d) :", numcpu );
			for(i=0; i<(0x10000000*4); i++){
				if( !(i%0x1000000*4) ){
					printf(".");
				}
			}
			printf("\ncounter (%d)\n", GetTickCount() - dwStart);
			// continue...
			numcpu++;
		}
		return 0;
	}


このプログラムの重要な個所は一定回数のカウントループです。

上記のプログラムは CPU 資源を無尽蔵に奪い取ってしまう劣悪なプログラムの代表例です。今回は、現在使用しているプロセッサがどれなのかを目視で確認するためにわざとプロセッサの使用率を 100% まで上げ、タスクマネージャ上で確認できるようにしました。


以下はプロセッサが 4 つ見えている状態での出力結果です。確かに一定時間で順序良くプロセッサが切り替わっていることが目視出来ました。

	GetProcessAffinityMask lpProcessAffinityMask=[15], lpSystemAffinityMask=[15]
	processor(0) :................................................................
	counter (1547)
	processor(1) :................................................................
	counter (1547)
	processor(2) :................................................................
	counter (1578)
	processor(3) :................................................................
	counter (1547)
	processor(0) :................................................................
	counter (1547)
	processor(1) :................................................................
	counter (1547)
	processor(2) :................................................................
	counter (1594)
	processor(3) :................................................................
	counter (1531)




//
// コラム:x86 のマルチプロセッサ環境の足跡
//
*コラム1:x86 のマルチプロセッサ環境の足跡 [#f7a62a09]
単一のプロセッサしか扱えない Windows 9x/Me から、複数のプロセッサを扱うことの出来る
Windows NT/2000/XP への移行が進むにつれ、CPU もより多くのプロセスを同時に操作することを要求されるようになりました。

**SMP [#xa181180]
'95 年に発売された Pentium Pro は、1台のシステム上に複数の Pentium Pro を搭載することでより多くのプロセスを同時に処理することを可能にしました。

このような構成は、CPU の数の分だけコストが生じてしまうため、一般ユーザー向け PC としては普及しておらず、常に複数ユーザーの要求を同時にこなす必要のあるサーバー用途で用いられています。現在でも Xeon (Intel) や Opteron(AMD) 等が SMP として用いられています。

-[[wikipedia:対称型マルチプロセッサ]]
-[[en.wikipedia:Symmetric multiprocessing]]

**Hyper-Threading [#f5e50488]
'02年、Intel 社は Hyper-Threading(HTT) という技術を組み込んだプロセッサを出荷しています。

この技術は、1つのプロセッサ上であたかも複数のプロセッサとして振舞うこと
でパフォーマンスを向上させることが出来ます。多くの場合、プロセッサは常にフル活用されている訳ではなく、処理と処理の間に隙間がが生じています。HTT は、この使われていない隙間を仮想的なプロセッサに割り当てることで、メインのプロセッサの処理を落とすことなく
動作しています。Intel によると従来の Pentium 4 から 5% 程度のダイサイズ増加で HTT を実装することが出来、この技術を利用することで約 30 % のパフォーマンス増加が見込まれるとしています。現在、Pentium 4 processor の殆どに HTT が実装されています(Celeron シリーズ, Pentium D, Pentium M には実装されていません)。

-[[頭脳放談 −プロセッサ デザイン現場の舞台裏− 第16回 x86を延命させる「Hyper-Threading Technology」、その魅惑の技術:http://www.atmarkit.co.jp/fpc/rensai/zunouhoudan016/hyperthreading.html]] (@IT, 01.09.26)
-[[e-Words:Hyper-Threading]]


**Multi-core [#m73a49a0]
'05年、AMD/Intel 社は Multi-core(Dual-core) という技術を組み込んだプロセッサを出荷しています。

この技術は、1つの物理 CPU 上に複数(multi)のプロセッサ(core)を搭載しており、1つの CPU で SMP と似た環境を構築することが出来ます。この技術が開発された背景として、動作周波数を上げることによるパフォーマンス向上が限界に達してしまい、動作周波数を上げること以外にパフォーマンスを向上させる必要性に迫られたことが挙げられます。また Multi-core は、大幅な設計変更を行うことなく比較的実装しやすかったという面もあったようです。

サーバー用途以外にデスクトップユーザー向けにも Multi-core 技術が実装されています。

-[[e-Words:マルチコア]]

*コラム2:シングルプロセッサと Hyper-Threading と Multi-core の違いについての簡単な調査 [#m5cf3b1b]

どれも CPU 使用率を 100% にするような重いプロセスが 1 つしかない場合、大きな差は生じません。慢性的に重いプロセスが複数個存在した場合、パフォーマンスの違いが顕著に出ます。これを簡単に説明してみます。

上記の Looper を指定したプロセッサに負荷を与えるよう改造しました。

	#include <stdio.h>
	#include <windows.h>
	int main(int argc, char* argv[])
	{
		HANDLE hCP;
		DWORD pamask, samask, patmp = 0;
		DWORD dwStart;
		int i, numcpu = 0, curr = 0;
		if( argc < 2 ){
			printf("usage : looper [select processor number]\n");
			printf("(ex.) : looper 2\n");
			return -3;
		}
		curr = atoi(argv[1]);
		hCP = GetCurrentProcess();
		if( GetProcessAffinityMask(hCP, &pamask, &samask) ){
			printf("GetProcessAffinityMask lpProcessAffinityMask=[%d], lpSystemAffinityMask=[%d]\n", pamask, samask);
		}else{
			printf("ERR:GetProcessAffinityMask [%d]\n", GetLastError());
			return -1;
		}
		if( pamask == 0 ){
			printf("ERR:pamask is 0\n");
			return -2;
		}
		numcpu = 0;
		for(;;){
			patmp = pamask>>numcpu;
			// no more processor?
			if( 0 == patmp ){
				printf("ERR: not found current cpu number [%d]", atoi(argv[1]));
				return -4;
			}
			// Is this processor work?
			if( !(patmp&0x1) ){
				numcpu++;
				continue;
			}
			if( curr > 0 ){
				numcpu++;
				curr--;
				continue;
			}
			// Change processor...
			if( !SetProcessAffinityMask(hCP, 1<<numcpu) ){
				break;
			}
			Sleep(1);
			printf( "processor(%d) :\n", numcpu );
			// Looper!
			for(;;){
				dwStart = GetTickCount();
				for(i=0; i<(0x10000000*4); i++){
					if( !(i%0x1000000*4) ){
						printf(".");
					}
				}
				printf("counter (%d)\n", GetTickCount() - dwStart);
			}
		}
		return 0;
	}


ここで重要なのは counter の出力です。counter は一定回数の空ループを処理する時間を出力しています。よってこの値が小さければ小さいほど速く空ループを処理したことになり、一種のベンチマーク的役割を果たしています。

**シングルプロセッサと HTT 対応プロセッサの比較 (重いプロセスが1つの場合) [#t90d4cf9]
まず HTT 対応 CPU にて HTT有効・無効とで差があるか確認してみました(BIOS による切り替え)。

 HTT-ON (looper2 0) : 約 1344
 HTT-OFF(looper2 0) : 約 1344

重いプロセスが 1 つの場合では、HTT-ON と HTT-OFF とでは特に差は見られませんでした。

**シングルプロセッサと HTT 対応プロセッサの比較 (重いプロセスが2つの場合) [#h661c962]
次に重いプロセスが 2 つの場合、どうなるのでしょうか。コマンドプロンプトを 2 つ開き、各々 looper2 を実行します。

 HTT-ON (looper2 0) : 3047 (1つ目)
 ................................................................counter (1360)
 ................................................................counter (1593) <--- 2つめ実行開始 (looper2 1)
 ................................................................counter (3047)
 ................................................................counter (3047)

 HTT-ON (looper2 1) : 3000 (2つ目)
 ................................................................counter (3000)
 ................................................................counter (3000)
 ................................................................counter (3016)
 ................................................................counter (3000)

上記の結果より推測される事項は以下の通り。
-各プロセスに対し均等に CPU 使用率が配当されている
-理想値(1344*2)と比べると 1 割程度のパフォーマンスダウン?~
複数プロセスが動作していることやタスク切り替えで CPU パワーが消費されていると思われます。


同じ CPU で HTT を Off にしたらどうなるでしょうか。

 HTT-OFF (looper2 0) : 4765 (1つ目)
 ................................................................counter (1359)
 ................................................................counter (2563) <--- 2つめ実行開始
 ................................................................counter (4672)
 ................................................................counter (4765)

 HTT-OFF (looper2 0) : 1875 (2つ目)
 ................................................................counter (1875)
 ................................................................counter (1875)
 ................................................................counter (1906)

この結果より推測される事項は以下の通り。
-各々のプロセスに対しまばらに CPU 使用率が割り振られている~
HTT-ON と比べ counter の値は安定した数値を示しませんでした。今回は XP による結果を出力しましたが、w2k の場合、更に値がまばらになります。
-HTT-ON(3047+3000) と比べると 1割程度のパフォーマンスダウン~
重いプロセスが複数実行されている環境では、シングルプロセッサよりマルチプロセッサの方が優位であることが分かります。


**Multi-core の場合 [#u7503f6d]

それでは Multi-core の場合はどうでしょうか(CPUは「AMD Athlon(tm) 64 X2 Dual Core Processor 3800+」)。

 Multi-core-ON (1つ目)
 ................................................................counter (3234)
 ................................................................counter (3234)<--- 2つめ実行開始
 ................................................................counter (3250)
 ................................................................counter (3235)

 Multi-core-ON (2つ目)
 ................................................................counter (3219)
 ................................................................counter (3218)
 ................................................................counter (3219)

この結果より推測される事項は以下の通り。
-各プロセスに対し影響を与えない理想的なマルチプロセッサ環境~
2つのプロセスが何ら影響を与えることはありません。遜色のないパフォーマンスが得られています。



-----
*準備編のまとめ [#r4800516]


*参考資料 [#o25eaf60]

-[[プロセッサー・アフィニティーの管理:http://www-06.ibm.com/jp/developerworks/linux/051028/j_l-affinity.shtml]] (dW@jp)

-----


// [ [[edit>Edit:Windows Vista/News]] ]
//*仮置き場
//*お勧め
//**用語
//*News
//*関連情報
//**用語
//**Linux
//**Windows
//-----
//*[[フィードバック]]
//アナタからのフィードバックをお待ちしています。書き込む前に[[フィードバック]]の注意書きをお読みください。
//#comment