我在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。也许答案就在那里。