如何确定哪些逻辑核心共享同一物理核心

本文关键字:核心 共享 何确定 | 更新日期: 2023-09-27 18:26:36

我正在开发一个工具,让学生能够自我评估编程作业的表现。特别是,他们正在编写的程序是多线程的,我没有直接的方法来影响创建的线程数量。我想比较他们的程序在不同内核数量的情况下的性能(理想情况下,他们的程序应该与允许使用的内核数量大致成比例地加速)。

我们可以将一个位掩码传递给Process.SetAffinity来控制程序使用的核心。

这在使用超线程并将每个物理内核拆分为两个逻辑内核的i5和i7机器上是有问题的。我希望程序在两个/四个不同的物理核心上运行。在我的i7机器上,亲和性设置为3(核心0&1)的进程运行速度与单个核心上的程序运行速度大致相同(表示这些逻辑核心共享同一物理核心),但亲和性设置成5(核心0&3)的进程将运行得更快(表示这些核心使用不同的物理核心)。然而,我还没有找到一种可靠的方法(除了试错)来确定这一点。

我如何(在没有实验的情况下)确定哪些逻辑核心共享同一物理核心?

(/proc/cpuinfo有我需要的信息,但在Windows机器上不可用。)

如何确定哪些逻辑核心共享同一物理核心

根据对您问题的评论(感谢所有人,尤其是@RLH),我为您制作了这门课:

/// <summary>
/// Provides CPU information
/// </summary>
public static class Processor
{
    private static IHardwareCore[] cores;
    private static int[] logicalCores;
    /// <summary>
    /// Hardware core
    /// </summary>
    public interface IHardwareCore 
    {
        /// <summary>
        /// Logical core IDs
        /// </summary>
        int[] LogicalCores { get; }
    }
    /// <summary>
    /// Hardware cores
    /// </summary>
    public static IHardwareCore[] HardwareCores
    {
        get
        {
            return cores ?? (cores = GetLogicalProcessorInformation()
                .Where(x => x.Relationship == LOGICAL_PROCESSOR_RELATIONSHIP.RelationProcessorCore)
                .Select(x => new HardwareCore((UInt64)x.ProcessorMask))
                .ToArray<IHardwareCore>());
        }
    }
    /// <summary>
    /// All logical core IDs
    /// </summary>
    public static int[] LogicalCores
    {
        get
        {
            return logicalCores ?? (logicalCores = HardwareCores
                .SelectMany(x => x.LogicalCores)
                .ToArray());
        }
    }
    /// <summary>
    /// Current logical core ID
    /// </summary>
    public static int CurrentLogicalCore
    {
        get { return GetCurrentProcessorNumber(); }
    }
    private class HardwareCore : IHardwareCore
    {
        public HardwareCore(UInt64 logicalCoresMask)
        {
            var logicalCores = new List<int>();
            for (var i = 0; i < 64; ++i)
            {
                if (((logicalCoresMask >> i) & 0x1) == 0) continue;
                logicalCores.Add(i);
            }
            LogicalCores = logicalCores.ToArray();
        }
        public int[] LogicalCores { get; private set; }
    }
    #region Exports
    [StructLayout(LayoutKind.Sequential)]
    private struct PROCESSORCORE
    {
        public byte Flags;
    };
    [StructLayout(LayoutKind.Sequential)]
    private struct NUMANODE
    {
        public uint NodeNumber;
    }
    private enum PROCESSOR_CACHE_TYPE
    {
        CacheUnified,
        CacheInstruction,
        CacheData,
        CacheTrace
    }
    [StructLayout(LayoutKind.Sequential)]
    private struct CACHE_DESCRIPTOR
    {
        public byte Level;
        public byte Associativity;
        public ushort LineSize;
        public uint Size;
        public PROCESSOR_CACHE_TYPE Type;
    }
    [StructLayout(LayoutKind.Explicit)]
    private struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION
    {
        [FieldOffset(0)]
        public PROCESSORCORE ProcessorCore;
        [FieldOffset(0)]
        public NUMANODE NumaNode;
        [FieldOffset(0)]
        public CACHE_DESCRIPTOR Cache;
        [FieldOffset(0)]
        private UInt64 Reserved1;
        [FieldOffset(8)]
        private UInt64 Reserved2;
    }
    private enum LOGICAL_PROCESSOR_RELATIONSHIP
    {
        RelationProcessorCore,
        RelationNumaNode,
        RelationCache,
        RelationProcessorPackage,
        RelationGroup,
        RelationAll = 0xffff
    }
    private struct SYSTEM_LOGICAL_PROCESSOR_INFORMATION
    {
        public UIntPtr ProcessorMask;
        public LOGICAL_PROCESSOR_RELATIONSHIP Relationship;
        public SYSTEM_LOGICAL_PROCESSOR_INFORMATION_UNION ProcessorInformation;
    }
    [DllImport(@"kernel32.dll", SetLastError = true)]
    private static extern bool GetLogicalProcessorInformation(
        IntPtr Buffer,
        ref uint ReturnLength
    );
    private const int ERROR_INSUFFICIENT_BUFFER = 122;
    private static SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] GetLogicalProcessorInformation()
    {
        uint ReturnLength = 0;
        GetLogicalProcessorInformation(IntPtr.Zero, ref ReturnLength);
        if (Marshal.GetLastWin32Error() == ERROR_INSUFFICIENT_BUFFER)
        {
            IntPtr Ptr = Marshal.AllocHGlobal((int)ReturnLength);
            try
            {
                if (GetLogicalProcessorInformation(Ptr, ref ReturnLength))
                {
                    int size = Marshal.SizeOf(typeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
                    int len = (int)ReturnLength / size;
                    SYSTEM_LOGICAL_PROCESSOR_INFORMATION[] Buffer = new SYSTEM_LOGICAL_PROCESSOR_INFORMATION[len];
                    IntPtr Item = Ptr;
                    for (int i = 0; i < len; i++)
                    {
                        Buffer[i] = (SYSTEM_LOGICAL_PROCESSOR_INFORMATION)Marshal.PtrToStructure(Item, typeof(SYSTEM_LOGICAL_PROCESSOR_INFORMATION));
                        Item += size;
                    }
                    return Buffer;
                }
            }
            finally
            {
                Marshal.FreeHGlobal(Ptr);
            }
        }
        return null;
    }
    [DllImport(@"kernel32.dll", SetLastError = true)]
    private static extern int GetCurrentProcessorNumber();
    #endregion
}

用法示例:

for (var i = 0; i < Processor.HardwareCores.Length; ++i)
{
    Console.WriteLine("Hardware Core {0} has logical cores {1}", i,
        string.Join(", ", Processor.HardwareCores[i].LogicalCores));
}
Console.WriteLine("All logical cores: " + string.Join(", ", Processor.LogicalCores));
Console.WriteLine("Current Logical Core is " + Processor.CurrentLogicalCore);

英特尔酷睿i5的输出示例:

Hardware Core 0 has logical cores 0, 1
Hardware Core 1 has logical cores 2, 3
All logical cores: 0, 1, 2, 3
Current Logical Core is 2