C# 如何确定哪些逻辑核共享相同的物理核?

C# 如何确定哪些逻辑核共享相同的物理核?,c#,.net,multithreading,processor,affinity,C#,.net,Multithreading,Processor,Affinity,我正在开发一种工具,允许学生自我评估编程作业的性能。特别是,他们正在编写的程序是多线程的,我无法直接影响创建的线程数。我想比较一下他们的程序在不同核数下的性能(理想情况下,他们的程序的速度应该与允许使用的核数大致成比例) 我们可以将位掩码传递给Process.SetAffinity以控制程序使用的内核 这在使用超线程并将每个物理内核拆分为两个逻辑内核的i5和i7计算机上是有问题的。我希望程序在两个/四个不同的物理内核上运行。在我的i7机器上,关联设置为3(核心0和1)的进程的运行速度与单个核心上

我正在开发一种工具,允许学生自我评估编程作业的性能。特别是,他们正在编写的程序是多线程的,我无法直接影响创建的线程数。我想比较一下他们的程序在不同核数下的性能(理想情况下,他们的程序的速度应该与允许使用的核数大致成比例)

我们可以将位掩码传递给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
}

这些信息可能通过物理/虚拟内核的计数(或者至少通过物理/虚拟内核的计数)获得,可以将其有效地外推到交错位掩码中。看起来我忘了提到我正在专门寻找C#解决方案。我确实添加了C#标记,但我没有在任何地方提到它。我现在在标题中添加了C#,这应该会让我更清楚地了解我在寻找什么。正如@user2864740所说,您可以使用WMI来发现有关处理器以及物理和虚拟内核数量的信息。相似性掩码的排序方式可能是每一位都是按顺序排列的虚拟处理器。因此,如果您有2个物理核和4个虚拟核(每个物理核两个虚拟核),那么位0-1是第一个物理核,位2-3是第二个核。另外,请注意,代码在映射到同一物理核的HT核上运行的速度很可能与在两个单独的物理核上运行的速度一样快。测量两个物理与一个物理上的两个逻辑之间的差异仍然是一个好主意,但HT实际上是一项非常棒的技术。当然,如果你正在做大量的同步I/O,这两种情况之间的差别很小,这也是一个有趣的信息。是的,分组的内核将在一个ID范围内,因此单CPU系统上的物理内核与逻辑内核的数量应该足够了。我想写一篇文章的答案,但我没有足够的时间收集足够的源和信息。但是,如果您不害怕C#中的COM调用,请参阅这篇文章:这篇文章中的信息与此问题相关,应该可以帮助您找到解决方案。如果你自己解决了这个问题,请发表一个答案。
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);
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