概要

マルチコアやハイパースレッド(HT)のCPUでシングルスレッドのプログラムを実行すると基本的には各CPUへ均等に割り当てられます。
実行CPUを固定すると、キャッシュやTLB等が有効に使われ実行速度の向上が期待できます。
本プログラムはOKボタンをクリックするとスレッドを作成し、そのスレッドの実行CPUを固定します。実行中に変更することもできます。
キャンセルボタンをクリックするとスレッドを終了させ、プログラムを終了します。

テスト環境

コンパイラ

Visual C++ 2008/2013 Express 32/64bit マルチバイト/UNICODE

実行環境

Windows XP Professional Service Pack 3 32bit(Pentium4)
Windows 7 Enterprise Service Pack 1 64bit(Core2Duo/Sandy Bridge/Sandy Bridge-E)
Windows 8.1 Enterprise 64bit(Arrandale)

プログラムソースの概要

WinMain

ダイアログボックスを表示します。

DlgProc

ダイアログボックスプロシージャーです。

WM_INITDIALOG

GetCPUMax関数により論理CPUの個数を取得します。
CPUを選択するラジオボタンはリソースファイルで1個のみ定義しています。
表示されているラジオボタンの位置をGetWindowRect APIにより取得しその位置大きさを元に、CreateWindow APIで論理CPU数-1の個数のラジオボタンを作成します。
作成されたラジオボタンの文字がリソースファイルで定義されているものより大きめになるので、WM_SETFONTメッセージをラジオボタンに送り文字の大きさを初期値にします。
ラジオボタンの作成が終わったら、CPU0に相当するラジオボタンにBM_SETCHECKメッセージを送りチェックを入れます。 本プログラムは、8個以上の論理CPUが存在する場合は、2段目以降にもCPU選択のラジオボタンが表示されます。ダイアログボックスの大きさが固定されていますので、32個以上は対応していません。

WM_COMMAND

IDOK
OKボタンをクリックすると発生するメッセージです。
EnableWindow APIによりOKボタンを無効にします。
CreateThread APIによりスレッドを作成し実行します。
SetThreadAffinityMask APIの第2引数でどのCPUに割り当てるかを指定します。
ビット0がCPU0、ビット1がCPU1といったぐらいにビットごとにCPU番号が決まっており、該当ビットを1にするとそのCPUに割り当てられます。
この例では、1<<1としておりこれは1を1回左へシフトしますので、2となり1ビット目が1となります。
以下に第2引数の例を示します。
0x3 // CPU0 CPU1に割り当て
0x80 // CPU7に割り当て
0xff // CPU0~CPU7に割り当て
1<<7 // CPU7に割り当て
IDCANCEL
キャンセルボタンをクリックすると発生するメッセージです。
end_f変数を1にセットし、スレッドに終了指示を与えます。
WaitForSingleObject APIによりスレッドが終了するまで待機します。
CloseHandle APIによりスレッド作成時に作成されたスタック等のデーターを解放します。
default
メッセージが発生したら、LOWORD(wPrama)をチェックし、ラジオボタンから発生したメッセージであるかをチェックします。
ラジオボタンであればID番号を取得し、CPUの番号に変換します。
スレッドが起動されていればSetThreadAffinityMask APIにより実行CPUを変更します。

hi_load

コンパイラの最適化によりループが削られないように終了指示用の変数はvolatile属性を付加しています。
end_fが0以外の値の時、スレッドを終了します。

GetCpuMax

GetSystemInfo APIにより論理CPUの個数を取得します。
例えば、Core i7-3820の場合、4コアでHT対応なので、論理CPU数は8個となります。

ソースコード

thread4.cpp

//	ダイアログボックスで指定した任意のCPUにスレッドを割り付ける 実行中も変更可能

#include <windows.h>
#include <tchar.h>
#include "resource.h"

#pragma comment(lib,"comctl32.lib")


//	論理CPU数を取得する

int GetCPUMax(void){
	SYSTEM_INFO sys;
	GetSystemInfo(&sys);
	return sys.dwNumberOfProcessors;
}

volatile int end_f = 0;	//	スレッドを終了させるときに0以外に設定

//	重負荷のスレッド

DWORD WINAPI  high_load(LPVOID lp){
	while (end_f == 0);
	return 0;
}

HINSTANCE hInst;

//	ダイアログボックスプロシージャー

