任意のCPUにスレッドを割り付ける(32/64bit)

icon 項目のみ表示/展開表示の切り替え

概要

マルチコアやハイパースレッド(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
}

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