我正在 Amzon EC2 云中运行的 Windows 服务器上运行程序。我使用的实例有 32 个可用虚拟核心。我想展示系统的线性可扩展性,因此需要控制系统可以运行的内核数量。我正在尝试使用
ProcessorAffinity
类的 Process
属性来做到这一点。准确地说,我设置亲和力的代码如下:
void SetCPU(int numCPU)
{
var currentProcess = Process.GetCurrentProcess();
Debug.Assert(numCpu <= Environment.ProcessorCount)
var str = GetProcessorAffinityString(numCPU);
var serverProcessorAffinity = Convert.ToInt64(str, 2);
currentProcess.ProcessorAffinity = (IntPtr)serverProcessorAffinity;
}
//Produces output: numCpu=4 -> 0000 0000 0000 0000 0000 0000 0000 1111
//...............: numCpu=8 -> 0000 0000 0000 0000 0000 0000 1111 1111
//...............: numCpu=16 -> 0000 0000 0000 0000 1111 1111 1111 1111
//...
static string GetProcessorAffinityString(int numCPU)
{
var str = "";
for (int i = 0; i < Environment.ProcessorCount; i++)
{
if (i < numCPU) str += "1";
else str += "0";
}
//Reverse
char[] charArray = str.ToCharArray();
Array.Reverse(charArray);
return new string(charArray);
}
但是,当我运行程序时,无论输入
numCpu
是什么,进程始终使用32个核心。我可以在资源管理器中手动更改它作为解决方法,但理想情况下我希望以编程方式完成此操作。
有人知道这段代码可能有什么问题吗?
不要构建和解析一些 Int64 值(这里我很困惑你为什么这样做),而是尝试使用简单的位掩码来获取必要的 CPU 编号:
var cpuBitMask = 1 << (cpuNumber - 1);
这里
cpuNumber
是int
值,代表CPU(核心)数量(非从零开始)。
为了简化使用,可以创建一个扩展方法来设置
ProcessorAffinity
:
public static class ProcessExtensions
{
public static void SetProcessorAffinity(this Process process, int cpuNumber)
{
if (process is null)
throw new ArgumentNullException(nameof(process));
if (cpuNumber <= 0 || cpuNumber > Environment.ProcessorCount)
throw new ArgumentOutOfRangeException("CPU number must be greater than 0 and less than total CPUs amount");
var cpuBitMask = 1 << (cpuNumber - 1);
process.ProcessorAffinity = (IntPtr)cpuBitMask;
}
}
以及 ofc,使用示例(控制台应用程序):
public static void Main()
{
using var currentProcess = Process.GetCurrentProcess();
for (var cpuNumber = 1; cpuNumber <= Environment.ProcessorCount; cpuNumber++)
{
currentProcess.SetProcessAffinity(cpuNumber);
Console.WriteLine($"Running single-CPU test for 10 seconds on CPU #{cpuNumber}...");
// Primitive CPU (core) loadout for 10 seconds
var timer = Stopwatch.StartNew();
while (timer.Elapsed.TotalSeconds < 10) { }
timer.Reset();
}
Console.ReadKey();
}