Author: Kim Sandell
When writing applications that are designated to tun in multi-CPU environments, it
is very useful to be able to control which CPU's the application executes on. By
optimizing the CPU usage one can dramatically increase the performance of the
application
Answer:
Introduction
When writing applications that are designated to tun in multi-CPU environments, it
is very useful to be able to control which CPU's the application executes on.
By optimizing the CPU usage one can dramatically increase the performance of the
application.
Affinity Masks - Background
When a process is created in windows, an affinity mask is passed to it. This is
usually the system affinity mask, since the system is launching the process.
Also by default each thread created by this process is now assigned the current
affinity mask for the process. This means that the thread is executed in any of the
available CPU's.
If the "Process Affinity Mask" is changed, all threads created after that will only
be allowed to execute in any of the available CPU's, also the whole process is
limited to the same CPU's.
Getting the Affinity Mask
Windows provides us with an API call that help us get the affinity mask.
The API call is:
GetProcessAffinityMask(hProcess: Cardinal; var procAFMask, sysAFMask);
The hProcess parameter is the current process handle, and the procAFMask and
sysAFMask variables are cardinals.
Before we change the affinity mask, we first need to get the current affinity mask
for the whole system. This is because we do not want to try to set an affinity mask
that is not possible.
When the API call returns it puts the BitMASK for the CPU's in each of the
parameters.
The bits are encoded as followes:
BitMask CPU's
00000001 1st CPU
00000010 2nd CPU
00000100 3rd CPU
00001000 4th CPU
00010000 5th CPU
00100000 6th CPU
01000000 7th CPU
10000000 8th CPU
By combining these BIT values one can determine the CPU count/mask. The BitMask is
32 bits in size, so theoretically the BitMask supports up to 32 CPU's.
Example: BitMask=00000011 would mean 2 CPU's, number 1 and 2.
Changing the Affinity Mask of a Process
Windows provides us with an API call that help us set the affinity mask.
The API call is:
SetProcessAffinityMask(hprocess: Cardinal; ProcessAffinityMask: Cardinal);
The hProcess parameter is the current process handle, and the ProcessAffinityMask
variable is a cardinal.
To obtain the current process handle we need another API call named
"GetCurrentProcess()". This API call returns the handle of the current process.
The ProcessAffinityMask variable contains the BitMASK of the CPU's we want this
process to execute on. (see the BitMask table above).
Example:
1 var
2 ProcAFMask,
3 SysAFMask: Cardinal;
4 begin
5 { Get the current values }
6 GetProcessAffinityMask(GetCurrentProcess, ProcAFMask, SysAFMask);
7 { Manipulate }
8 SysAFMAsk := $00000001; // Set this process to run on CPU 1 only
9 { Set the Process Affinity Mask }
10 SetProcessAffinityMask(GetCurrentProcess, SysAFMAsk);
11 end;
A realworld example
Now that I have shown how to get and set the affinity masks for processes, I'd like
to show a real-world example of how to utilize this.
I had a situation where our customer had a 4 CPU server, and used it for some heavy
processing about 80% of the time.
The customer wanted us to create an application for them, but they didn't want to
invest in the hardware, since they already had a good server running. They where
unsure of the total load on the server so we investigated, and found that the
server only used the 2 first CPU's when under heavy load. This meant that there
were 2 CPU's available for us !
So we implemented the Affinity Mask API calls, and concluded that our application
was executing nicely on CPU's number 3 and 4 only, leaving the 2 other CPU's free
for the other application on the server.
Our application used alot of Threads, but since the master affinity for the whole
process was changed, the threads followed the set parameters without problems.
What about the affinity masks for the Threads?
If you want to read more about the affinity masks for Threads there is an excellent
article:
Extending TThread for multiple processor environments
|