A Windows App Developer's Primer on CPU Settings: Priority, Affinity, and P-cores/E-cores

· Updated: · · Windows, CPU, Performance, Priority, Affinity, P-cores, E-cores, Power Efficiency, EcoQoS

Revision history (1 updates, last updated Sep 1, 2026)

A log of the changes made to this article. Where a pre-update version was archived, it stays readable at a permanent DOI link.

Retranslated as a full translation of the Japanese original. The previous English version was an abridgement that carried only part of the source, so sections, tables, Mermaid diagrams, figure captions and FAQ entries were missing. All of them have been restored to match the Japanese original, and the technical claims are the same as in the Japanese version. Read the version before this update (DOI: 10.5281/zenodo.21614649)
First published
Cite this article(DOI: 10.5281/zenodo.21614648)

This article is archived on Zenodo. Below are both the DOI that always resolves to the latest version and the DOI pinned to the version you are reading.

Go Komura (2026). A Windows App Developer's Primer on CPU Settings: Priority, Affinity, and P-cores/E-cores. KomuraSoft LLC. https://doi.org/10.5281/zenodo.21614648 https://comcomponent.com/en/blog/2026/06/03/000-windows-app-cpu-priority-affinity-power/

DOI (latest version)
10.5281/zenodo.21614648
DOI (this version)
10.5281/zenodo.22220507

The performance of a Windows app is not determined by code alone. The same .exe can feel different depending on the PC it runs on.

On one PC, the periodic processing is stable.
On another, it occasionally lags.
On AC power there is no problem, but on battery it is oddly slow.
You raised the priority in Task Manager, but it did not get as fast as you expected.
You thought you had steered it onto the P-cores, but the processing time still jitters.
Conversely, when you tried to raise performance, the fan never stopped and other apps became sluggish.

When you build Windows apps, you run into these phenomena that are hard to explain from the code alone.

When that happens, these are the four things to look at.

What to look at Roughly speaking
Priority Which thread runs first
Affinity Which CPUs it is allowed to run on
P-cores / E-cores Whether that CPU leans toward performance or power efficiency
Power-saving settings How hard the CPU is allowed to run

On modern Windows, EcoQoS and Task Manager’s Efficiency mode are also part of the picture.

In other words, a Windows app’s execution environment is not simply a matter of whether the CPU is fast or slow.

When it gets executed
Where it gets executed
What kind of core it gets executed on
What performance state the CPU is running in
Whether the OS considers it performance-critical or fine to run power-efficiently

These factors combine to determine the actual responsiveness and processing time.

This article organizes the relationships among priority, affinity, P-cores/E-cores, and power-saving settings, the things that are easy to overlook in Windows app development.

The code in this article is published on GitHub as a buildable, runnable sample set: a C# library and demo that handle priority and affinity, PowerShell scripts, and unit tests.

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

1. The Big Picture First

To start, here is the big picture as a diagram.

Application codeWindows schedulerPriorityhow readily it gets executedAffinity / CPU Setswhich CPUs it may run onQoS / EcoQoSperformance-focused or power-efficientChoose the thread to runChoose the logical processor to run onP-cores / E-coresperformance-leaning or efficiency-leaningPower mode / power plan / PPMfrequency, boost, Core ParkingActual responsivenessprocessing timeheatbattery drainimpact on other apps

The important thing is that these are not independent.

Raise the priority, and the thread becomes more likely to run.
But if the CPU itself is being managed toward power saving, it may not get as fast as you hoped.

Narrow the CPUs with affinity, and thread migration may decrease.
But if the narrowed set lands on efficiency-leaning cores, or clashes with Core Parking and power management, it can backfire.

Steer onto the P-cores, and the computation may get faster.
But push everything onto the high-performance side and you increase heat, fan noise, battery drain, and the impact on other processes.

Tuning Windows app performance is not a simple hunt for a make-it-faster button.

Which processing do you want to be fast?
Which processing is allowed to be slow?
Do you prioritize responsiveness to user input?
Do you prioritize the completion time of background work?
How much battery drain and heat will you tolerate?

It becomes a question of design.

In the diagram a solid line marks a relation that always holds and a dashed line marks a conditional one (the conditions are given per relation on the detail page). The full list of relations (25 in total, with evidence and certainty) and the definitions of the main concepts are collected on the knowledge map detail page (in Japanese). Data: JSON-LD / Turtle

