紧循环中对象初始化的执行时间

本文关键字:执行时间 初始化 对象 循环 | 更新日期: 2023-09-27 18:25:13

我在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

这需要花费3100ms的运行时间。然后,如果我在for中进行对象初始化,则该时间增加到7200ms,我的代码块如下所示;

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

然而,如果我初始化了我的对象,但没有使用其中的任何方法,则需要花费652ms。我的代码和这个一样,

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是不可比的。是什么导致了前两者之间如此大的差异?

紧循环中对象初始化的执行时间

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

因此,有三件事需要记住:

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

当然,这是一种概括——无论你调用多少次,许多方法都需要相同的时间,而其他方法可能在前10次调用时速度较慢,之后速度较快。我相信,在GetHashCode()的情况下,它是"第一次呼叫是昂贵的"领域。用一种简单的方式覆盖GetHashCode()的类型来尝试它,我怀疑你会发现所花费的时间直线下降。

此外,GetType()可能需要一段时间才能在第一次调用object时为其构建Type——我不确定。基本上,你在这里测量的是一堆不同的东西,这总是导致难以分析。

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