山本ワールド
Windowsプログラミング
アルゴリズム Vitual C++ 2008/2013によるWin32/Win64 APIレベルのプログラム 基礎 Vitual C++ 2008/2013によるAPIレベルのプログラム(32/64bit) Wix3でインストーラーを作る Visual C++ 2008 Standard Editonによるフォームアプリケーションのプログラム(32/64bit) Vitual C++ 2008 Standard EditonによるAPIレベルのプログラム(32/64bit) Windows 7対応 Visual C++ 2008 ExpressによるAPIレベルのプログラム Visual C++ 2005 ExpressによるAPIレベルのプログラム Visual C++ Versiosn 5 BORLAND C++ Windowsプログラム全般 Excel VBA その他任意のCPUにスレッドを割り付ける(32/64bit)
概要
マルチコアやハイパースレッド(HT)のCPUでシングルスレッドのプログラムを実行すると基本的には各CPUへ均等に割り当てられます。
実行CPUを固定すると、キャッシュやTLB等が有効に使われ実行速度の向上が期待できます。
本プログラムはOKボタンをクリックするとスレッドを作成し、そのスレッドの実行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
}
Copyright (C) 2012 山本ワールド All Rights Reserved.