【问题标题】:Find Execution time of a Method查找方法的执行时间
【发布时间】:2013-04-14 08:36:31
【问题描述】:

我正在为我的大学制作一个图像隐写术项目。我已经完成了这个项目,并保留了几种不同的算法来隐藏图像中的数据。

我想问的是,在 C# 中有什么方法可以找到程序中两点之间的执行/运行时间。 例如

//Some Code
//Code to start recording the time.
hideDataUsingAlgorithm();
//Code to stop recording and get the time of execution of the above function. 

我想这样做是为了展示简单(耗时较少)和更高效但耗时的算法(使用相同的数据和相同的图像)之间的区别。我有大约 10 种不同的彩色和灰度图像算法。

没有多线程,所以不会有问题。只有一个主线程。

【问题讨论】:

  • Measuring code execution time 的可能重复项 -- 这是几天前的,但 note, "一般规则是保留问题的最佳答案集合,然后关闭另一个作为副本。"

标签: c# .net


【解决方案1】:

这是一个非常有用的秒表扩展方法:

public static class StopwatchExt
{
    public static string GetTimeString(this Stopwatch stopwatch, int numberofDigits = 1)
    {
        double time = stopwatch.ElapsedTicks / (double)Stopwatch.Frequency;
        if (time > 1)
            return Math.Round(time, numberofDigits) + " s";
        if (time > 1e-3)
            return Math.Round(1e3 * time, numberofDigits) + " ms";
        if (time > 1e-6)
            return Math.Round(1e6 * time, numberofDigits) + " µs";
        if (time > 1e-9)
            return Math.Round(1e9 * time, numberofDigits) + " ns";
        return stopwatch.ElapsedTicks + " ticks";
    }
}

像这样使用它:

Stopwatch stopwatch = Stopwatch.StartNew();
//Call your method here
stopwatch.Stop();
Console.WriteLine(stopwatch.GetTimeString());

