【问题标题】:Execution time of object initialization in a tight loop紧密循环中对象初始化的执行时间
【发布时间】:2012-02-21 12:24:36
【问题描述】:

我在 c# 中有三个方法运行相同的代码,但有一点不同,我的第一个代码块是

Stopwatch s = new Stopwatch();
object o = new object();
s.Start();
for (int i = 0; i < 100000000; i++)
{
    o.ToString();
    o.GetType();
    o.GetHashCode();
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds); //3100ms

这需要 3100 毫秒的运行时间。然后,如果我将 for 中的对象初始化增加到 7200 毫秒,我的代码块看起来像这样;

Stopwatch s = new Stopwatch();
s.Start();
for (int i = 0; i < 100000000; i++)
{
    object o = new object();
    o.ToString();
    o.GetType();
    o.GetHashCode();
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds);//7200ms

但是,如果我初始化我的对象但不使用其中的任何方法,则需要 652 毫秒。而我的代码就是这样的,

Stopwatch s = new Stopwatch();
s.Start();
for (int i = 0; i < 100000000; i++)
{
    object o = new object();
}
s.Stop();
Console.WriteLine(s.ElapsedMilliseconds);//625ms

所以我想知道,3100ms + 625ms 无法与 7200ms 相提并论。是什么导致前两者之间的差异如此之大?

【问题讨论】:

  • 这是在发布模式下编译并在 VS 之外运行的吗?
  • 调用你的 3 个object 方法怎么样?
  • @Menahem :我在没有调试的情况下调用它。
  • @ken2k :实际上并没有改变任何东西,记录仍然相同。

标签: c# .net for-loop benchmarking


【解决方案1】:

在您的第二个区块中,您在许多新对象上调用GetHashCode()。据我所知,第一次在对象上调用非覆盖 GetHashCode() 方法时,会为该对象分配一个同步块。这是相对昂贵的,尽管随后对同一对象的GetHashCode 调用(根据您的第一个代码)很便宜。

所以要记住三件事:

  • 对象分配(和任何 GC)的成本
  • 第一次调用方法的成本
  • 后续方法调用的成本

这当然是一个概括 - 许多方法花费相同的时间,无论您调用它们多少次,而其他方法对于(例如)前 10 次调用可能会很慢,然后会很快。我相信在GetHashCode() 的情况下,它是“第一次通话很昂贵”的领域。尝试使用以某种简单方式覆盖 GetHashCode() 的类型,我怀疑您会发现所花费的时间直线下降。

另外,GetType() 第一次被调用时可能需要一段时间来为object 构造Type - 我不确定。基本上你在这里同时测量一堆不同的东西,这总是会导致分析困难。

【讨论】:

  • 编译器是否有可能删除分配语句?
  • @Polity:我非常怀疑。除非它特别知道object 构造函数不能抛出异常,否则这将是无效的优化。
  • @JonSkeet :感谢您的快速回复,在我覆盖 GetHashCode() 方法后它真的改变了。但我不认为我理解“许多方法花费相同的时间,无论你调用它们多少次,而其他方法可能(比如)前 10 次调用很慢,然后很快”
  • @lazycoder:这完全取决于方法的作用。如果它懒惰地做一些昂贵的事情(如 GetHashCode 所做的那样),那么第一次会更昂贵。对于其他一些情况,费用会随着时间而变化(想想缓存)或者可能有一些固定数量的预分配结果等。基本上你不能假设一个方法总是需要相同的时间来运行。
  • @JonSkeet 哦,我现在明白了,你当时在谈论内部的业务逻辑。无论如何,谢谢。
【解决方案2】:

我会用 Refactor 或类似的东西反编译这些方法,然后查看生成的 IL。或许答案就在那里。

【讨论】:

  • 一些优化在 IL 中并不明显,例如。实例化将在第二个和第三个中都有,但是由于在第三个示例中从未使用分配的值,因此优化器有更多选项,因此仅 IL 可能是不够的
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 2019-08-07
  • 2014-07-10
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多