当前位置: 首页 > 知识库问答 >
问题:

< =比

谷梁卓
2023-03-14

我在 SO 上发现了一些关于性能比较的问题

我为比较编写了一个程序(不太好使…复制到您的机器上运行它),其中我为(int i=0; i创建了两个循环

我运行每种方法100次;取了平均经过的时间,发现循环

我多次运行该程序,并且

3018.73, 2778.22

2816.87, 2760.62

2859.02, 2797.05

我的问题是:如果没有一个更快,为什么我会看到结果的差异?我的程序有什么问题吗?


共有2个答案

楚宏胜
2023-03-14

首先,有很多很多理由可以看到基准的变化,即使它们做得很好。以下是我想到的几个:

  • 您的计算机同时运行许多其他进程,在上下文中切换和切换内容,等等。操作系统不断接收和处理来自各种 I/O 设备等的中断。所有这些因素都可能导致计算机暂停一段时间,使正在测试的实际代码的运行时间相形见绌。
  • JIT 进程可以检测函数何时运行了一定次数,并根据该信息对其应用其他优化。诸如循环展开之类的事情可以大大减少程序必须进行的跳转次数,这比典型的CPU操作要昂贵得多。重新优化指令在第一次发生时需要时间,然后在那之后加快速度。
  • 您的硬件正在尝试进行其他优化,例如分支预测,以确保尽可能高效地使用其管道。(如果它猜对了,它基本上可以假装它会在等待查看是否

其次,要做好基准测试实际上真的很难。这是我已经使用了一段时间的基准模板。它并不完美,但它非常擅长确保任何新兴模式不太可能基于执行顺序或随机机会:

/* 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中运行此程序时得到的结果:

所以你会注意到有一些变化,特别是在早期,但是在来回运行足够多的时间后,没有一个清晰的模式出现来表明一种方式比另一种方式快得多或慢得多。

黎苑博
2023-03-14

基准测试是一门艺术。你所描述的在物理上是不可能的,即

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%或更少的差异没有统计学意义。找出小于这个数值的差异是相当困难的,你必须非常仔细地研究机器代码。一些愚蠢的事情,比如分支目标没有对齐或者变量没有存储在处理器寄存器中,都会对执行时间产生很大的影响。这不是你可以修复的,抖动没有足够的旋钮来调整。

 类似资料:
  • 本文向大家介绍从发展历程、产品机制、运营机制、发展瓶颈等方面对2-3个产品进行比较。备选的有头条系的抖音、火山,快手、小红书、微博、朋友圈、B站、Ins等等。相关面试题,主要包含被问及从发展历程、产品机制、运营机制、发展瓶颈等方面对2-3个产品进行比较。备选的有头条系的抖音、火山,快手、小红书、微博、朋友圈、B站、Ins等等。时的应答技巧和注意事项,需要的朋友参考一下 抖音vs快手 从发展历程、产

  • 本文向大家介绍你如何看待子弹短信?从产品的角度,简要对比分析微信和子弹短信。相关面试题,主要包含被问及你如何看待子弹短信?从产品的角度,简要对比分析微信和子弹短信。时的应答技巧和注意事项,需要的朋友参考一下 1. 产品定位: 子弹短信 定位只是一个通讯工具。 微信 已经从通讯工具泛化为一个包含支付、小程序、公众号生态的平台级应用。 2. 目标用户: 子弹短信 想解决那些日处理消息量非常大的人群的需

  • 问题内容: 我想结合两个查询 然后计算百分比(将第二个查询除以第一个查询)。我想在一个查询中实现这一目标。到目前为止我尝试过的是: 我得到的是: 我想要的是: 问题答案: 这应该给您您想要的: 编辑:没注意到这是为Access。我不知道Access中是否可用,因此您可能需要使用一个等效函数来确保整数不会简单地产生1或0。Access可能会自动将除法转换为小数,但在SQL Server则不然。

  • 问题内容: 我有一个表,其中包含有关事件和节日的数据,下面的列记录了它们的开始和结束日期。 开始日期 结束日期 日期格式为。我需要在以下情况下获取事件详细信息。 需要获取所有从当前月份开始的事件,并且结束日期可以说什么。 我对结束日期的概念很清楚。但不确定如何获取开始日期为当前月份的数据。为此,我需要将当前年份和当前月份与数据库中的列进行比较。 谁能帮助我指出我该怎么做? 问题答案:

  • 问题内容: 我有一个带有〜已知二进制序列的字节数组。我需要确认二进制序列是应该的。除之外,我还尝试了其他方法,但均无济于事。 问题答案: 在您的示例中,您具有: 在处理对象时,java中会比较 参考值 。您正在检查对by返回的数组的引用是否与所保存的引用相同,这当然永远不会正确。此外,数组类不会覆盖,因此其行为仅是比较参考值。 为了比较两个数组的 内容 ,Arrays类提供了静态数组比较方法。

  • 问题内容: 我正在使用以下方法比较junit中的文本文件: 这是比较文本文件的好方法吗?什么是首选? 问题答案: junit-addons对它有很好的支持:FileAssert 它为您提供了如下异常:

  • 问题内容: 我有两个列表 , 都包含 MyData 类型的对象,而 MyData* 包含这些变量。 利斯塔和数组listB都包含MyData的对象,现在我要两个列表的对象值比较这里 的名字 ,以及 检查 变量一样,如果 利斯塔 包含这些对象值 和ListB也包含 然后我必须比较列表并返回false,因为两个列表相同但是如果ListA包含 和 ListB 包含 然后我必须比较列表并返回true,因为

  • 问题内容: 最近,我注意到声明包含64个元素的数组比声明具有65个元素的相同类型的数组要快得多(> 1000倍)。 这是我用来测试的代码: 这将运行在大约6毫秒,如果我更换用它需要大约7秒。如果作业分布在越来越多的线程中,那么这个问题就会成倍地恶化,这就是我的问题所在。 不同类型的数组(例如或)也会发生此问题。大字符串不会发生此问题:,但将其更改为时确实会发生 我想知道为什么会这样,是否有可能规避