【讨论】:

    【解决方案2】:

    【讨论】:

    • 绝对使用Stopwatch 类进行性能测试。 TickCount 对于基准测试并不是特别有用。
    【解决方案3】:

    你可以使用StopWatch类:

    var timer = System.Diagnostics.StopWatch.StartNew();
    hideDataUsingAlgorithm();
    timer.Stop();
    var elapsed = timer.ElapsedMilliseconds;
    

    【讨论】:

      【解决方案4】:

      您可以向您的测试方法声明一个委托,并使用以下扩展方法之一来执行它 N 次。根据传递的格式字符串,您将打印到控制台:

      • 第一次通话时间
      • 经过的时间
      • 通话频率

      这些都是有用的值。扩展方法使用秒表来获得最高精度。

      Action acc = hideDataUsingAlgorithm;
      acc.Profile(100*1000, "Method did run {runs} times in {time}s, Frequency: {frequency}");
      

      要检查你可以使用的启动效果

      acc.ProfileFirst(100*1000, "First call {0}s", "Method did run {runs} times in {time}s, Frequency: {frequency}");
      

      通过这种方式,您可以轻松地检查您的方法,如果所讨论的方法不是一个会扭曲时间的空方法,因为委托调用将与您的方法调用相当。最初的想法在博客here.

      对于更深入的调用时间分析,分析器也非常有用。您也应该尝试使用这些来诊断更棘手的问题。

      using System;
      using System.Globalization;
      using System.Diagnostics;
      
      namespace PerformanceTester
      {
         /// <summary>
         /// Helper class to print out performance related data like number of runs, elapsed time and frequency
         /// </summary>
         public static class Extension
         {
             static NumberFormatInfo myNumberFormat;
      
             static NumberFormatInfo NumberFormat
             {
                 get
                 {
                     if (myNumberFormat == null)
                     {
                         var local = new CultureInfo("en-us", false).NumberFormat;
                         local.NumberGroupSeparator = " "; // set space as thousand separator
                         myNumberFormat = local; // make a thread safe assignment with a fully initialized variable
                     }
                     return myNumberFormat;
                 }
             }
      
             /// <summary>
             /// Execute the given function and print the elapsed time to the console.
             /// </summary>
             /// <param name="func">Function that returns the number of iterations.</param>
             /// <param name="format">Format string which can contain {runs} or {0},{time} or {1} and {frequency} or {2}.</param>
             public static void Profile(this Func<int> func, string format)
             {
      
                 Stopwatch watch = Stopwatch.StartNew();
                 int runs = func();  // Execute function and get number of iterations back
                 watch.Stop();
      
                 string replacedFormat = format.Replace("{runs}", "{3}")
                                               .Replace("{time}", "{4}")
                                               .Replace("{frequency}", "{5}");
      
                 // get elapsed time back
                 float sec = watch.ElapsedMilliseconds / 1000.0f;
                 float frequency = runs / sec; // calculate frequency of the operation in question
      
                 try
                 {
                     Console.WriteLine(replacedFormat,
                                         runs,  // {0} is the number of runs
                                         sec,   // {1} is the elapsed time as float
                                         frequency, // {2} is the call frequency as float
                                         runs.ToString("N0", NumberFormat),  // Expanded token {runs} is formatted with thousand separators
                                         sec.ToString("F2", NumberFormat),   // expanded token {time} is formatted as float in seconds with two digits precision
                                         frequency.ToString("N0", NumberFormat)); // expanded token {frequency} is formatted as float with thousands separators
                 }
                 catch (FormatException ex)
                 {
                     throw new FormatException(
                         String.Format("The input string format string did contain not an expected token like "+
                                       "{{runs}}/{{0}}, {{time}}/{{1}} or {{frequency}}/{{2}} or the format string " +
                                       "itself was invalid: \"{0}\"", format), ex);
                 }
             }
      
             /// <summary>
             /// Execute the given function n-times and print the timing values (number of runs, elapsed time, call frequency)
             /// to the console window.
             /// </summary>
             /// <param name="func">Function to call in a for loop.</param>
             /// <param name="runs">Number of iterations.</param>
             /// <param name="format">Format string which can contain {runs} or {0},{time} or {1} and {frequency} or {2}.</param>
             public static void Profile(this Action func, int runs, string format)
             {
                 Func<int> f = () =>
                 {
                     for (int i = 0; i < runs; i++)
                     {
                         func();
                     }
                     return runs;
                 };
                 f.Profile(format);
             }
      
             /// <summary>
             /// Call a function in a for loop n-times. The first function call will be measured independently to measure
             /// first call effects.
             /// </summary>
             /// <param name="func">Function to call in a loop.</param>
             /// <param name="runs">Number of iterations.</param>
             /// <param name="formatFirst">Format string for first function call performance.</param>
             /// <param name="formatOther">Format string for subsequent function call performance.</param>
             /// <remarks>
             /// The format string can contain {runs} or {0},{time} or {1} and {frequency} or {2}.
             /// </remarks>
             public static void ProfileWithFirst(this Action func, int runs, string formatFirst, string formatOther)
             {
                 func.Profile(1, formatFirst);
                 func.Profile(runs - 1, formatOther);
             }
         }
      }
      

      【讨论】:

        【解决方案5】:

        你也可以使用BenchmarkDotNet

        然后你做:

        1) 创建一个控制台项目,并引用您要测试的代码。

        using BenchmarkDotNet.Running;
        using BenchmarkDotNet.Attributes;
        class Program
        {
            static void Main()
            {
                var summary = BenchmarkRunner.Run<YourBenchmarks>();
            }
        }
        
        public class YourBenchmarks
        {
            [Benchmark]
            public object HideDataUsingAlgorithm()
            {
                return Namespace.hideDataUsingAlgorithm(); // call the code you want to benchmark here
            }
        }
        

        2) 在发行版中构建并在没有调试器的情况下运行。

        3) 打开 bin/release/YourBenchmarks-report-stackoverflow.md 中的报告

        报告默认包含 Median 和 StdDev。 BenchmarkDotNet 负责预热并多次启动该过程以提供准确的统计数据。

        样本报告:

                         Method |      Median |    StdDev |
        ----------------------- |------------ |---------- |
         HideDataUsingAlgorithm | 252.4869 ns | 8.0261 ns |
        

        有关配置,请阅读docs

        【讨论】:

        • 如果我没有单独的课程怎么办?我想在同一个类中找到一个函数。
        猜你喜欢
        • 2018-07-03
        • 1970-01-01
        • 1970-01-01
        • 2018-01-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2021-10-19
        • 2012-11-17
        相关资源
        最近更新 更多