2. Priority Is “How Readily It Gets Executed”

On Windows, when multiple runnable threads exist, the scheduler decides which thread goes onto the CPU next. This is where priority comes into play.

Broadly, priority is determined in two stages.

Process priority class
  + thread relative priority
  = thread base priority

In Win32 API terms, there is SetPriorityClass on the process side and SetThreadPriority on the thread side.

To view the priority of a running process in PowerShell, for example:

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

To raise the priority of the current PowerShell process:

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

In C#, you can write it like this:

using System.Diagnostics;

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

The thing to note here is that priority is not a setting that makes the CPU faster. What priority affects is which thread runs first when there is contention.

It is not a setting that raises the CPU frequency.
It is not a setting that selects P-cores.
It is not a setting that speeds up I/O.
It is not a setting that eliminates lock waits or network waits.

That is why “I raised the priority but it did not get faster” is entirely common. If the slowness is caused by things like the following, raising the priority will not fundamentally solve it.

  • Waiting on disk I/O
  • Waiting on the network
  • Waiting on a DB response
  • Lock contention
  • GC pauses
  • A blocked UI thread
  • Waits on the GPU or driver side
  • File scanning by antivirus software
  • The CPU frequency being held down toward power saving

Also, HIGH_PRIORITY_CLASS and REALTIME_PRIORITY_CLASS are not things to use casually.

A thread that keeps running at high priority for a long time becomes reluctant to yield CPU time to other threads.
If that stayed inside your own app it would be one thing, but it can degrade the responsiveness of the entire system.

Priority is like medicine.

In the situations where it works, it works.
But more is not better.

Things to consider before raising priority

Before raising priority, there are things worth considering first.

Question What to look at
Is the processing really CPU-bound? CPU utilization, ETW (Event Tracing for Windows, the OS’s built-in tracing mechanism), a profiler
Are you blocking the UI thread? UI responsiveness, going async, queue design
Is the high-priority window short? Raise temporarily and restore when done
Will it disturb other apps? Impact on input, printing, browsers, resident software
Will permissions or policy in the customer environment block it? Administrator rights, run-as user, security products

What matters in a Windows app is not always running at top priority, but prioritizing the necessary processing at the necessary time, over the necessary scope only.

3. Affinity Is “Which CPUs It May Run On”

If priority is “how readily it gets executed,” affinity is “which CPUs it may run on.”

On Windows, you can specify the set of logical processors a process or thread is allowed to run on.

In the Win32 API, there is SetProcessAffinityMask for processes and SetThreadAffinityMask for threads.

To view a process’s affinity in PowerShell, for example:

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

For verification purposes, to restrict the current PowerShell process to the first 4 logical processors, you can write:

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

0xF is 1111 in binary.
That is, it means logical processors 0-3 are allowed.

But this is strictly a verification example.

Narrowing affinity can improve CPU cache locality and reduce jitter in periodic processing.
On the other hand, it also pens threads into a narrow space when Windows could otherwise have shifted them to idle CPUs.

You need to be especially careful in environments like these.

  • P-cores and E-cores coexist
  • SMT/Hyper-Threading (Simultaneous Multi-Threading, which presents one physical core as several logical processors) makes the mapping between logical processors and physical cores unintuitive
  • It is a NUMA configuration (Non-Uniform Memory Access, where the distance from a CPU to memory is not uniform)
  • Processor Groups come into play beyond 64 logical processors
  • Core Parking is enabled
  • OEM or BIOS power management is aggressive
  • It runs in a virtualized environment

Affinity is a setting that imposes a fairly strong constraint on the system: run only on these CPUs.

Constraints can be a tool for stabilization.
But get them wrong and you cut off the escape routes.

These days there are also CPU Sets

The classic affinity mask is a quite strong constraint.

Windows, meanwhile, also has a mechanism called CPU Sets.
CPU Sets is an API for the application to communicate its CPU preferences more softly.

In Microsoft’s description, CPU Sets are positioned as a “soft” affinity specification compatible with OS power management.

Do you want to pin CPUs strictly?
Or do you want to bias roughly where things run, while cooperating with OS power management and scheduling?

