Object.GetType()的性能

我们的应用程序中有很多日志logging调用。 我们的logging器需要一个System.Type参数,以便显示哪个组件创build了调用。 有时候,当我们感到困扰的时候,我们会这样做:

class Foo { private static readonly Type myType = typeof(Foo); void SomeMethod() { Logger.Log(myType, "SomeMethod started..."); } } 

因为这需要只获取一次Type对象。 但是,我们没有任何实际的指标。 任何人都知道这是多less节省下来调用this.GetType()每次我们login?

(我意识到我可以自己做没有大问题的指标,但嘿,什么是StackOverflow?)

我强烈怀疑,GetType()将比实际的日志logging花费更less的时间。 当然,有可能你的Logger.Log的调用不会做任何实际的IO …我仍然怀疑这种差异将是无关紧要的。

编辑:基准代码是在底部。 结果:

 typeof(Test): 2756ms TestType (field): 1175ms test.GetType(): 3734ms 

这就是所谓的方法1 亿次 – 优化几秒钟左右。 我怀疑真正的日志logging方法将会有更多的工作要做,而且调用1亿次会花费比4秒多得多的时间,即使它没有写出任何东西。 (当然,我可能是错的 – 你必须亲自尝试一下。)

换句话说,正常情况下,我会使用最易读的代码而不是微型优化。

 using System; using System.Diagnostics; using System.Runtime.CompilerServices; class Test { const int Iterations = 100000000; private static readonly Type TestType = typeof(Test); static void Main() { int total = 0; // Make sure it's JIT-compiled Log(typeof(Test)); Stopwatch sw = Stopwatch.StartNew(); for (int i = 0; i < Iterations; i++) { total += Log(typeof(Test)); } sw.Stop(); Console.WriteLine("typeof(Test): {0}ms", sw.ElapsedMilliseconds); sw = Stopwatch.StartNew(); for (int i = 0; i < Iterations; i++) { total += Log(TestType); } sw.Stop(); Console.WriteLine("TestType (field): {0}ms", sw.ElapsedMilliseconds); Test test = new Test(); sw = Stopwatch.StartNew(); for (int i = 0; i < Iterations; i++) { total += Log(test.GetType()); } sw.Stop(); Console.WriteLine("test.GetType(): {0}ms", sw.ElapsedMilliseconds); } // I suspect your real Log method won't be inlined, // so let's mimic that here [MethodImpl(MethodImplOptions.NoInlining)] static int Log(Type type) { return 1; } } 

GetType()函数用特殊属性[MethodImpl(MethodImplOptions.InternalCall)]标记。 这意味着它的方法体不包含IL,而是.NET CLR的内部钩子。 在这种情况下,它会查看对象元数据的二进制结构并在其周围构造一个System.Type对象。

编辑:我想我错了什么…

我说:“因为GetType()需要一个新的对象来构build”,但似乎这是不正确的。 不知何故,CLRcachingType并始终返回相同的对象,所以不需要构build新的Type对象。

我基于以下testing:

 Object o1 = new Object(); Type t1 = o1.GetType(); Type t2 = o1.GetType(); if (object.ReferenceEquals(t1,t2)) Console.WriteLine("same reference"); 

所以,我不期望你的实施获得很多收益。

我怀疑你会从这个问题上得到一个令人满意的答案。 原因是性能,特别是这种types的场景,是高度专用的。

有人可能会用一个简单的秒表回来,例如在原始毫秒的情况下会更快。 但坦率地说,这并不意味着你的应用程序。 为什么? 这很大程度上取决于特定场景的使用模式。 例如 …

  1. 你有几种types?
  2. 你有多大的方法?
  3. 你做这个每种方法,或只有大的?

这些只是几个会大大改变直线时间基准的相关性的问题。

就应用程序性能而言,差异可能是微不足道的。 但是,cachingtypes的第一种方法应该更快。 我们去testing吧。

这段代码会告诉你不同之处:

 using System; namespace ConsoleApplicationTest { class Program { static void Main(string[] args) { int loopCount = 100000000; System.Diagnostics.Stopwatch timer1 = new System.Diagnostics.Stopwatch(); timer1.Start(); Foo foo = new Foo(); for (int i = 0; i < loopCount; i++) { bar.SomeMethod(); } timer1.Stop(); Console.WriteLine(timer1.ElapsedMilliseconds); System.Diagnostics.Stopwatch timer2 = new System.Diagnostics.Stopwatch(); timer2.Start(); Bar bar = new Bar(); for (int i = 0; i < loopCount; i++) { foo.SomeMethod(); } timer2.Stop(); Console.WriteLine(timer2.ElapsedMilliseconds); Console.ReadLine(); } } public class Bar { public void SomeMethod() { Logger.Log(this.GetType(), "SomeMethod started..."); } } public class Foo { private static readonly Type myType = typeof(Foo); public void SomeMethod() { Logger.Log(myType, "SomeMethod started..."); } } public class Logger { public static void Log(Type type, string text) { } } } 

在我的机器上,这给了大约的结果。 第一种方法为1500毫秒, 秒为2200毫秒。

(代码和时间更正 – doh!)

使用字段是最好的方法,它避免了由typeof()和GetType()引起的内部字典锁保持唯一的引用。

你有没有考虑过使用nameof运算符?