我正在为我的大学制作一个图像隐写术项目。我已完成项目并保留了几种不同的算法来隐藏图像中的数据。
我想问的是,在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种不同的Color和GrayScale图像算法。
没有多线程,因此不会成为问题。 Theres只是一个主线程。
答案 0 :(得分:14)
这是秒表的有用扩展方法:
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());
答案 1 :(得分:12)
System.Environment.TickCount和System.Diagnostics.Stopwatch类是两个适用于更精细分辨率和直接使用的类。
另见:
答案 2 :(得分:4)
您可以使用StopWatch
类:
var timer = System.Diagnostics.StopWatch.StartNew();
hideDataUsingAlgorithm();
timer.Stop();
var elapsed = timer.ElapsedMilliseconds;
答案 3 :(得分:0)
您可以向您的测试方法声明一个委托,并使用以下扩展方法之一执行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);
}
}
}
答案 4 :(得分:0)
您也可以使用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。