Windows應用程式開發者的CPU設定入門:優先順序・親和性・P核心/E核心

· · Windows, Windows應用程式, CPU, 效能, 優先順序, 親和性, P核心, E核心, 省電, EcoQoS

Windows應用程式的效能,不是光靠程式碼就能決定的。即使是同一個 .exe,在不同的電腦上執行,體感也可能不同。

在某台電腦上,週期性處理很穩定。
在另一台電腦上,偶爾會延遲。
接AC電源時沒問題,但用電池時卻莫名其妙變慢。
在工作管理員裡提高了優先順序,卻沒有想像中變快。
以為已經導向P核心,處理時間卻還是在晃動。
反過來,想拉高效能結果,風扇一直轉不停,其他應用程式也變得卡頓。

在開發Windows應用程式時,常會遇到這種「光靠程式碼很難解釋的現象」。

這時候該看的,是這4件事。

要看的東西 簡單來說
優先順序 讓哪個執行緒先動
親和性 允許在哪些CPU上執行
P核心 / E核心 這顆CPU偏效能還是偏省電
省電設定 CPU會被允許多認真地轉

而在現代Windows中,還要再加上 EcoQoS 以及工作管理員的 Efficiency mode

也就是說,Windows應用程式的執行環境,不是單純的「CPU快還是慢」。

什麼時候執行
在哪裡執行
用哪種核心執行
CPU目前處於哪種效能狀態
作業系統認為這是「重視效能」,還是「省電就好」

這些因素組合起來,才決定了實際的回應性與處理時間。

本文將整理Windows應用程式開發中容易被忽略的優先順序、親和性、P核心/E核心、省電設定之間的關係。

