面向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 软件开发、技术咨询与故障排查为中心,擅长难以复现的故障调查,以及既有资产仍在运行的项目。

返回博客列表