How can I use SetProcessAffinityMask to select more than one logical processor?
In Windows Task Manager you can do this as an example:
I updated my CreateProcess procedure to do this:
type
TProcessPriority = (ptLow = $00000040,
ptBelowNormal = $00004000,
ptNormal = $00000020,
ptAboveNormal = $00008000,
ptHigh = $00000080,
ptRealtime = $00000100);
procedure RunProcess(FileName: string; Priority: TProcessPriority);
var
StartInfo: TStartupInfo;
ProcInfo: TProcessInformation;
CmdLine: string;
Done: Boolean;
begin
FillChar(StartInfo, SizeOf(TStartupInfo), #0);
FillChar(ProcInfo, SizeOf(TProcessInformation), #0);
StartInfo.cb := SizeOf(TStartupInfo);
CmdLine := FileName;
UniqueString(CmdLine);
try
Done := CreateProcess(nil, PChar(CmdLine), nil, nil, False,
CREATE_NEW_PROCESS_GROUP + Integer(Priority),
nil, nil, StartInfo, ProcInfo);
if Done then
begin
// Todo: Get actual cpu core count before attempting to set affinity!
// 0 = <All Processors>
// 1 = CPU 0
// 2 = CPU 1
// 3 = CPU 2
// 4 = CPU 3
// 5 = CPU 5
// 6 = CPU 6
// 7 = CPU 6
// 8 = CPU 7
// this sets to CPU 0 - but how to allow multiple parameters to
// set more than one logical processor?
SetProcessAffinityMask(ProcInfo.hProcess, 1);
end else
MessageDlg('Could not run ' + FileName, mtError, [mbOk], 0)
finally
CloseHandle(ProcInfo.hProcess);
CloseHandle(ProcInfo.hThread);
end;
end;
Note the comments I put in there. It would be good to update my procedure to include a new Affinity parameter which I can pass to SetProcessAffinityMask.
Calling any of these won't select the corresponding processors for obvious reasons, they give the idea of what I am wanting to do:
SetProcessAffinityMask(ProcInfo.hProcess, 1 + 2);
SetProcessAffinityMask(ProcInfo.hProcess, 1 and 2);
eg, select any of the CPU's for a process, as shown in Task Manager.
How should I do this, using an Array, Set or something else? I cannot get it to work with multiple values.
Thanks.
It is a bitmask as described in the documentation.
A process affinity mask is a bit vector in which each bit represents a logical processor on which the threads of the process are allowed to run.
And so on. You can use logical or
to combine them. So processors 0 and 1 would be $01
or $02
which equals $03
.
I would use the shift operator shl
to create values for specific processors. Like this:
function SingleProcessorMask(const ProcessorIndex: Integer): DWORD_PTR;
begin
//When shifting constants the compiler will force the result to be 32-bit
//if you have more than 32 processors, `Result:= 1 shl x` will return
//an incorrect result.
Result := DWORD_PTR(1) shl (ProcessorIndex);
end;
You can readily extend this to generate masks for lists of processors using logical or
in a loop.
function CombinedProcessorMask(const Processors: array of Integer): DWORD_PTR;
var
i: Integer;
begin
Result := 0;
for i := low(Processors) to high(Processors) do
Result := Result or SingleProcessorMask(Processors[i]);
end;
You can test for a processor being in a bit mask like this:
function ProcessorInMask(const ProcessorMask: DWORD_PTR;
const ProcessorIndex: Integer): Boolean;
begin
Result := (SingleProcessorMask(ProcessorIndex) and ProcessorMask)<>0;
end;
Note: I'm using DWORD_PTR
because for 64 bit targets the bitmask is 64 bits wide. That nuance doesn't matter for you on XE but it's worth getting it right to make any future code porting easier.