如何在C#中测量给定进程的CPU周期?

时间:2014-07-27 19:44:25

标签: c# performance cpu-usage

我有一些使用不同算法执行相同任务的不同模型。我想通过测量CPU周期来比较这些模型的性能(我尝试使用System.Diagnostics.StopWatch计算Ticks,但结果不够准确)。

我找到了一个使用P/Invoke测量CPU周期的class,如下所示:

IModel model;
CodeTimer.Time(true, model.ToString(), totalTime, model.TimeStep);

上述方法按方法N迭代model.TimeStep次。我注意到CodeTimer.Time的结果差异很大(至少一个数量级,从58 KCycles890 KCycles)。因此,如果我错了,请纠正我,但是当CodeTimer类标记进程前后的循环时,它还可以计算执行{{1}期间发生的任何其他进程(甚至是OS进程)所使用的循环方法(我想测量性能的方法)。

所以有两个问题:

  1. 我的上述假设是否正确?如果是,那么我需要提出另一种测量CPU周期的解决方案,这导致了我的主要问题:
  2. 然后我提出了在另一个model.TimeStep中迭代model.TimeStep()并使用QueryProcessCycleTime测量给定线程的周期的想法。但是,当System.Threading.Thread收到QueryProcessCycleTime(或System.Threading.WaitHandle)作为输入时,我不知道如何告诉此方法我想测量该特定的周期IntPtr我刚刚创建了。换句话说,我不知道如何将我创建的Thread放在一起来迭代模型threadWaitHandle,即使我已阅读了一些{ {3}}使用QueryProcessCycleTime(我无法将所有内容混合在一起)。

    1. 我怎样才能做到这一点?
    2. 这将是这样的:

      WaitHandle

      class ModelSimulator { public Model1 model1 { get; private set; } // implements IModel public Model2 model2 { get; private set; } // implements IModel /* other methods */ public void RunModel(object obj) { IModel model = (IModel)obj; Int32 t = model.maxTime; while (t-- > 0) model.TimeStep(); } } 方法中,我这样做:

      main
      PS:我不是examples的副本,因为我需要一些更具体的答案,而不是......我的问题可能是对这个问题的补充。

1 个答案:

答案 0 :(得分:0)

正如@ShlomiBorovitz所建议的,可以使用GetCurrentThread()来识别正在运行该进程的Thread。可能的解决方案是CodeTimer.cs中的一个非常简单的修改。所需的修改可以在本答案的最后找到。我添加了一个名为PerformanceStatus的类,以获得级别0,1和2的垃圾收集量以及用于度量性能的CodeTimer.Time()方法的结果。因此,人们还可以知道垃圾收集器是否在此过程中收集了任何内容。

然后您只需要使用它:

ModelSimulator nm = new ModelSimulator();
CodeTimer c = new CodeTimer(nm.RunModel, model); // model implements IModel
UInt64 time = c.Time().CPUCycles;
Console.WriteLine("{0:0.000e+000} KCy", (Double)time / 1000.0D);

这样在问题中给出了类ModelSimulator,带有一个方法ModelSimulator.RunModel(IModel model),它接收类型为IModel的对象。

以下是衡量效果所需的CodeTimer.cs

public sealed class CycleTime
{
    private Boolean m_trackingThreadTime;
    private SafeWaitHandle m_handle;
    private UInt64 m_startCycleTime;

    private CycleTime(Boolean trackingThreadTime, SafeWaitHandle handle)
    {
        m_trackingThreadTime = trackingThreadTime;
        m_handle = handle;
        m_startCycleTime = m_trackingThreadTime ? Thread() : Process(m_handle);
    }

    [CLSCompliant(false)]
    public UInt64 Elapsed()
    {
        UInt64 now = m_trackingThreadTime ? Thread(/*m_handle*/) : Process(m_handle);
        return now - m_startCycleTime;
    }

    public static CycleTime StartThread(SafeWaitHandle threadHandle)
    {
        return new CycleTime(true, threadHandle);
    }

    public static CycleTime StartProcess(SafeWaitHandle processHandle)
    {
        return new CycleTime(false, processHandle);
    }

    public static UInt64 Thread(IntPtr threadHandle)
    {
        UInt64 cycleTime;
        if (!QueryThreadCycleTime(threadHandle, out cycleTime))
            throw new Win32Exception();
        return cycleTime;
    }

    /// <summary>
    /// Retrieves the cycle time for the specified thread.
    /// </summary>
    /// <param name="threadHandle">Identifies the thread whose cycle time you'd like to obtain.</param>
    /// <returns>The thread's cycle time.</returns>
    [CLSCompliant(false)]
    public static UInt64 Thread(SafeWaitHandle threadHandle)
    {
        UInt64 cycleTime;
        if (!QueryThreadCycleTime(threadHandle, out cycleTime))
            throw new Win32Exception();
        return cycleTime;
    }

    [CLSCompliant(false)]
    public static UInt64 Thread()
    {
        UInt64 cycleTime;
        if (!QueryThreadCycleTime((IntPtr)(-2), out cycleTime))
            throw new Win32Exception();
        return cycleTime;
    }

    /// <summary>
    /// Retrieves the sum of the cycle time of all threads of the specified process.
    /// </summary>
    /// <param name="processHandle">Identifies the process whose threads' cycles times you'd like to obtain.</param>
    /// <returns>The process' cycle time.</returns>
    [CLSCompliant(false)]
    public static UInt64 Process(SafeWaitHandle processHandle)
    {
        UInt64 cycleTime;
        if (!QueryProcessCycleTime(processHandle, out cycleTime))
            throw new Win32Exception();
        return cycleTime;
    }

    /// <summary>
    /// Retrieves the cycle time for the idle thread of each processor in the system.
    /// </summary>
    /// <returns>The number of CPU clock cycles used by each idle thread.</returns>
    [CLSCompliant(false)]
    public static UInt64[] IdleProcessors()
    {
        Int32 byteCount = Environment.ProcessorCount;
        UInt64[] cycleTimes = new UInt64[byteCount];
        byteCount *= 8;   // Size of UInt64
        if (!QueryIdleProcessorCycleTime(ref byteCount, cycleTimes))
            throw new Win32Exception();
        return cycleTimes;
    }

    [DllImport("Kernel32", ExactSpelling = true, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern Boolean QueryThreadCycleTime(IntPtr threadHandle, out UInt64 CycleTime);

    [DllImport("Kernel32", ExactSpelling = true, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern Boolean QueryThreadCycleTime(SafeWaitHandle threadHandle, out UInt64 CycleTime);

    [DllImport("Kernel32", ExactSpelling = true, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern Boolean QueryProcessCycleTime(SafeWaitHandle processHandle, out UInt64 CycleTime);

    [DllImport("Kernel32", ExactSpelling = true, SetLastError = true)]
    [return: MarshalAs(UnmanagedType.Bool)]
    private static extern Boolean QueryIdleProcessorCycleTime(ref Int32 byteCount, UInt64[] CycleTimes);

    [DllImport("Kernel32", ExactSpelling = true, SetLastError = true)]
    public static extern IntPtr GetCurrentThread();
}

public sealed class CodeTimer //: IDisposable
{
    private Int32 m_collectionCount0;
    private Int32 m_collectionCount1;
    private Int32 m_collectionCount2;

    private Thread m_thread;
    private IModel m_model;
    private Action<IModel> m_performanceMethod;

    private UInt64 outThreadCycles;

    public CodeTimer(Action<IModel> perfMethod, IModel model)
    {
        PrepareForOperation();

        m_performanceMethod = perfMethod;
        m_model = model;
        m_thread = new Thread(PerformanceTest);
    }

    private void PerformanceTest()
    {
        PrepareForOperation();
        IntPtr p = CycleTime.GetCurrentThread();
        UInt64 t = CycleTime.Thread(p);
        m_performanceMethod(m_model);
        outThreadCycles = CycleTime.Thread(p) - t;
    }

    public PerformanceStatus Time()
    {
        m_thread.Start();
        m_thread.Join();
        return new PerformanceStatus(GC.CollectionCount(0) - m_collectionCount0, GC.CollectionCount(1) - m_collectionCount1, GC.CollectionCount(2) - m_collectionCount2, outThreadCycles);
    }

    private void PrepareForOperation()
    {
        GC.Collect();
        GC.WaitForPendingFinalizers();
        GC.Collect();
        m_collectionCount0 = GC.CollectionCount(0);
        m_collectionCount1 = GC.CollectionCount(1);
        m_collectionCount2 = GC.CollectionCount(2);
    }
}

public class PerformanceStatus
{
    public Int32 GCCount1;
    public Int32 GCCount2;
    public Int32 GCCount3;
    public UInt64 CPUCycles;
    public PerformanceStatus(Int32 gc1, Int32 gc2, Int32 gc3, UInt64 cpuCycles)
    {
        this.GCCount1 = gc1;
        this.GCCount2 = gc2;
        this.GCCount3 = gc3;
        this.CPUCycles = cpuCycles;
    }
}