2014-10-28 3 views
2

Я ищу способ измерения циклов процессора, когда вызов функции выполняется по потоку.Измерение циклов процессора вызова функции

Пример псевдокод:

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... 
} 

Я не хочу использовать Секундомер или некоторые другие измерения времени, потому что он будет включать в любое время, что поток спит, что я не хочу измерения. Я хочу только измерить реальную работу.

Это измерение должно работать во время выполнения, как и в моем псевдокоде, поскольку результаты используются для определения того, разрешено ли дочерней функции продолжать выполнение (мой реальный случай представляет собой архитектуру типа плагина), поэтому инструмент профилирования мне не поможет.

+3

считают это решение (GetThreadTimes): http://stackoverflow.com/questions/26472936/why-does-getthreadtimes-return/26475906#26475906 (не код, просто звонки). Кодекс серьезно нуждается в помощи, но разбивка результатов может представлять интерес. – Jeff

+0

Есть ли что-нибудь, что предотвращает появление плагина из дополнительных потоков и/или использования threadpool? Если нет, то планирование на основе текущего времени потока не даст желаемого результата. –

+0

@BrianRasmussen, да, я использую Mono.Cecil для сканирования всех типов Ref, и если используются какие-либо пространства имен/типы, которые управляют потоками, они автоматически исключаются. –

ответ

8

Вы можете pinvoke 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); 

    } 
} 
+0

Знаете ли вы, что значение, возвращаемое этой функцией, обновляется в (около) в режиме реального времени или имеет ту же проблему, что и GetThreadTimes, что она только обновляется при прерывании тактового сигнала? –

+0

Просто попробуйте сами, просто позвоните дважды. Да. –

+0

Хорошая точка. Извините за ленивость. Насколько я могу судить, он действительно обновляется на каждом звонке. –

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 секунды сна не включены. Надеюсь, это поможет.

+1

Вы не можете получить лучшее разрешение, чем скорость прерывания тактового сигнала. По умолчанию 64 прерывания/сек. Или 15.625 мс, как вы можете сказать. –

+0

@Jeff, Спасибо за ваш ответ. По сравнению с ответом Ханса, это выглядит довольно сложно. Есть ли польза от использования этого по сравнению с его? Ганс, тот же вопрос. –

+0

@ ХансПасант - Хорошая мысль. Спасибо за примечание. – Jeff

Смежные вопросы