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

时间:2014-10-15 19:55:18

标签: c# .net multithreading processor affinity

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

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

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

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

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

1 个答案:

答案 0 :(得分:5)

基于对你的问题的评论(感谢所有人,尤其是@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);

Intel Core 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