This distinction matters.

Rather than trying to solve everything with the old-school SetProcessAffinityMask, a modern Windows app needs to think in terms of CPU Sets and QoS as well.

4. P-cores/E-cores: “CPUs Have Personalities Too”

On recent CPUs, not all cores necessarily have the same performance and the same power consumption. The representative case is P-cores and E-cores; roughly speaking, P-cores lean toward performance and E-cores lean toward efficiency.

Type Good at
P-cores Low latency, high single-thread performance, heavy foreground work
E-cores Power efficiency, background processing, soaking up parallel work

However, it is dangerous for a developer to casually hardcode “CPUs 0-7 are P-cores and 8-15 are E-cores.” The ordering of CPU numbers can vary with the CPU, the BIOS, the Windows version, firmware, OEM settings, and virtualization environments.

On the Windows side, the CPU Sets information includes a concept called EfficiencyClass.
On systems with heterogeneous processors, this value indicates the efficiency characteristics of that CPU Set. Microsoft’s documentation explains that a CPU Set with a higher value has a faster but less power-efficient processor.

If you want to work with P-cores/E-cores, looking at CPU numbers alone is not enough. To really nail it down, you need observations like these.

  • Look at EfficiencyClass via the CPU Sets API
  • Look at which CPU each thread executes on with Windows Performance Recorder / Analyzer (WPR / WPA, the standard tools for capturing and analyzing ETW traces)
  • Look at the trends in Task Manager’s logical processor view
  • Measure processing times on each physical machine
  • Compare AC power versus battery
  • Compare across different power modes

P-cores/E-cores are not just a hardware specification. Combined with the Windows scheduler, QoS, and power-saving settings, they determine where execution actually lands.

5. Power-Saving Settings Determine “How Hard the CPU Runs”

This part matters a great deal in the field.

Priority is “which thread runs first.”
Affinity is “which CPUs it may run on.”
P-cores/E-cores are “whether that CPU leans toward performance or efficiency.”

And the power-saving settings concern

what frequency and power state the CPU runs in to begin with

In other words, this can happen.

You raised the priority.
You steered onto the P-cores.
But if the power settings lean toward power saving,
the CPU may not run at full tilt.

This is a very Windows-flavored story.

On Windows 11, you can choose the power mode from the Settings app under “System > Power & battery.”
The labels vary by environment and version, but the directions are roughly these.

Power mode Direction
Best power efficiency Prioritize battery and power saving
Balanced Balance performance and power
Best performance Prioritize performance

In addition, the long-standing power plans include Power Saver, Balanced, and High Performance.
Balanced adjusts performance and power consumption according to demand, while High Performance is the setting that trades power consumption for readily reaching maximum performance.

Even though the user-visible settings are simple, behind them sit the Processor Power Management (PPM) settings.

6. P-states, C-states, Boost, and EPP

A CPU does not always run at its maximum frequency; it has states for reducing power consumption.

Term Roughly speaking
P-state A performance state that changes the CPU’s frequency and voltage
C-state A power-saving state that shuts down parts of the CPU when idle
Boost A mechanism that enters performance states above the rated spec when conditions allow
EPP Energy Performance Preference. The preference between performance and power saving

P-states are the mechanism that lowers power consumption by changing the CPU’s frequency and voltage.
C-states are the mechanism by which the CPU shuts down some functions when idle and enters deeper power-saving states.

Windows power management uses these mechanisms to balance performance against power consumption.

That is why the phenomenon of “high priority but still slow” is entirely possible.

The thread is being executed preferentially.
But the CPU frequency is low.
Boost is hard to enter.
EPP leans toward power saving.
Core Parking limits the usable cores.
On battery, the whole OS leans toward power saving.

In cases like this, looking at priority alone will not get you to the cause.

Especially in periodic processing, image processing, instrument control, video processing, audio processing, USB camera capture, and serial communication, the problem is not just the average processing time but the occasional lag.

The average is fast.
But one run in a thousand lags.
That one run clogs the buffer.
The UI freezes.
The timing with the equipment slips.

For phenomena like this, the average CPU utilization is not enough.

You need to look at the distribution of processing times, the maximum, the outliers, the power state, the executing CPU, and the background load together.

7. Core Parking Affects “How Many Cores Are Usable”

