Mirror

Using the Affinity Mask in multi-CPU environments (Views: 100)


Problem/Question/Abstract:

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:

var
  ProcAFMask,
    SysAFMask: Cardinal;
begin
  { Get the current values }
  GetProcessAffinityMask(GetCurrentProcess, ProcAFMask, SysAFMask);
  { Manipulate }
  SysAFMAsk := $00000001; // Set this process to run on CPU 1 only
  { Set the Process Affinity Mask }
  SetProcessAffinityMask(GetCurrentProcess, SysAFMAsk);
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

<< Back to main page