关于performance comparison of < and <=我发现了一些关于SO的问题(这个问题非常低调)我总是找到相同的答案,两者之间没有性能差异。
我编写了一个比较程序(not so working fiddle...copy to your machine to run it),我在其中用两种不同的方法创建了两个循环for (int i = 0; i <= 1000000000; i++ )
和for (int i = 0; i < 1000000001; i++ )
。
我跑了100次;花了平均经过的时间,发现<=
运算符的循环比运行<
的运算符慢。
我多次运行程序,<=
总是花费更多时间来完成。
我的结果(im ms)是:
3018.73,2772.22
2816.87,2760.62
2859.02,2797.05
我的问题是: 如果两者都不快,为什么我会看到结果的差异?我的程序有什么问题吗?
答案 0 :(得分:10)
基准测试是一门艺术。您所描述的内容在物理上是不可能的,&lt; =和&lt;运算符只生成以精确相同速度执行的不同处理器指令。我稍微修改了你的程序,运行DoIt十次并从for()循环中删除两个零,所以我不必等待永远:
x86抖动:
Less Than Equal To Method Time Elapsed: 0.5
Less Than Method Time Elapsed: 0.42
Less Than Equal To Method Time Elapsed: 0.36
Less Than Method Time Elapsed: 0.46
Less Than Equal To Method Time Elapsed: 0.4
Less Than Method Time Elapsed: 0.34
Less Than Equal To Method Time Elapsed: 0.33
Less Than Method Time Elapsed: 0.35
Less Than Equal To Method Time Elapsed: 0.35
Less Than Method Time Elapsed: 0.32
Less Than Equal To Method Time Elapsed: 0.32
Less Than Method Time Elapsed: 0.32
Less Than Equal To Method Time Elapsed: 0.34
Less Than Method Time Elapsed: 0.32
Less Than Equal To Method Time Elapsed: 0.32
Less Than Method Time Elapsed: 0.31
Less Than Equal To Method Time Elapsed: 0.34
Less Than Method Time Elapsed: 0.32
Less Than Equal To Method Time Elapsed: 0.31
Less Than Method Time Elapsed: 0.32
x64抖动:
Less Than Equal To Method Time Elapsed: 0.44
Less Than Method Time Elapsed: 0.4
Less Than Equal To Method Time Elapsed: 0.44
Less Than Method Time Elapsed: 0.45
Less Than Equal To Method Time Elapsed: 0.36
Less Than Method Time Elapsed: 0.35
Less Than Equal To Method Time Elapsed: 0.38
Less Than Method Time Elapsed: 0.34
Less Than Equal To Method Time Elapsed: 0.33
Less Than Method Time Elapsed: 0.34
Less Than Equal To Method Time Elapsed: 0.34
Less Than Method Time Elapsed: 0.32
Less Than Equal To Method Time Elapsed: 0.32
Less Than Method Time Elapsed: 0.35
Less Than Equal To Method Time Elapsed: 0.32
Less Than Method Time Elapsed: 0.42
Less Than Equal To Method Time Elapsed: 0.32
Less Than Method Time Elapsed: 0.31
Less Than Equal To Method Time Elapsed: 0.32
Less Than Method Time Elapsed: 0.35
您从中获得的唯一真实信号是第一个DoIt()的执行速度慢,在测试结果中也可见,这是抖动开销。而最重要的信号是嘈杂。两个循环的中值大致相等,标准偏差相当大。
否则,当您进行微优化时,您总是得到的那种信号,代码的执行不是很确定。从通常很容易消除的.NET运行时开销来看,您的程序并不是唯一在您的计算机上运行的程序。它必须共享处理器,只是WriteLine()调用已经有影响。由conhost.exe进程执行,与您的测试同时运行,同时您的测试代码进入下一个for()循环。在你的机器,内核代码和中断处理程序上发生的其他事情也可以轮到他们了。
并且codegen可以发挥作用,例如,您应该做的一件事就是交换两个调用。处理器本身通常非常不确定地执行代码。处理器缓存的状态以及分支预测逻辑收集了多少历史数据非常重要。
当我进行基准测试时,我认为15%或更低的差异无统计学意义。寻找低于此值的差异非常困难,您必须非常仔细地研究机器代码。愚蠢的事情,如分支目标未对齐或变量未存储在处理器寄存器中,可能会对执行时间产生很大影响。不是你可以修复的东西,抖动没有足够的旋钮来调整。
答案 1 :(得分:4)
首先,有很多很多理由看到基准测试的变化,即使它们做得正确也是如此。以下是一些想到的内容:
i++
或<
比较完成时等待<=
,然后如果找到则丢弃结果这是错误的。)这些优化的影响取决于很多因素,并不容易预测。其次,实际上很难做好基准测试。这是我已经使用了一段时间的基准模板。它并不完美,但它非常适合确保任何新兴模式不太可能基于执行顺序或随机机会:
/* This is a benchmarking template I use in LINQPad when I want to do a
* quick performance test. Just give it a couple of actions to test and
* it will give you a pretty good idea of how long they take compared
* to one another. It's not perfect: You can expect a 3% error margin
* under ideal circumstances. But if you're not going to improve
* performance by more than 3%, you probably don't care anyway.*/
void Main()
{
// Enter setup code here
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; // Tweak this as necessary
TimeActions(TimesToRun, actions);
}
#region timer helper methods
// Define other methods and classes here
public void TimeActions(int iterations, params TimedAction[] actions)
{
Stopwatch s = new Stopwatch();
int length = actions.Length;
var results = new ActionResult[actions.Length];
// Perform the actions in their initial order.
for(int i = 0; i < length; i++)
{
var action = actions[i];
var result = results[i] = new ActionResult{Message = action.Message};
// Do a dry run to get things ramped up/cached
result.DryRun1 = s.Time(action.Action, 10);
result.FullRun1 = s.Time(action.Action, iterations);
}
// Perform the actions in reverse order.
for(int i = length - 1; i >= 0; i--)
{
var action = actions[i];
var result = results[i];
// Do a dry run to get things ramped up/cached
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中运行时获得的结果:
所以你会注意到有一些变化,特别是在早期,但是在向后和向后运行所有东西之后,没有一个明确的模式表明一种方式比另一种更快或更慢。< / p>