首先,即使正确执行基准测试,也有很多原因会导致结果存在差异。以下是一些可能的原因:
- 您的计算机同时运行许多其他进程,切换上下文等。操作系统不断接收和处理来自各种I/O设备的中断等。所有这些都可能导致计算机暂停的时间超过您正在测试的实际代码的运行时间。
- JIT(Just-In-Time)编译器可以检测到函数已运行一定次数,并根据此信息应用额外的优化。例如,循环展开可以大大减少程序需要进行的跳转次数,而跳转比典型的CPU操作昂贵得多。重新优化指令需要时间,但之后会加速运行。
- 您的硬件正在尝试进行额外的优化,例如分支预测,以确保其流水线被尽可能有效地使用。(如果猜测正确,它可以在等待
<
或
<=
比较完成时假装执行
i++
,然后在发现错误时丢弃结果。)这些优化的影响取决于许多因素,不容易预测。
其次,实际上很难进行良好的基准测试。以下是我一直在使用的基准测试模板。它并不完美,但它确保任何出现的模式不太可能基于执行顺序或随机机会:
void Main()
{
var actions = new[]
{
new TimedAction("control", () =>
{
int i = 0;
}),
new TimedAction("<", () =>
{
for (int i = 0; i < 1000001; i++)
{}
}),
new TimedAction("<=", () =>
{
for (int i = 0; i <= 1000000; i++)
{}
}),
new TimedAction(">", () =>
{
for (int i = 1000001; i > 0; i--)
{}
}),
new TimedAction(">=", () =>
{
for (int i = 1000000; i >= 0; i--)
{}
})
};
const int TimesToRun = 10000;
TimeActions(TimesToRun, actions);
}
#region timer helper methods
public void TimeActions(int iterations, params TimedAction[] actions)
{
Stopwatch s = new Stopwatch();
int length = actions.Length;
var results = new ActionResult[actions.Length];
for(int i = 0; i < length; i++)
{
var action = actions[i];
var result = results[i] = new ActionResult{Message = action.Message};
result.DryRun1 = s.Time(action.Action, 10);
result.FullRun1 = s.Time(action.Action, iterations);
}
for(int i = length - 1; i >= 0; i--)
{
var action = actions[i];
var result = results[i];
result.DryRun2 = s.Time(action.Action, 10);
result.FullRun2 = s.Time(action.Action, iterations);
}
results.Dump();
}
public class ActionResult
{
public string Message {get;set;}
public double DryRun1 {get;set;}
public double DryRun2 {get;set;}
public double FullRun1 {get;set;}
public double FullRun2 {get;set;}
}
public class TimedAction
{
public TimedAction(string message, Action action)
{
Message = message;
Action = action;
}
public string Message {get;private set;}
public Action Action {get;private set;}
}
public static class StopwatchExtensions
{
public static double Time(this Stopwatch sw, Action action, int iterations)
{
sw.Restart();
for (int i = 0; i < iterations; i++)
{
action();
}
sw.Stop();
return sw.Elapsed.TotalMilliseconds;
}
}
#endregion
当我在LINQPad中运行时,得到的结果如下:
因此,您会注意到存在一些变化,特别是在早期阶段,但是在反复运行所有内容之后,没有明显的模式出现表明一种方式比另一种方式快或慢。
for
循环...在发布模式下它可能会被优化掉...所以你需要在里面添加一些代码。 - xanatosStopwatch
还是DateTime
?在程序整体上,这个运算符的2%差异会对你的程序产生很大影响吗?它们给出不同的结果 - 如果其中一个更快,为什么会很重要呢? - D Stanley