在某些情況下,為了提升程序的執行效率,需要將程序綁定到指定的cpu核心上。根據微軟官方文檔,windows提供了兩個win32函數:setthreadaffinitymask和setprocessaffinitymask,用于設置指定線程或進程的處理器關聯掩碼,從而實現線程或進程對處理器的綁定。
SetThreadAffinityMask函數用于設置指定線程的處理器關聯掩碼,使線程在指定的CPU核心上運行。其函數定義如下:
DWORD_PTR SetThreadAffinityMask( [in] HANDLE hThread, [in] DWORD_PTR dwThreadAffinityMask);
該函數需要傳遞兩個參數:
- hThread:指向要設置處理器關聯的線程句柄。如果要設置當前線程的關聯掩碼,可以使用GetCurrentThread()函數獲取句柄。
- dwThreadAffinityMask:處理器的關聯掩碼,是一個DWORD_PTR類型的值,長度為8個字節(64位),每個bit代表一個CPU核心。例如,要將線程綁定到第0個核心,則dwThreadAffinityMask = 0B_0001(0x01);綁定到第1個核心,則dwThreadAffinityMask = 0B_0010(0x02);綁定到第2個核心,則dwThreadAffinityMask = 0B_0100(0x04);綁定到第3個核心,則dwThreadAffinityMask = 0B_1000(0x08)。如果要綁定到多個核心,比如第1和第2個核心,則dwThreadAffinityMask = 0B_0110,對應的十六進制數為0x06。
調用示例首先需要引入Win32 API:
[DllImport("kernel32.dll")]static extern UIntPtr SetThreadAffinityMask(IntPtr hThread, UIntPtr dwThreadAffinityMask);[DllImport("kernel32.dll")]static extern IntPtr GetCurrentThread();
由于dwThreadAffinityMask的值是按照2的n次冪遞增,與通常習慣指定第n個核心不符,并且不同設備的CPU核心數不一樣,指定CPU核心時可能超出CPU核心數量,因此可以對指定CPU核心做簡單的處理:
static ulong SetCpuID(int lpIdx){ ulong cpuLogicalProcessorId = 0; if (lpIdx >= System.Environment.ProcessorCount) { lpIdx = 0; } //通過移位運算轉換lpIdx->dwThreadAffinityMask:0->1,1->2,2->4,3->8,…… cpuLogicalProcessorId |= 1UL << lpIdx; return cpuLogicalProcessorId;}
接下來可以進行測試:
ulong LpId = SetCpuID((int)lpIdx);SetThreadAffinityMask(GetCurrentThread(), new UIntPtr(LpId));Stopwatch stopwatch = new Stopwatch();stopwatch.Start();for (int i = 0; i < 100000000; i++){ // 執行一些操作}stopwatch.Stop();Console.WriteLine("Elapsed time: " + stopwatch.ElapsedMilliseconds + " ms");
效果如圖所示:
SetProcessAffinityMask函數與SetThreadAffinityMask非常相似,不同之處在于其作用于整個進程,可以決定進程內的所有線程共同運行在指定的處理器上。其函數定義如下:
BOOL SetProcessAffinityMask( [in] HANDLE hProcess, [in] DWORD_PTR dwProcessAffinityMask);
和SetThreadAffinityMask一樣,也需要傳遞兩個參數,只不過第一個參數傳遞的是進程的句柄。
總結:在某些場景下,可以通過SetThreadAffinityMask和SetProcessAffinityMask提高程序執行效率,主要基于以下幾個原因:
- 提高性能:通過將線程綁定到特定的處理器,可以減少線程在不同處理器之間的切換開銷,尤其是在多核系統中,有助于提升程序的執行效率。
- 避免緩存抖動:確保線程始終在同一個處理器上運行,可以減少緩存未命中,因為相關的數據更可能保留在該處理器的高速緩存中。
- 實時系統和并發控制:在需要嚴格控制線程執行位置的場景下,比如實時系統或者某些并發控制策略中,通過設定處理器關聯可以滿足特定的調度需求。
需要注意的是,SetThreadAffinityMask和SetProcessAffinityMask并不是獨占CPU核心,如果關聯的CPU核心本身負載就很高,這個時候程序執行效率反而會降低。