順帶一提,本文中出現的程式碼,已經以可建置、可執行的範例(處理優先順序與親和性的 C# 函式庫與示範程式、PowerShell 腳本、單元測試)整套公開在 GitHub 上。

windows-app-cpu-priority-affinity-power - komurasoft-blog-samples (GitHub)

1. 先看整體架構

首先,把整體架構畫成圖大概是這樣。

應用程式的程式碼Windows 排程器優先順序多容易被排入執行親和性 / CPU Sets允許在哪些CPU上執行QoS / EcoQoS偏重效能或偏重省電選擇要執行的執行緒選擇要執行的邏輯處理器P核心 / E核心偏效能還是偏省電電源模式 / 電源計劃 / PPM頻率・Boost・Core Parking實際的回應性處理時間發熱電池消耗對其他應用程式的影響

重要的是,這些因素並不是各自獨立的。

提高優先順序,確實會更容易被排入執行。
但如果CPU本身被控制成偏省電,就可能沒有預期的那麼快。

用親和性限制CPU,執行緒的移動或許會減少。
但如果限制後落在偏省電的核心,或是與Core Parking、電源管理不協調,反而可能造成不利。

把負載導向P核心,計算處理或許會變快。
但若把所有東西都導向高效能側,發熱、風扇噪音、電池消耗、對其他行程的影響都會增加。

Windows應用程式的效能調校,不是單純「找一個能變快的按鈕」。

想讓哪個處理變快?
哪個處理慢一點也沒關係?
要優先使用者操作的回應性,還是背景處理的完成時間?
電池與發熱可以容忍到什麼程度?

這些都屬於設計層面的問題。

2. 優先順序決定的是「多容易被排入執行」

在Windows中,當有多個可執行的執行緒時,排程器要決定「接下來讓哪個執行緒上CPU」。這時發揮作用的就是優先順序。

優先順序大致上分兩個層級決定。

行程的優先順序類別
  + 執行緒的相對優先順序
  = 執行緒的基礎優先順序

以Win32 API來說,行程側有 SetPriorityClass,執行緒側有 SetThreadPriority

若要用PowerShell查看目前執行中行程的優先順序,例如可以這樣寫。

Get-Process -Id $PID | Select-Object Id, ProcessName, PriorityClass

要提高目前PowerShell行程的優先順序,可以這樣做。

$p = Get-Process -Id $PID
$p.PriorityClass = "AboveNormal"

用C#的話,可以這樣寫。

using System.Diagnostics;

using var process = Process.GetCurrentProcess();
process.PriorityClass = ProcessPriorityClass.AboveNormal;

這裡要注意的是,優先順序並不是「讓CPU變快的設定」。優先順序影響的,是有競爭時該讓哪個執行緒先動。

它不是提高CPU頻率的設定。
也不是選擇P核心的設定。
也不是加快I/O的設定。
更不是消除鎖等待或網路等待的設定。

因此,「提高了優先順序卻沒有變快」是很常見的事。舉例來說,如果變慢的原因是以下這些,光提高優先順序並無法從根本解決。

  • 磁碟I/O等待
  • 網路等待
  • DB回應等待
  • 鎖競爭
  • GC停頓
  • UI執行緒被阻塞
  • GPU或驅動程式側的等待
  • 防毒軟體造成的檔案掃描
  • CPU頻率被壓低在省電側

另外,HIGH_PRIORITY_CLASSREALTIME_PRIORITY_CLASS 不是能隨意使用的設定。

長時間以高優先順序持續運作的執行緒,會不容易把CPU時間讓給其他執行緒。
如果只是自己應用程式內的問題還好,但這有時會惡化整個系統的回應性。

優先順序就像藥物一樣。

在有效的場合確實有效。
但不是用量越多越好。

提高優先順序之前要先想的事

在提高優先順序之前,有些事要先想清楚。

問題 該看的東西
那個處理真的是在等CPU嗎 CPU使用率、ETW、剖析器
是不是堵住了UI執行緒 UI回應、非同步化、佇列設計
提高優先順序的範圍夠短嗎 暫時提高,結束後還原
會不會打擾到其他應用程式 輸入、列印、瀏覽器、常駐軟體受到的影響
在客戶環境中會不會被權限或政策擋住 系統管理員權限、執行使用者、資安產品

對Windows應用程式來說,重要的不是一直用最高優先順序執行,而是在必要的時機,只把必要的處理,優先到必要的範圍。

3. 親和性決定的是「允許在哪些CPU上執行」

如果優先順序是「多容易被排入執行」,那麼親和性就是「允許在哪些CPU上執行」。

在Windows中,可以對行程或執行緒指定一組允許使用的邏輯處理器。

以Win32 API來說,對行程有 SetProcessAffinityMask,對執行緒有 SetThreadAffinityMask

若要用PowerShell查看行程的親和性,例如可以這樣寫。

Get-Process -Id $PID | Select-Object Id, ProcessName, ProcessorAffinity

為了驗證目的,把目前的PowerShell行程限制在前4個邏輯處理器,可以這樣寫。

$p = Get-Process -Id $PID
$p.ProcessorAffinity = [IntPtr]0xF

0xF 用二進位表示是 1111
也就是說,代表允許使用邏輯處理器0~3。

不過,這僅僅是驗證用的範例。

限制親和性,有時能改善CPU快取的局部性,也能減少週期性處理的抖動。
但另一方面,這也會把原本Windows能疏導到閒置CPU的執行緒,關進一個狹窄的空間裡。

尤其在以下這類環境中,要特別注意。

  • P核心/E核心混合存在
  • SMT/超執行緒導致邏輯處理器與實體核心的對應不直觀
  • NUMA架構
  • 超過64個邏輯處理器而涉及Processor Group
  • 啟用了Core Parking
  • OEM或BIOS的電源控制很強勢
  • 執行於虛擬化環境中

親和性是對系統強力施加「只能用這些CPU執行」限制的設定。

限制可以是穩定化的工具。
但用錯了,就會堵死系統的退路。

現在還有CPU Sets

傳統的親和性遮罩,是相當強的限制。

而Windows還有一個叫 CPU Sets 的機制。
CPU Sets是讓應用程式端能以更柔性的方式,把CPU的希望傳達給系統的API。

依照Microsoft的說明,CPU Sets被定位為一種與作業系統電源管理相容的「soft」親和性指定方式。

究竟是想嚴格固定CPU?
還是希望在與作業系統的電源管理、排程協調的前提下,大致把執行位置導向某處?

這個差異很重要。

現代的Windows應用程式,不該只靠傳統的 SetProcessAffinityMask 解決所有問題,而必須把CPU Sets與QoS也一起納入考量。

4. P核心/E核心:「CPU也是有個性的」

在近期的CPU中,並非所有核心的效能與耗電量都相同。代表性的例子就是P核心與E核心,簡單來說,P核心偏效能,E核心偏效率。

種類 擅長的事
P核心 低延遲、高單體效能、繁重的前景處理
E核心 省電、背景處理、並行處理的承接方

不過,開發者若安易地認定「CPU 0~7是P核心,8~15是E核心」是很危險的。CPU編號的排列,會隨CPU型號、BIOS、Windows版本、韌體、OEM設定、虛擬化環境而改變。

在Windows端,CPU Sets的資訊中有個叫 EfficiencyClass 的概念。
這是在具有異質處理器的系統中,用來表示該CPU Set效率特性的一個值。依Microsoft的文件說明,這個值越高,代表該CPU Set擁有速度更快、但電源效率較低的處理器。

若想妥善運用P核心/E核心,僅看CPU編號是不夠的。若要真正深入處理,需要以下觀察。

  • 用CPU Sets API查看 EfficiencyClass
  • 用Windows Performance Recorder / Analyzer觀察執行緒的執行CPU
  • 用工作管理員的邏輯處理器畫面觀察趨勢
  • 在實機上逐一測量處理時間
  • 比較AC電源與電池時的差異
  • 更換電源模式後比較差異

P核心/E核心不只是硬體規格,而是與Windows的排程器、QoS、省電設定組合後,才決定實際的執行位置。

5. 省電設定:關乎「CPU會被允許多認真地轉」

這一點,在實務上相當重要。

優先順序是「讓哪個執行緒先動」。
親和性是「允許在哪些CPU上執行」。
P核心/E核心是「這顆CPU偏效能還是偏省電」。

而省電設定,關乎的是

CPU究竟被允許在什麼樣的頻率、電力狀態下運作

也就是說,會發生這樣的事。

提高了優先順序。
導向了P核心。
但如果電源設定偏向省電,
CPU或許不會認真地轉。

這是相當有Windows特色的一件事。

在Windows 11中,可以從設定應用程式的「系統 > 電源與電池」選擇電源模式。
標示會因環境或版本而不同,但方向大致如下。

電源模式 方向
最高電源效率 / Best power efficiency 優先電池與省電
平衡 / Balanced 效能與電力的平衡
最佳效能 / Best performance 優先效能

此外,傳統上還有電源計劃:Power SaverBalancedHigh Performance
Balanced 會依需求調整效能與耗電,High Performance 則是傾向以耗電為代價換取最大效能的設定。

使用者能看到的設定看似簡單,但背後有Processor Power Management,也就是PPM的設定。

6. P-state、C-state、Boost、EPP

CPU並不是永遠以最大頻率運作,它有一些用來降低耗電的狀態。

用語 簡單來說
P-state 改變CPU頻率與電壓的效能狀態
C-state 閒置時停止CPU部分功能的省電狀態
Boost 條件符合時,進入超出額定值的高效能狀態的機制
EPP Energy Performance Preference。偏重效能或偏重省電的傾向

P-state是透過改變CPU的頻率與電壓來降低耗電的機制。
C-state是CPU閒置時停止部分功能,進入更深省電狀態的機制。

Windows的電源管理,會利用這些機制在效能與耗電之間取得平衡。

因此,「明明是高優先順序卻很慢」這種現象是有可能發生的。

執行緒被優先執行。
但CPU頻率偏低。
不容易進入Boost。
EPP偏省電。
Core Parking限制了可用的核心。
電池驅動下整個作業系統偏向省電運作。

在這種情況下,只看優先順序找不到原因。

尤其在週期性處理、影像處理、量測儀器控制、影片處理、音訊處理、USB相機擷取、序列通訊等場景中,不只是平均處理時間,「偶爾延遲」本身就是個問題。

平均很快。
但1000次裡有1次會延遲。
就這一次讓緩衝區堵住。
UI凍結。
與裝置間的時序偏移。

在這類現象中,光看CPU使用率的平均是不夠的。

需要一起檢視處理時間的分佈、最大值、離群值、電源狀態、實際執行的CPU、背景負載。

7. Core Parking:關乎「可用核心的數量」

Windows有一個叫Core Parking的機制。這是一種讓未使用的邏輯處理器休息的控制方式。
在使用率低時,把部分核心導向低功耗狀態,藉此抑制耗電。

依Microsoft的資料說明,CPMinCores 是用來指定在任意時間點,至少要有百分之幾的邏輯處理器保持un-parked,也就是可使用狀態的設定。
若設為100%,Core Parking演算法就會被停用。

這裡會出問題的,是與親和性的組合。

用親和性限制了「希望在這群CPU上執行」。
但在電源管理上,那些核心究竟被如何對待,是另一個問題。

在針對Windows Server的資料中也說明,若活躍的執行緒被強力指定親和性到NUMA節點內的一部分CPU,有時會與Core Parking的判斷不協調。

這在客戶端電腦上,作為思考方式同樣值得參考。

親和性對排程器施加限制。
Core Parking則關乎電源管理「讓哪些核心保持可用狀態」。

若把這兩者分開來想,就會誤判原因。

8. EcoQoS / Efficiency Mode:告訴系統「這個處理省電就好」的機制

過去,Windows應用程式的效能調校,主要圍繞在提高或降低優先順序、改變親和性這類話題。但現代Windows已經有了另一種思考方式,那就是QoS。

QoS,Quality of Service,是一種用來表示「這個處理有多重視效能,或有多重視省電」的概念,套用在執行緒上。

依Microsoft的文件說明,排程優先順序仍然是決定接下來執行哪個執行緒的主要指標,而QoS則可能影響核心選擇與處理器電源管理。

尤其是 EcoQoS,是一種讓效能不是最重要的處理,能被導向省電側的機制。

用C++的話,例如可以用 SetThreadInformationThreadPowerThrottling,把目前的執行緒設為EcoQoS。

#include <windows.h>

void EnableEcoQoSForCurrentThread()
{
    THREAD_POWER_THROTTLING_STATE powerThrottling = {};
    powerThrottling.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
    powerThrottling.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
    powerThrottling.StateMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;

    SetThreadInformation(
        GetCurrentThread(),
        ThreadPowerThrottling,
        &powerThrottling,
        sizeof(powerThrottling));
}

反過來,若要還原為效能優先,可以對同一個控制目標將 StateMask 設為0。

void DisableEcoQoSForCurrentThread()
{
    THREAD_POWER_THROTTLING_STATE powerThrottling = {};
    powerThrottling.Version = THREAD_POWER_THROTTLING_CURRENT_VERSION;
    powerThrottling.ControlMask = THREAD_POWER_THROTTLING_EXECUTION_SPEED;
    powerThrottling.StateMask = 0;

    SetThreadInformation(
        GetCurrentThread(),
        ThreadPowerThrottling,
        &powerThrottling,
        sizeof(powerThrottling));
}

EcoQoS並不是一種「什麼都設成省電就好」的功能。適合的,是像下面這類處理。

  • 背景同步
  • 低優先的索引建立
  • 不急的日誌彙總
  • 與使用者操作沒有直接關係的快取更新
  • 晚一點結束也無妨的維護處理

另一方面,以下這類處理則應謹慎使用。

  • 直接關係到UI操作的處理
  • 相機擷取
  • 音訊處理
  • 關乎控制週期的處理
  • 檢測裝置的判定處理
  • 使用者正在等待的匯出處理
  • 需要接近即時性的回應處理

工作管理員的 Efficiency mode 也與這個概念相關。
在Microsoft的Performance Diagnostics部落格中說明,啟用Efficiency mode後,會把行程的基礎優先順序降為Low,並將QoS設為EcoQoS。

也就是說,Efficiency mode並不只是單純的「省電圖示」,而是結合低優先順序化與EcoQoS,用來保護前景應用程式回應性與電源效率的機制。

從開發者的角度來看,這相當重要。

因為除了「希望這個處理快點結束」之外,也能設計成
「這個處理只要在不打擾使用者的範圍內運作就好」
的方式,把意圖傳達給作業系統。

9. 判斷流程圖

到這裡的內容相當複雜,這裡整理一份調查Windows應用程式效能或週期抖動時的判斷流程。

不是 / 不確定不是不是不是不是症狀慢・週期抖動・UI凍結・風扇很吵先記錄處理時間 / 最大值 / CPU使用率 / 電源狀態 / AC或電池 / 目標電腦CPU是主要原因嗎?確認I/O・鎖・DB・網路・GC・GPU・驅動程式在動優先順序或親和性之前,先消除等待時間與其他行程的CPU競爭很強嗎?考慮優先順序但要限定範圍避免長期使用High/Realtime是否偏向特定CPU?確認親和性 / CPU Sets是否固定得太多有沒有看P/E核心與NUMA頻率・電源狀態可疑嗎?確認電源模式 / 電源計劃 / PPM懷疑P-state / EPP / Boost / Core Parking背景處理正在打擾前景嗎?考慮EcoQoS / Efficiency Mode / 降低優先順序把不急的處理導向省電側重新檢視演算法・並行度・佇列設計・UI執行緒設計一次改一項並進行A/B測量不只看平均也要看最大值・離群值・發熱・對其他應用程式的影響

這個流程中重要的是,不要一開始就亂動設定。先記錄。

  • 是一直慢,還是偶爾慢
  • 是AC電源還是電池
  • 電源模式是什麼
  • 工作管理員裡是不是已經進入Efficiency mode
  • CPU使用率高不高
  • 頻率有沒有拉高
  • 哪個執行緒在用CPU
  • 處理時間的最大值大概多少
  • 有沒有其他常駐軟體或防毒軟體有關

然後,一次只改一項。

改優先順序。
改親和性。
改電源模式。
加上EcoQoS。
分離背景處理。
加入佇列。
從UI執行緒中拿出來。

同時改多項,就無法知道到底哪個有效。

10. 現場常用的確認指令

當Windows應用程式的行為隨環境不同而不同時,先讓自己能取得狀態資訊會很方便。

查看CPU資訊

Get-CimInstance Win32_Processor |
  Select-Object Name, NumberOfCores, NumberOfLogicalProcessors, MaxClockSpeed

查看目前啟用的電源計劃

powercfg /getactivescheme

查看處理器電源管理的設定

powercfg /q SCHEME_CURRENT SUB_PROCESSOR

輸出相當長,但可以確認最小・最大處理器狀態、EPP、Boost、與Core Parking相關的設定。
可看到的項目會因環境而異。

產生電源效率診斷報告

在具系統管理員權限的終端機中執行。

powercfg /energy

經過一段時間的量測後,會輸出HTML報告。
可以作為觀察驅動程式、裝置、計時器解析度、USB省電、阻止睡眠等問題的入口。

查看目標行程的優先順序與親和性

Get-Process -Name MyApp |
  Select-Object Id, ProcessName, PriorityClass, ProcessorAffinity

查看CPU的效能狀態

依環境不同,可用的計數器名稱會改變,但以下這類Performance Counter可作為參考。

Get-Counter '\Processor Information(_Total)\% Processor Performance'
Get-Counter '\Processor Information(_Total)\% Processor Utility'

若要更深入地查看,用Windows Performance Recorder與Windows Performance Analyzer擷取ETW會更確實。
可以一併追蹤週期抖動、內容切換、實際執行的CPU、DPC/ISR、磁碟I/O、CPU頻率變化。

11. 在軟實時處理中,要整體一起考慮

Windows並不是一般意義上的實時作業系統。但在實務的Windows應用程式中,有時會被要求接近實時的特性。

  • 以固定週期從USB相機取得影像
  • 以序列通訊與裝置交換資料
  • 從量測儀器讀取資料
  • 與PLC或外部裝置同步
  • 處理音訊或影片
  • 在一定時間內回傳檢測結果
  • 在不凍結UI的情況下執行繁重的計算

在這類處理中,光靠程式碼速度是不夠的。

需要執行緒設計。
需要佇列設計。
需要日誌。
需要逾時處理。
需要反壓(backpressure)。
需要決定延遲時要捨棄、等待,還是重試。

而在這之上,還需要檢視優先順序、親和性、P核心/E核心、省電設定。

舉例來說,來想想相機擷取執行緒。

這個執行緒與使用者操作相關,也具有週期性。
或許不該設為EcoQoS。
稍微提高優先順序或許有價值。
但提高過頭,會對UI或其他處理造成不良影響。
固定親和性或許會更穩定。
但如果固定的位置偏向E核心,反而可能造成反效果。
如果電源模式偏向省電,可能只有在電池模式下才會延遲。

另一方面,來想想舊日誌的壓縮處理。

這或許是使用者不在等待的處理。
那麼,就降低優先順序。
設為EcoQoS。
在閒置時執行。
只在AC電源時執行。
這樣的設計,對整個應用程式反而更友善。

不是把所有處理都變「快」,而是把該變快的處理,與不該打擾別人的處理分開來。

12. 設計的基本方針

在處理這些議題時,有6項基本方針。

1. 不要一開始就固定

一開始不要強力固定優先順序或親和性比較好。

Windows的排程器,多數情況下都做得相當好。
應用程式端加上多餘的限制,反而可能讓事情變糟。

先正常地做。
測量。
出現問題時,建立假設。
小幅度修改。
再測量。

依這個順序進行。

2. 優先順序要暫時使用

高優先順序,只用在必要的區間。

比起一直維持高優先順序,

在重要處理開始前才提高
結束後就還原

這樣的做法更安全。

3. 親和性放到後面再考慮

親和性是很強的設定。

在懷疑是週期抖動或CPU移動造成影響時,有時很有效。
但它不是最先該動的設定。

尤其若客戶端有多種機型,安易地固定CPU編號是危險的。

4. P核心/E核心:不是「認定」,而是「觀察」

若想妥善運用P核心/E核心,要先在實機上觀察。

不要用CPU編號認定,而要用CPU Sets、EfficiencyClass、ETW、實測來確認。

5. 以省電設定為前提進行測試

「開發機是AC電源、高效能設定,所以很快」,光靠這樣是不夠的。

在客戶端環境,以下狀況是很常見的。

  • 筆記型電腦的電池驅動
  • Best power efficiency
  • Energy saver
  • OEM自家的省電工具
  • 企業政策所固定的電源設定
  • 因發熱而效能下降的輕薄筆電
  • 常駐軟體很多的業務用終端

效能測試時,至少要分開來看以下這幾種條件。

條件 要看的東西
AC + Best performance 接近最大效能的狀態
AC + Balanced 標準的業務使用情境
Battery + Balanced 筆記型電腦的現實情況
Battery + Best power efficiency 偏省電的下限
長時間連續執行 發熱、風扇、熱降頻
同時使用其他應用程式 與瀏覽器、Teams、Excel、防毒軟體共存

6. 背景處理可考慮使用EcoQoS

與使用者體驗沒有直接關係的處理,有時優先「不打擾」比追求效能更好。

用高效能運行一切的應用程式,或許很快。
但會打擾其他工作。
會讓筆記型電腦的風扇轉個不停。
會消耗電池。
結果反而變成一個不好用的應用程式。

對Windows應用程式來說,速度以外,「共存的好壞」也是一種品質。

13. 常見的誤解

提高優先順序就會變快

不一定會變快。

如果原因是CPU競爭,有時確實有效。
但如果原因是I/O等待、鎖等待、省電導致的低頻率、在E核心上執行等,光靠優先順序是不夠的。

固定在P核心永遠是正解

不永遠是正解。

P核心效能高,但發熱與耗電也會增加。
而且也可能與其他重要的前景處理發生競爭。

把該導向P核心的處理,與E核心就足夠的處理分開來,才是更重要的事。

省電設定是使用者的偏好,與應用程式無關

有關係。

同一個應用程式,也會因電源模式、電源計劃、EPP、Boost、Core Parking而執行方式不同。

尤其在筆記型電腦上,AC電源與電池的行為會不一樣。

Efficiency mode只是單純變慢

不只是單純變慢。

它是透過降低優先順序與EcoQoS,來保護前景應用程式回應性與電源效率的機制。
對不急的背景處理,反而值得積極考慮使用。

因為是Windows所以不穩定

這也是很粗略的看法。

Windows不是實時作業系統。
但只要理解排程、電源管理、QoS、量測、日誌、執行緒設計,就能讓它相當穩定地應用在實務上。

問題不在於「因為是Windows所以沒辦法」,而在於沒有看清楚哪一層發生了什麼事。

14. 在實作之前,先設計日誌

這類問題有時只會在客戶端環境中發生。因此,讓應用程式端能留下最低限度的診斷資訊,會很有幫助。

例如,可以在診斷日誌中輸出以下資訊。

  • 應用程式版本
  • Windows版本
  • CPU名稱
  • 邏輯處理器數量
  • AC電源還是電池
  • 處理時間的平均・最大值・百分位數
  • 處理週期的延遲次數
  • 目標執行緒的優先順序
  • 行程優先順序
  • 是否設有親和性
  • 是否設有EcoQoS
  • 啟動時的電源計劃
  • 目標處理逾時的次數

當客戶端說「偶爾會變慢」時,如果沒有任何記錄,就只能靠猜測。有了日誌,就能建立假設。

只有電池模式時才慢
只有特定電腦才慢
只有剛啟動時才慢
30分鐘後開始變慢
只在其他應用程式啟動時才慢
以固定週期出現離群值

只要能看出這類差異,就更容易判斷出到底是優先順序、親和性、電源設定、發熱,還是別的等待造成的問題。

15. 小村軟體式的觀點

在Windows應用程式的開發中,光靠漂亮的理論是不夠的。

要能在客戶的電腦上運作。
要能在現場的終端上運作。
要能與老舊的周邊裝置共存。
要能在有防毒軟體、印表機、公司政策的環境中運作。
要能在筆記型電腦的省電設定下也不崩潰。
在需要效能的地方,要確實發揮效能。
在不急的處理上,不要打擾使用者。

要做到這些,最好不要把Windows單純視為「黑箱作業系統」。

Windows在考量該如何執行執行緒。
在考量該用哪個CPU執行。
在電力與效能之間取得平衡。
在處理像P核心/E核心這樣的異質核心。
試圖區分前景應用程式與背景處理。

開發者不該與它對抗,而應該在必要之處,把意圖傳達給它。

這個處理讓使用者在等。
這個處理慢一點也無妨。
這個處理的週期性很重要。
這個處理在背景安靜運作就好。
這個處理不能打擾其他處理。

把這樣的設計,落實到優先順序、親和性、QoS、省電設定的理解之中。

16. 總結

Windows應用程式的效能,不是光靠程式碼就能決定的。

即使提高優先順序,若親和性偏向E核心,可能還是不如預期地運作。
即使導向P核心,若電源模式偏向省電,CPU也可能無法發揮最大效能。
若不考慮CPU Sets與QoS就套用強力親和性,可能會堵死Windows電源管理與排程器的退路。
若把一切導向高效能側,發熱、風扇噪音、電池消耗、對其他應用程式的影響都會增加。

在Windows應用程式開發中,不只是速度,還要把回應性、穩定性、耗電、發熱、與其他行程的共存都納入設計。

因此該看的,終究還是這4件事。

優先順序
親和性
P核心 / E核心
省電設定

而在現代Windows中,還要再加上EcoQoS與Efficiency mode。

要擔心的事情很多。

但不需要把擔心一直放在不安的狀態。

把擔心,轉化為量測。
把擔心,轉化為日誌。
把擔心,轉化為設計。
把擔心,轉化為測試條件。

這樣一來,Windows就不再是單純任性的執行環境,而會變成一個相當值得觀察、能在現場依賴的平台。

Windows不是實時作業系統。
即便如此,只要理解並設計執行環境,就能做出在現場足以信賴的應用程式。

而這種難度,正是Windows應用程式開發的有趣之處。

參考連結

共用相同標籤的最新文章。能以相近的主題延伸理解。

與本文相近的主題頁面。以本文為起點,可進一步連到相關服務與其他文章。

本文連結到以下服務頁面,歡迎從最接近的入口查看。

常見問題

整理諮詢這個主題時常見的問題。

CPU優先順序要怎麼設定?
優先順序由行程的優先順序類別與執行緒的相對優先順序兩個層級決定。Win32 API中,行程側有 SetPriorityClass,執行緒側有 SetThreadPriority。若用 PowerShell,可以對 Get-Process 取得的物件的 PriorityClass 屬性設定 "AboveNormal" 之類的值;在 C# 中,則是把 Process.GetCurrentProcess() 的 PriorityClass 設為 ProcessPriorityClass.AboveNormal。不過,長期使用 HIGH_PRIORITY_CLASS 或 REALTIME_PRIORITY_CLASS 會讓整個系統的回應性變差,應該避免。
提高了優先順序,應用程式卻沒有變快,為什麼?
因為優先順序決定的是「有競爭時先讓哪個執行緒動」,並不是讓 CPU 變快的設定。如果變慢的原因是磁碟 I/O 等待、網路等待、鎖競爭、GC 停頓、UI 執行緒被阻塞、防毒軟體掃描,或是省電設定壓低了 CPU 頻率,提高優先順序並不能從根本解決問題。首先要記錄處理時間的分佈、電源狀態、實際執行的 CPU,才能找出真正的原因。
用 SetThreadInformation 設定 EcoQoS 會發生什麼事?
對 SetThreadInformation 指定 ThreadPowerThrottling 與 THREAD_POWER_THROTTLING_EXECUTION_SPEED,就能告訴作業系統把該執行緒視為 EcoQoS(偏向省電的 QoS)。QoS 可能影響核心選擇與處理器電源管理,因此可以把背景同步或不急的日誌彙總這類處理,導向不打擾前景應用程式的省電側。反之,直接關係到 UI 操作、相機擷取、控制週期的處理則應謹慎使用。將 StateMask 設為 0 即可還原為效能優先。
powercfg 的 CPMinCores 是什麼?
CPMinCores 是 Windows Core Parking 相關的電源設定,用來指定在任意時間點,至少要有百分之幾的邏輯處理器保持 un-parked(可使用)狀態。若設為 100%,Core Parking 演算法就會被停用。可以用系統管理員權限執行 powercfg /q SCHEME_CURRENT SUB_PROCESSOR 來確認目前的設定;當用親和性限制 CPU 時,Core Parking 的判斷有時會與之衝突,因此兩者必須一起檢視。

作者檔案

本文作者的個人檔案頁面。

Go Komura

小村軟體有限公司 代表

以 Windows 軟體開發、技術諮詢與故障調查為中心,在難以重現的故障調查與既有資產仍在運作的專案上具有優勢。

回到部落格一覽