Windows has a mechanism called Core Parking. It is a control that rests logical processors that are not in use.
When utilization is low, it shifts some cores toward low-power states to reduce power consumption.

In Microsoft’s documentation, CPMinCores is described as the setting that specifies the minimum percentage of logical processors that must be un-parked, that is, kept usable, at any given time.
Setting the value to 100% disables the Core Parking algorithm.

What becomes a problem here is the combination with affinity.

You constrained the threads with affinity: run on this group of CPUs.
But how power management treats those particular cores is a separate question.

Microsoft’s Windows Server documentation likewise explains that when active threads are strongly affinitized to a subset of the CPUs within a NUMA node, there are situations where this fails to mesh with Core Parking’s decisions.

As a way of thinking, this is instructive for client PCs too.

Affinity constrains the scheduler.
Core Parking concerns which cores power management keeps in a usable state.

Think about these two separately and you will misread the cause.

8. EcoQoS / Efficiency Mode: Telling the OS “This Work Can Run Power-Efficiently”

In the old days, tuning Windows app performance mostly meant raising or lowering priority and changing affinity. Modern Windows, however, has a somewhat different concept as well. That is QoS.

QoS, Quality of Service, is the idea of indicating, per thread, how performance-critical or how power-efficient a piece of work should be.

Microsoft’s documentation explains that while scheduling priority remains the primary input for deciding which thread runs next, QoS can influence core selection and processor power management.

In particular, EcoQoS is the mechanism for treating work whose performance is not paramount in a power-efficient way.

In C++, for example, you can put the current thread into EcoQoS using SetThreadInformation with ThreadPowerThrottling.

#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));
}

Conversely, to return to performance-focused behavior, set StateMask to 0 for the same control target.

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));
}

To use it from C#, note that the .NET class library has no dedicated type for setting EcoQoS. You call SetThreadInformation through P/Invoke. The following assumes .NET 8.

using System;
using System.ComponentModel;
using System.Runtime.InteropServices;

internal static class EcoQos
{
    // ThreadPowerThrottling from THREAD_INFORMATION_CLASS
    private const int ThreadPowerThrottling = 3;

    // THREAD_POWER_THROTTLING_CURRENT_VERSION
    private const uint CurrentVersion = 1;

    // THREAD_POWER_THROTTLING_EXECUTION_SPEED
    private const uint ExecutionSpeed = 0x1;

    [StructLayout(LayoutKind.Sequential)]
    private struct ThreadPowerThrottlingState
    {
        public uint Version;
        public uint ControlMask;
        public uint StateMask;
    }

    [DllImport("kernel32.dll", SetLastError = true)]
    private static extern IntPtr GetCurrentThread();

