测量函数调用的CPU周期

时间:2014-10-28 21:41:29

标签: c# .net .net-4.5

我正在寻找一种方法来测量线程上函数调用所需的cpu周期。

伪代码示例:

void HostFunction()
{
     var startTick = CurrentThread.CurrentTick;  //does not exist

     ChildFunction();

     var endTick = CurrentThread.CurrentTick;  //does not exist

     var childFunctionCost = endTick - startTick;
}

void ChildFunction() 
{
    //Do whatever...

    Thread.Sleep(3000);

    //Do some more...
}

我不想使用秒表或其他一些时间测量,因为它会包含线程正在休眠的任何时间,我不想测量。我只想衡量实际工作。

这个测量需要在运行时工作,就像我的伪代码一样,因为结果用于确定是否应该允许子函数继续运行(我的实例是一个插件类型的体系结构),所以进行分析工具不会帮助我。

2 个答案:

答案 0 :(得分:9)

你可以对QueryThreadCycleTime()进行转发。请查看链接以获取详细信息。

一些示例代码:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;

class Program {
    static void Main(string[] args) {
        ulong start, end;
        start = NativeMethods.GetThreadCycles();
        System.Threading.Thread.Sleep(1000);
        end = NativeMethods.GetThreadCycles();
        ulong cycles = end - start;
        Debug.Assert(cycles < 200000);
    }

    static class NativeMethods {
        public static ulong GetThreadCycles() {
            ulong cycles;
            if (!QueryThreadCycleTime(PseudoHandle, out cycles))
                throw new System.ComponentModel.Win32Exception();
            return cycles;
        }
        [DllImport("kernel32.dll", SetLastError = true)]
        private static extern bool QueryThreadCycleTime(IntPtr hThread, out ulong cycles);
        private static readonly IntPtr PseudoHandle = (IntPtr)(-2);

    }
}

答案 1 :(得分:2)

根据我上面提供的评论,请考虑以下代码:

using System;
using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;

namespace FunctionTiming
{
    class Program
    {
        private static Thread _thread;
        private static IntPtr _threadHandle;

        static void Main(string[] args)
        {
            _thread = new Thread(new ThreadStart(Program.TargetFunction));
            _thread.Start();
            _thread.Join();

            System.Runtime.InteropServices.ComTypes.FILETIME start, end, rawKernelTime, rawUserTime;
            bool result = GetThreadTimes(_threadHandle, out start, out end, out rawKernelTime, out rawUserTime);
            Debug.Assert(result);

            ulong uLow = (ulong)rawKernelTime.dwLowDateTime;
            ulong uHigh = (uint)rawKernelTime.dwHighDateTime;
            uHigh = uHigh << 32;
            long kernelTime = (long)(uHigh | uLow);

            uLow = (ulong)rawUserTime.dwLowDateTime;
            uHigh = (uint)rawUserTime.dwHighDateTime;
            uHigh = uHigh << 32;
            long userTime = (long)(uHigh | uLow);

            Debug.WriteLine("Kernel time: " + kernelTime);
            Debug.WriteLine("User time: " + userTime);
            Debug.WriteLine("Combined raw execution time: " + (kernelTime + userTime));

            long functionTime = (kernelTime + userTime) / 10000;
            Debug.WriteLine("Funciton Time: " + functionTime + " milliseconds");
        }

        static void TargetFunction()
        {
            IntPtr processHandle = GetCurrentProcess();
            bool result = DuplicateHandle(processHandle, GetCurrentThread(), processHandle, out _threadHandle, 0, false, (uint)DuplicateOptions.DUPLICATE_SAME_ACCESS);

            double value = 9876543.0d;
            for (int i = 0; i < 100000; ++i)
                value = Math.Cos(value);

            Thread.Sleep(3000);

            value = 9876543.0d;
            for (int i = 0; i < 100000; ++i)
                value = Math.Cos(value);
        }

        [DllImport("kernel32.dll", SetLastError = true)]
        static extern bool GetThreadTimes(IntPtr hThread,
            out System.Runtime.InteropServices.ComTypes.FILETIME lpCreationTime, out System.Runtime.InteropServices.ComTypes.FILETIME lpExitTime,
            out System.Runtime.InteropServices.ComTypes.FILETIME lpKernelTime, out System.Runtime.InteropServices.ComTypes.FILETIME lpUserTime);

        [DllImport("kernel32.dll")]
        private static extern IntPtr GetCurrentThread();

        [DllImport("kernel32.dll", SetLastError = true)]
        [return: MarshalAs(UnmanagedType.Bool)]
        static extern bool DuplicateHandle(IntPtr hSourceProcessHandle,
            IntPtr hSourceHandle, IntPtr hTargetProcessHandle, out IntPtr lpTargetHandle,
            uint dwDesiredAccess, [MarshalAs(UnmanagedType.Bool)] bool bInheritHandle, uint dwOptions);

        [Flags]
        public enum DuplicateOptions : uint
        {
            DUPLICATE_CLOSE_SOURCE = (0x00000001),// Closes the source handle. This occurs regardless of any error status returned.
            DUPLICATE_SAME_ACCESS = (0x00000002), //Ignores the dwDesiredAccess parameter. The duplicate handle has the same access as the source handle.
        }

        [DllImport("kernel32.dll")]
        static extern IntPtr GetCurrentProcess();
    }
}

产生以下结果(在我的旧机器上):

Kernel time: 0
User time: 156250
Combined raw execution time: 156250
Function time: 15 milliseconds

你可以清楚地看到没有包括3秒的睡眠时间。 希望这会有所帮助。