CPU Management - and why you shouldn't

ubuysa

The BSOD Doctor
There are only two Windows controls available to the user that affect application CPU performance, Base Priority and CPU Affinity, and using either of them is more likely to make performance worse rather than better. It is surprisingly easy with these controls to make your whole system sluggish and even hang, forcing a restart. Where these controls are moderately useful is in CPU performance troubleshooting, where they can help to identify which application is causing CPU performance issues. For this reason alone it's worth understanding what these controls do and how best to use them.


Processes and Threads

We think in terms of applications, we start the Chrome browser for example and think of it as one application, but internally it's a bit more complex than that. Applications are managed in Windows as one or more processes and within each process the executing code is managed by one or more threads.

The basic work grouping in Windows is called a process. A process contains all the resources needed for an application to function, each process gets its own virtual address space for example. If you open the Task Manager and click the Details tab every entry you see there is an individual process. Some applications (Notepad for example) run as a single process but other applications (Chrome for example) run as multiple processes.

Within each process the individual units of work are called threads. On the Task Manager Details screen right-click anywhere on the header row and click on 'Select Columns'. In the dialog that opens scroll down and check the box labelled Threads and then click Ok. In this column you can see the number of threads inside each process. A few processes have only one thread - if you have Java installed on your PC then you'll have the Java Update Scheduler process running, it's called jusched.exe, it has only a single thread (it's called a single-threaded process). Now start Notepad (the Task Manager Details page will refresh itself in a few seconds) and look at the thread count for notepad.exe, there are many threads in notepad.exe (this is called a multi-threaded process).

What executes on a CPU are threads, so a multi-threaded process could have several of its threads executing at the same time on different CPUs.

You can find out how many CPUs you have by entering 'msinfo32' in the Run command box, this displays the System Information panel. Make sure the System Summary is selected (it's the default). Look down the Item column in the right-hand pane until you find the Processor entry. That tells you what CPU chip you have and how many cores it has, each core is a separate CPU. If your processor supports hyperthreading, which internally appears to run two threads on each core at the same time, there are two logical processors per core, so a four core processor has eight logical processors. Windows uses logical processors, so on a four core hyperthreading processor Windows will see eight separate CPUs and will run eight threads concurrently, one on each logical processor. If your processor doesn't support hyperthreading then the number of cores is the number of CPUs that Windows will see.

On the Task Manager Details tab look at the Status column, count up how many processes have a status of Running on your system, there are likely well over 100. Even if we assume that each process has only one active thread (and most processes will have more than that) it's clear that we have way more active threads than we have CPUs. How can they all be Running?


Dispatching Threads

A thread exists in one of three states (there are others but they're not important here)...

1. Executing - the thread is executing instructions on a CPU right now
2. Ready - the thread can execute when a CPU becomes available, but it is currently in a queue
3. Waiting - the thread is waiting for some external event to complete and is not able to execute at the moment

In the Task Manager Details page we mentioned above, all those processes with a status of Running have at least one thread that is in one of these states. It means that process is doing work, but not necessarily executing on a CPU right now.

The Windows component that decides which thread to run on which CPU (and when) is called the Dispatcher. The Dispatcher is only interested in threads that are Executing or Ready, a thread that is Waiting is invisible to the Dispatcher.

When a thread moves from the Waiting state to the Ready state it's placed on a queue called the Ready Queue, there is one Ready Queue per logical processor (or per core if your processor doesn't support hyperthreading). The Dispatcher will normally select the thread at the top of the Ready Queue to run on that processor next, so a thread's position on the Ready Queue is critical to it's performance. Threads are queued based on their priority.


Thread Priority

Every thread has a priority from 0 to 31 (where 31 is the highest), this is made up of two components; the base priority and the relative priority.

A thread's base priority is inherited from the process; every process has a base priority which is called either Real-Time, High, Above Normal, Normal, Below Normal, or Low - these names equate to a number (higher numbers mean higher priority). Every thread in a process thus uses the same base priority, this is assigned by the developer based on the relative importance of this process in the whole system.

Each thread also has a relative priority within its process. These are assigned by the developer to establish the relative priorities of the various threads within the process, this is crucial to the proper operation of the process, so only the developer can change these. These have similar names to the base priority but they equate to different numbers, some of them negative numbers.

The relative priority is added to (or subtracted from) the base priority and then capped to give the actual thread priority used to order threads on the Ready Queues. To give you a feel for how the base priority and relative priority are combined to produce the actual priority of a thread the various base priorities produce actual priorities in these ranges....

Low - 1 to 15 but skewed at the low end, mostly around 1 to 6.
Below Normal - 1 to 15 but skewed close to the lower end, mostly around 4 to 8.
Normal - 1 to 15 but skewed in the middle, mostly around 6 to 10.
Above Normal - 1 to 15 but skewed close to the upper end, mostly around 8 to 12.
High - 1 to 15 but skewed at the upper end, mostly 11 to 15.
Real-Time - 16 to 31 but skewed in the middle, mostly 22 to 26.

From this you can see that it is perfectly possible for a thread in a process with Normal base priority to end up with an actual priority that is higher than a thread in a process with the Above Normal base priority. You can also see that the Real-Time base priority always produces an actual priority that is higher than any of the other base priorities.

You can change the base priority of a process but in doing so you change the priority of every thread in that process, from the above you can see that this might produce some unintended consequences for other threads in other processes. Elevating a process to the Real-Time base priority is very likely to have very serious (and probably fatal) consequences for other processes in the system, especially system processes. Don't forget that you have no idea of the relative priority of threads within a process, so you have no idea of the actual priority that each thread in a process will get when you change the base priority of the process.

Open the Task Manager and click the Details tab, if the column labelled Base Priority is not visible then right-click anywhere on the header line, click 'Select Columns', scroll down and check the box labelled Base Priority. In this column you can see the Base Priority assigned to most processes (some System processes run at a fixed priority of 31 and show as N/A in this column). Click the Base Priority column header to sort the processes based on this column, you'll see that you probably have a few High priority processes, a great mass of Normal priority processes and possibly a few Below Normal and even perhaps one or two Low priority processes.
 
Last edited:

ubuysa

The BSOD Doctor
Changing Base Priority

To see how the base priority of a process can be changed start Notepad and wait for the Task Manager Details page to refresh. The Base Priority of Notepad is Normal, to change it right-click on notepad.exe and select Set Priority, from the fly-out list select Below Normal and confirm the change in the warning dialog that pops up. On the Task Manager Details page you can see the base priority of notepad.exe has changed to Below Normal, so all the threads in Notepad now queue lower down on the Ready Queues of all CPUs because their actual priority is now lower. They will thus get to execute on CPUs much less often.

You can try using Notepad now with its Below Normal base priority, but if you have idle logical processors in your system you'll not see any difference, thread priority only becomes important when there is more work than the CPUs can handle. We'll create a demonstration later that will let you see thread priority in action.

Note that this base priority change lasts only whilst the process is active. If you close Notepad and then start it again you'll see it starts with the Normal base priority (because initial base priority is set by the developer in the application).

Should you change the base priority of a process? I hope your answer is probably not. You should certainly never change base priority upwards without careful consideration of the potential consequences, and be prepared for some unexpected consequences too. Never ever consider raising a base priority to Real-Time, it will impact system processes, at best you'll make your system very sluggish, at worst it will hang.

Changing the base priority downwards however can sometimes be a useful performance aid. Suppose you have a multi-threaded CPU hog application running for example, one that uses lots and lots of CPU cycles on all of your CPUs, it will make the rest of your system very sluggish. If you change the base priority for the CPU hog process downward by one level (so from Normal to Below Normal for example) the application will keep running but it will now go on all the CPU Ready Queues below your other processes, this will restore the responsiveness of your system. Of course the CPU hog application will now take much longer to complete.

The above technique can also be used for CPU performance troubleshooting, by reducing the base priority of (non-system) processes you can quickly find out which process is contributing to your CPU issue without having to kill processes.

Do not be tempted to increase the base priority of a process in order to improve its performance, even if the CPU is the bottleneck. The unintended consequences could be dire. Either consider reducing the base priority of competing processes or, better still, reduce the volume of work in the system by closing unnecessary applications.


CPU Affinity

Windows will normally schedule all threads across all available logical processors. The Dispatcher does prefer certain processors for certain threads because keeping a thread on a given processor retains that thread's status information in the processor cache, improving the performance of that thread. In general terms however, any thread could be run on any available logical processor and it doesn't have to run on the same processor every time it's dispatched.

Setting CPU Affinity allows you to specify which logical processors the threads of a given process run on (it can be one or many). Note that you can only set CPU Affinity at the process level, so all threads in a process run on that processor (or processors).

Before we look at how to do that let's talk about why you'd want to do that. The Dispatcher is far better at choosing which processor to use than you are, so although there is a small performance benefit in using the same processor for a thread, that is not a good enough reason to use CPU Affinity. A multi-threaded process with threads that the developer expects to be running concurrently on many processors may not run very well if you restrict it to one or two.

The only valid performance reason for setting CPU Affinity is in the unlikely event that you have an application that is not multiprocessor aware and must run all its threads on a single CPU. Today these kinds of application are as rare as hen's teeth.

Another potential reason for setting CPU Affinity might be in a CPU troubleshooting scenario. Suppose you have a process that seems to be running away with CPU cycles across all logical processors, confining it to a single processor with CPU Affinity will isolate the problem to that CPU, allowing the rest of the system to run on the other processors. You can then decide whether to lower that processes base priority and remove CPU Affinity or allow it to complete on that single processor.

Note that when you set CPU Affinity for a process then the threads of that process can only run on that processor (or processors) but that doesn't mean it's the only process running on that processor(s). The Dispatcher will still schedule other threads to run on the same processor(s). There is no mechanism that allows you to specify exclusive CPU Affinity.

To change CPU Affinity open the Task Manager at the Details tab, right-click on the process and select Set Affinity. In the dialog that opens uncheck CPUs to leave only the desired CPU(s) selected and click Ok.

Note that CPU Affinity lasts only whilst the process is active. If you stop and restart the process the CPU Affinity will be lost.

To see this in action we need a fairly CPU intensive application. Most of us run the Malwarebyte's on-demand anti-virus scanner and this is a big CPU user. If you don't have the Malwarebyte's scanner perhaps now is a good time to download it?

Start the Malwarebyte's scanner and configure it to scan all your disks. Once it gets started open the Task Manager Details page, you'll see that the MBAMService.exe process is consuming a lot of CPU cycles. Now click the Performance tab, CPU section, and display the graphs for each logical processor (if you have a single overall graph, right-click on the single graph and select Change Graph To and select logical processors). You can see high CPU usage across all CPUs.

Now go back to the Details tab, right-click on the MBAMService.exe process and set the CPU Affinity to a single logical processor - it's wise not to choose CPU 0 because some system threads prefer CPU 0, so select a single CPU other than 0.

If you now go back to the Processor tab you can see that only the logical processor you selected is now consuming lots of CPU cycles, the others are relatively idle. This is because MBAMService.exe is running only on that processor.


Demonstrating The Effect of Base Priority Changes

We can use Malwarebyte's scanner and CPU Affinity to demonstrate how base priority affects application performance. If you have fewer than four logical processors in your system (or four physical core if your processor doesn't support hyperthreading) I suggest you not attempt this demonstration, it could negatively impact your overall system performance quite severely.

With the Malwarebyte's scanner running on only a single CPU, start Notepad. On the Task Manager Details tab set the CPU Affinity for notepad.exe to the same processor as the MBAMService.exe. Now try opening an existing text document with Notepad, you may find it a little less responsive than usual but probably not not by much. This is because both MBAMService.exe and notepad.exe share the same base priority (Normal) so the actual priority of their respective threads is probably very similar (we can't know for sure) so they queue close together on the Ready Queue.

Now change the base priority of notepad.exe to Below Normal, then try opening an existing text document with Notepad again, you will find that Notepad is now very sluggish and quite difficult to use. This is because most of MBAMService.exe threads are queuing above those of notepad.exe resulting in very poor Notepad performance.

Now try swapping the base priorities over, make notepad.exe Normal and MBAMService.exe Below Normal. You should find that Notepad is behaving quite normally now, this is because the MBAMService.exe threads are being queued behind notepad.exe threads.

Because both Base Priority and CPU Affinity last only for the life of the process there is no need to change them back (unless you want to). Simply close the Malwarebyte's application (via the icon in the system tray) and close Notepad and the changes you just made will be lost.
 
Last edited:
Top