    [DllImport("kernel32.dll", SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern bool SetThreadInformation(
        IntPtr hThread,
        int threadInformationClass,
        ref ThreadPowerThrottlingState threadInformation,
        uint threadInformationSize);

    /// <summary>Puts the current thread into EcoQoS (pass false to return to performance-focused behavior).</summary>
    public static void SetForCurrentThread(bool enabled)
    {
        var state = new ThreadPowerThrottlingState
        {
            Version = CurrentVersion,
            ControlMask = ExecutionSpeed,
            StateMask = enabled ? ExecutionSpeed : 0u,
        };

        var ok = SetThreadInformation(
            GetCurrentThread(),
            ThreadPowerThrottling,
            ref state,
            (uint)Marshal.SizeOf<ThreadPowerThrottlingState>());

        if (!ok)
        {
            throw new Win32Exception(Marshal.GetLastWin32Error());
        }
    }
}

The calling side looks like this. EcoQoS is a per-thread setting, so the safe pattern is to start a dedicated thread and turn it on and off inside that thread.

using System.Threading;

// RunBackgroundMaintenance is the non-urgent work you supply yourself
var worker = new Thread(() =>
{
    EcoQos.SetForCurrentThread(true);
    try
    {
        RunBackgroundMaintenance();
    }
    finally
    {
        // Always restore it when you are done
        EcoQos.SetForCurrentThread(false);
    }
})
{
    IsBackground = true,
};

worker.Start();

What to watch out for here is not applying this directly to Task.Run or to thread pool threads. What gets configured is an OS thread, not the Task that happens to be running. Put EcoQoS on a pool thread and that thread stays on the power-efficient side the next time it is handed different work. And if async / await moves the continuation to another thread, the setting will take effect outside the interval you intended, or not at all inside it.

EcoQoS is not a feature where making everything power-efficient is the right call. The work it suits looks like this.

  • Background synchronization
  • Low-priority index building
  • Log aggregation that is not urgent
  • Cache updates not directly tied to user interaction
  • Maintenance work that just needs to finish eventually

On the other hand, you should be cautious with work like this.

  • Processing directly tied to UI interaction
  • Camera capture
  • Audio processing
  • Processing tied to a control cycle
  • Pass/fail judgment in inspection equipment
  • An export the user is waiting on
  • Processing that requires near-real-time responses

Task Manager’s Efficiency mode is related to this same idea.
Microsoft’s Performance Diagnostics blog explains that enabling Efficiency mode lowers the process’s base priority to Low and sets its QoS to EcoQoS.

In other words, Efficiency mode is not just a power-saving icon. It is a mechanism that combines priority lowering with EcoQoS to protect the responsiveness of foreground apps and power efficiency.

From a developer’s perspective, this is quite important.

It enables a design where you can tell the OS not only “I want this work to finish fast” but also
“this work just needs to run without getting in the user’s way.”

9. A Decision Flowchart

Since the story so far is complicated, here is a decision flow for investigating performance issues and timing jitter in Windows apps.

No / unknownYesYesNoYesNoYesNoYesNoSymptomsslow, jittery cycles, frozen UI, loud fanRecord firstprocessing time / max / CPU utilization / power state / AC or battery / target PCIs the CPU the main cause?Check I/O, locks, DB, network, GC, GPU, driverseliminate the waits before touching priority or affinityIs there heavy CPU contention with other processes?Consider prioritybut limit the scopeavoid routine use of High/RealtimeIs execution skewed toward specific CPUs?Check affinity / CPU Setsare you pinning too hardare you accounting for P/E cores and NUMAAre frequency or power states suspicious?Check power mode / power plan / PPMsuspect P-states / EPP / boost / Core ParkingIs background work disturbing the foreground?Consider EcoQoS / Efficiency Mode / lowering priorityshift non-urgent work to the power-efficient sideRevisit the algorithm, parallelism, queue design, and UI thread designChange one item at a time and A/B measureLook not just at the average but atmaximums, outliers, heat, and the impact on other apps

What matters in this flow is not fiddling with settings from the start. Record first.

  • Is it always slow?
  • Or only occasionally slow?
  • AC power or battery?
  • What is the power mode?
  • Is it in Efficiency mode in Task Manager?
  • Is CPU utilization high?
  • Is the frequency rising?
  • Which thread is using the CPU?
  • How large is the maximum processing time?
  • Is other resident software or antivirus software involved?

Then change things one at a time.

Change the priority.
Change the affinity.
Change the power mode.
Apply EcoQoS.
Separate out the background processing.
Insert a queue.
Move it off the UI thread.

Change several at once, and you will no longer know what worked.

10. Commands for Checking in the Field

When a Windows app behaves differently depending on the environment, it helps to be able to capture the state first.

View CPU information

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

View the active power plan

powercfg /getactivescheme

View the processor power management settings

powercfg /q SCHEME_CURRENT SUB_PROCESSOR

The output is quite long, but you can check the settings related to minimum and maximum processor state, EPP, boost, and Core Parking.
The visible items vary by environment.

Generate a power efficiency diagnostics report

Run in an elevated terminal.

powercfg /energy

After measuring for a fixed period, an HTML report is produced.
It is an entry point for looking at drivers, devices, timer resolution, USB power saving, sleep blockers, and so on.

View the priority and affinity of a target process

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

Check it in Task Manager

On a customer’s machine you may not be able to open PowerShell. When you want to check everything from the GUI, here is where to look. The wording changes between Windows versions, so the names below follow the English UI.

What you want to see How to get there
A process’s priority Task Manager > Details page > right-click a column header > Select columns > check Base priority
Change the priority On the Details page, right-click the process > Set priority
View or change affinity On the Details page, right-click the process > Set affinity
Whether Efficiency mode is on The Status column on the Processes page shows Efficiency mode. A parent process that has put its child processes into Efficiency mode gets a leaf icon
Turn Efficiency mode on Select the process on the Processes page and press Efficiency mode in the command bar, or choose it from the right-click menu
Load per logical processor Performance page > CPU > right-click the graph > Change graph to > Logical processors

Efficiency mode cannot be applied to core Windows processes, because throttling them affects system stability.

Efficiency mode is also not just a display label. Microsoft’s Performance Diagnostics blog explains that it lowers the process’s base priority to Low and sets its QoS to EcoQoS. In other words, it applies the two settings from section 8 in one shot from the GUI.

Note that what the Details page of Task Manager shows is the per-process priority only. Per-thread relative priorities and per-thread QoS are not displayed. To see that far, capture ETW with WPR / WPA.

View the CPU’s performance state

The available counter names vary by environment, but these Performance Counters are a useful reference.

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

For serious investigation, capturing ETW with Windows Performance Recorder (WPR) and Windows Performance Analyzer (WPA) is more reliable.
You can track cycle jitter, context switches, the executing CPU, DPC/ISR (Deferred Procedure Call / Interrupt Service Routine, a driver’s interrupt handling and the work it defers), disk I/O, and CPU frequency changes all together.

11. For Soft Real-Time Work, Consider Everything Together

Windows is not a real-time OS in the usual sense. Yet Windows apps in the field are sometimes asked for near-real-time characteristics.

  • Capturing images from a USB camera on a fixed cycle
  • Exchanging data with equipment over serial communication
  • Reading data from measuring instruments
  • Synchronizing with PLCs and external devices
  • Processing audio or video
  • Returning inspection results within a fixed time
  • Running heavy computation without freezing the UI

For work like this, fast code alone is not enough.

You need thread design.
You need queue design.
You need logging.
You need timeouts.
You need backpressure, the deliberate throttling of the upstream flow when the downstream cannot keep up.
You need to decide whether to drop, wait, or retry when things run late.

And on top of all that, you need to look at priority, affinity, P-cores/E-cores, and power-saving settings.

For example, consider a camera capture thread.

This thread is close to user interaction and has periodicity.
So it should not be put into EcoQoS.
Its priority is worth raising by one step, but only for the capture interval. Never run at HIGH_PRIORITY_CLASS or above as a matter of course; it hurts the UI and other processing.
Pin the affinity only when measurements show a correlation between cycle jitter and CPU migration. If you do pin it, choose the P-core side by looking at EfficiencyClass, not at CPU numbers. Pinning to the E-core side backfires.
If the power mode leans toward power saving, lags that never appeared on AC power will appear on battery. So always include a battery condition in your performance testing.

What about compressing old logs, on the other hand?

That is the archetype of work no user is waiting on.
In that case, lower its priority.
Make it EcoQoS.
Run it at idle.
Run it only on AC power.
A design like that is kinder for the app as a whole.

Rather than making everything fast, separate the work that should be fast from the work that should stay out of the way.

12. Basic Design Principles

There are six basic principles for handling these matters in a Windows app.

1. Do not pin things from the start

It is better not to strongly fix priority or affinity from the start.

The Windows scheduler does a rather good job most of the time.
Imposing needless constraints from the app side can make things worse.

Build it normally first.
Measure.
When a problem appears, form a hypothesis.
Make a small change.
Measure again.

That is the order.

2. Use priority temporarily

Keep high priority to just the intervals that need it.

Rather than staying at high priority permanently,

raise it just before the important work
restore it when done

is safer.

3. Consider affinity near the end

Affinity is a strong setting.

It can be effective when cycle jitter or the effects of CPU migration are suspected.
But it is not the first setting to touch.

In particular, if your customers have multiple kinds of PCs, hardcoding CPU numbers is dangerous.

4. Do not hardcode P-cores/E-cores, observe them

If you want to use P-cores and E-cores selectively, observe on real machines first.

Rather than hardcoding by CPU number, look at CPU Sets, EfficiencyClass, ETW, and actual measurements.

5. Test with power-saving settings as a premise

“It is fast because the development machine is on AC power with high-performance settings” is not enough.

In customer environments, states like these are entirely normal.

  • A laptop running on battery
  • Best power efficiency
  • Energy saver
  • OEM-specific power-saving utilities
  • Power settings locked down by corporate policy
  • Thin PCs that throttle under heat
  • Business terminals loaded with resident software

In performance testing, you want to examine at least these conditions separately.

Condition What to look at
AC + Best performance A state close to maximum performance
AC + Balanced Typical business use
Battery + Balanced The reality of laptops
Battery + Best power efficiency The lower bound on the power-saving side
Long continuous runs Heat, fans, thermal throttling
Concurrent use with other apps Coexistence with browsers, Teams, Excel, antivirus software

6. Consider EcoQoS for background processing

For work not directly tied to the user experience, it is often better to prioritize staying out of the way over chasing performance.

An app that runs everything at high performance may be fast.
But it disturbs other work.
It spins up the laptop’s fans.
It drains the battery.
The result is an app that is unpleasant to use.

For Windows apps, coexisting well is part of quality, not just speed.

13. Common Misconceptions

Raising priority makes it faster

Not necessarily.

If CPU contention is the cause, it can work.
But if the cause is I/O waits, lock waits, low frequency due to power saving, or execution on the E-core side, priority alone is insufficient.

Pinning to P-cores is always the right answer

Not always.

P-cores are high performance, but heat and power consumption increase too.
They may also contend with other important foreground work.

What matters more is separating the work that should go to P-cores from the work for which E-cores are sufficient.

Power-saving settings are the user’s preference and irrelevant to the app

They are relevant.

The same app executes differently depending on the power mode, power plan, EPP, boost, and Core Parking.

On laptops especially, behavior changes between AC power and battery.

Efficiency mode just makes things slower

It does not just make things slower.

Through priority lowering and EcoQoS, it is a mechanism for protecting foreground app responsiveness and power efficiency.
For non-urgent background work, it is worth considering proactively.

Windows is just inherently unstable

That, too, is a sloppy view.

Windows is not a real-time OS.
But if you understand scheduling, power management, QoS, measurement, logging, and thread design, you can stabilize it substantially for field use.

The problem is not that it is impossible because it is Windows. The problem is not looking at what is happening in which layer.

14. Design the Logging Before the Implementation

Problems of this kind sometimes occur only in customer environments. So it helps to make the app capable of recording at least minimal diagnostic information.

For example, the diagnostic log should include information like this.

  • App version
  • Windows version
  • CPU name
  • Number of logical processors
  • AC power or battery
  • Average, maximum, and percentiles of processing time
  • Number of delayed processing cycles
  • Priority of the target thread
  • Process priority
  • Whether affinity is set
  • Whether EcoQoS is set
  • Power plan at startup
  • Number of times the target processing timed out

When a customer says it is occasionally slow and you have no records, you are left to a battle of guesses. With logs, you can form hypotheses.

Slow only on battery
Slow only on a specific PC
Slow only right after startup
Slow starting 30 minutes in
Slow only while another app is running
Outliers appearing at a fixed cycle

Once differences like these are visible, it becomes much easier to determine whether it is priority, affinity, power settings, heat, or some other wait.

15. The KomuraSoft Perspective

In Windows app development, clean theory alone is not enough.

Working on the customer’s PC.
Working on the terminals in the field.
Coexisting with old peripherals.
Working in environments with antivirus software, printers, and corporate policies.
Not falling apart under a laptop’s power-saving settings.
Delivering real performance where performance is needed.
Staying out of the user’s way for work that is not urgent.

To achieve that, it is better not to see Windows as a mere black-box OS.

Windows is thinking about how to run your threads.
It is thinking about which CPU to run them on.
It is balancing power against performance.
It is handling heterogeneous cores like P-cores and E-cores.
It is trying to distinguish foreground apps from background work.

Developers should not fight that. They should communicate their intent where it is needed.

This work is keeping the user waiting.
This work can be somewhat slow.
This work’s cycle timing is critical.
This work just needs to run quietly in the background.
This work must not disturb other work.

You translate that design into your understanding of priority, affinity, QoS, and power-saving settings.

What this article covered is the settings on the CPU side. When the cause of “slow” lies outside the CPU, one of the following articles is the shorter route.

Symptom Article to read next
You cannot tell where the time is going Pinpointing “Slow” with PerfView and dotnet-trace: A Practical Introduction to .NET Performance Investigation
You want to start by building a way to record customer environments An Introduction to Windows Event Log and ETW: Putting Your Business App’s Logs on the OS’s Standard Mechanisms
You suspect I/O waits or a clogged thread pool The Depths of Windows I/O (Part 3): I/O Completion Ports (IOCP) and the .NET Thread Pool
Memory keeps growing and things slow down Telling GC Lag from a Memory Leak in .NET
Only the UI freezes WPF/WinForms async and the UI Thread on One Sheet

16. Conclusion

The performance of a Windows app is not determined by code alone.

Raise the priority, and it may still not behave as expected if affinity has steered it toward the E-cores.
Steer onto the P-cores, and the CPU may still not deliver maximum performance if the power mode leans toward power saving.
Apply strong affinity without considering CPU Sets and QoS, and you cut off the escape routes of Windows’s power management and scheduler.
Push everything to the high-performance side, and heat, fan noise, battery drain, and the impact on other apps all increase.

In Windows app development, you need to design not just for speed but for responsiveness, stability, power consumption, heat, and coexistence with other processes.

And the things to look at remain these four.

Priority
Affinity
P-cores / E-cores
Power-saving settings

And on modern Windows, EcoQoS and Efficiency mode join them.

There is plenty to worry about.

But there is no need to leave the worry as anxiety.

Turn the worry into measurement.
Turn the worry into logs.
Turn the worry into design.
Turn the worry into test conditions.

Do that, and Windows becomes not a capricious execution environment but a platform for the field that rewards careful observation.

Windows is not a real-time OS.
Even so, if you understand the execution environment and design for it, you can build apps that are thoroughly dependable in the field.

And that very difficulty is part of what makes Windows app development interesting.

Recent articles sharing the same tags. Deepen your understanding with closely related topics.

These topic pages place the article in a broader service and decision context.

This article connects naturally to the following service pages.

Frequently Asked Questions

Common questions about the topic of this article.

How do I set CPU priority?
Priority is determined in two stages: the process priority class and the thread's relative priority. In the Win32 API there is SetPriorityClass on the process side and SetThreadPriority on the thread side. In PowerShell you can set the PriorityClass property of the object returned by Get-Process to a value such as "AboveNormal"; in C# you assign ProcessPriorityClass.AboveNormal to the PriorityClass of Process.GetCurrentProcess(). That said, routine use of HIGH_PRIORITY_CLASS or REALTIME_PRIORITY_CLASS degrades the responsiveness of the whole system and should be avoided.
Why doesn't my app get faster after I raise its priority?
Because priority decides which thread runs first when there is contention; it is not a setting that makes the CPU faster. If the slowness comes from disk I/O waits, network waits, lock contention, GC pauses, a blocked UI thread, antivirus scanning, or a CPU frequency held down by power-saving settings, raising priority will not fundamentally solve it. Start by recording the distribution of processing times, the power state, and which CPU the work runs on, so that you can isolate the cause.
What happens when I set EcoQoS with SetThreadInformation?
Passing ThreadPowerThrottling and THREAD_POWER_THROTTLING_EXECUTION_SPEED to SetThreadInformation tells the OS to treat that thread as EcoQoS, a power-efficient quality of service level. Because QoS can influence core selection and processor power management, you can move work such as background synchronization or non-urgent log aggregation onto the power-efficient side, where it does not disturb the foreground app. Be cautious, on the other hand, with work tied directly to UI interaction, with camera capture, and with anything on a control cycle. Setting StateMask to 0 returns the thread to performance-focused behavior.
What is CPMinCores in powercfg?
CPMinCores is a Windows power setting for Core Parking that specifies the minimum percentage of logical processors to keep un-parked, that is, available, at any given time. Setting the value to 100% disables the Core Parking algorithm. You can check the current setting by running powercfg /q SCHEME_CURRENT SUB_PROCESSOR with administrator rights. If you are narrowing CPUs with affinity, there are situations where that fails to mesh with Core Parking's decisions, so you need to look at both together.

Author Profile

Profile page for the article author.

Go Komura

Representative of KomuraSoft LLC

Focused on Windows software development, technical consulting, and investigations into failures that are difficult to reproduce.

Back to the Blog