LRESULT CALLBACK DlgProc(HWND hDlg, UINT msg, WPARAM wParam, LPARAM lParam){
	static int cpuMax;
	static HWND* hCpuv = 0;
	TCHAR buf[16];
	static HANDLE handle = 0;	//	スレッドハンドル
	static DWORD id;
	static int cpun = 0;

	switch (msg) {
	case WM_INITDIALOG:{
		cpuMax = GetCPUMax();
		hCpuv = new HWND[cpuMax];
		hCpuv[0] = GetDlgItem(hDlg, IDC_RADIOBOX000);
		int x1, y1, lx, ly;
		int id;
		RECT rect;
		POINT pt;
		GetWindowRect(hCpuv[0], &rect);
		pt.x = rect.left;
		pt.y = rect.top;
		ScreenToClient(hDlg, &pt);
		rect.left = pt.x;
		rect.top = pt.y;
		pt.x = rect.right;
		pt.y = rect.bottom;
		ScreenToClient(hDlg, &pt);
		rect.right = pt.x;
		rect.bottom = pt.y;

		x1 = rect.left;
		y1 = rect.top;
		lx = rect.right - rect.left;
		ly = rect.bottom - rect.top;
		id = IDC_RADIOBOX000;
		int cmax = 8;	// スレッド最大列数
		int x = 0;
		int y = 0;
		for (int n = 0; n < cpuMax; n++){
			if (0 < n){
				_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("%i"), n);
				hCpuv[n] = CreateWindow(
					TEXT("BUTTON"), buf,
					WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON,
					x1 + (lx)*x, y1 + ly*y, lx, ly,
					hDlg, (HMENU)(id + n), ::hInst, NULL
				);
				SendMessage(hCpuv[n], WM_SETFONT, (WPARAM)GetStockObject(DEFAULT_GUI_FONT), MAKELPARAM(true, 0));
			}
			if (cmax <= ++x){
				x = 0;
				++y;
			}
		}
		SendMessage(hCpuv[0], BM_SETCHECK, BST_CHECKED, 0);
		_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("CPU %i"), cpun);
		SetWindowText(GetDlgItem(hDlg, IDC_LABEL), buf);
		return TRUE;
	}
	case WM_COMMAND:
		switch (LOWORD(wParam)) {
		case IDOK:{
			EnableWindow(GetDlgItem(hDlg, IDOK), FALSE);
			handle = CreateThread(0, 0, &high_load, 0, 0, &id);	//	スレッドを作成
			SetThreadAffinityMask(handle, 1 << cpun);	//	CPUを固定
			return TRUE;
		}
		case IDCANCEL:{
			end_f = -1;
			if (handle){
				WaitForSingleObject(handle, INFINITE); // スレッド終了を待つ
				CloseHandle(handle);
			}
			EndDialog(hDlg, TRUE);
			return TRUE;
		}
		default:
			if (IDC_RADIOBOX000 <= LOWORD(wParam) || LOWORD(wParam) <= IDC_RADIOBOX000 + cpuMax - 1){
				cpun = LOWORD(wParam) - IDC_RADIOBOX000;	//	ビット番号
				_stprintf_s(buf, sizeof(buf) / sizeof(TCHAR), _TEXT("CPU %i"), cpun);
				SetWindowText(GetDlgItem(hDlg, IDC_LABEL), buf);
				if (handle)
					SetThreadAffinityMask(handle, 1 << cpun);	//	CPUを固定
			}
			return FALSE;

		}
	default:
		return FALSE;
	}
	return TRUE;
}



int WINAPI WinMain(HINSTANCE hCurInst, HINSTANCE hPrevInst, LPSTR lpsCmdLine, int nCmdShow){
	::hInst = hCurInst;
	//	ダイアログボックスの表示
	DialogBox(hCurInst, TEXT("DLG1"), 0, (DLGPROC)DlgProc);

	return 0;
}

resource.h

#define	IDC_LABEL		100
#define	IDC_RADIOBOX000	200

resource.rc


#include <windows.h>
#include	"resource.h"


DLG1 DIALOG DISCARDABLE 0, 0, 238, 104
EXSTYLE WS_EX_DLGMODALFRAME
STYLE WS_POPUP | WS_CAPTION | WS_SYSMENU | WS_MINIMIZEBOX | DS_SETFONT
CAPTION "thread4"
FONT 9, "MS 明朝"
{
	CONTROL "割当CPU", IDC_LABEL, "STATIC", WS_CHILD | WS_VISIBLE | SS_NOTIFY, 7, 7, 48, 15

	CONTROL "0", IDC_RADIOBOX000, "BUTTON", WS_CHILD | WS_VISIBLE | BS_AUTORADIOBUTTON, 7, 21, 28, 12
 CONTROL "OK", IDOK, "BUTTON", WS_CHILD | WS_VISIBLE | BS_DEFPUSHBUTTON, 58, 83, 54, 14
 CONTROL "キャンセル", IDCANCEL, "BUTTON", WS_CHILD | WS_VISIBLE | BS_PUSHBUTTON, 126, 83, 54, 14
}

実行ファイルとソースファイルのダウンロード