C# Object.GetType() 的性能
声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow
原文地址: http://stackoverflow.com/questions/353342/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me):
StackOverFlow
Performance of Object.GetType()
提问by Gaz
We have lots of logging calls in our app. Our logger takes a System.Type parameter so it can show which component created the call. Sometimes, when we can be bothered, we do something like:
我们的应用程序中有很多日志记录调用。我们的记录器采用 System.Type 参数,因此它可以显示哪个组件创建了调用。有时,当我们感到困扰时,我们会这样做:
class Foo
{
private static readonly Type myType = typeof(Foo);
void SomeMethod()
{
Logger.Log(myType, "SomeMethod started...");
}
}
As this requires getting the Type object only once. However we don't have any actual metrics on this. Anyone got any idea how much this saves over calling this.GetType() each time we log?
因为这只需要获取 Type 对象一次。但是,我们对此没有任何实际指标。有没有人知道每次登录时调用 this.GetType() 可以节省多少?
(I realise I could do the metrics myself with no big problem, but hey, what's StackOverflow for?)
(我意识到我可以自己做指标没有大问题,但是嘿,StackOverflow 有什么用?)
采纳答案by Jon Skeet
I strongly suspect that GetType() will take significantly less time than any actual logging. Of course, there's the possibility that your call to Logger.Log won't do any actual IO... I still suspect the difference will be irrelevant though.
我强烈怀疑 GetType() 将比任何实际日志记录花费的时间少得多。当然,您对 Logger.Log 的调用可能不会执行任何实际的 IO ……但我仍然怀疑这种差异是无关紧要的。
EDIT: Benchmark code is at the bottom. Results:
编辑:基准代码在底部。结果:
typeof(Test): 2756ms
TestType (field): 1175ms
test.GetType(): 3734ms
That's calling the method 100 milliontimes - the optimisation gains a couple of seconds or so. I suspect the real logging method will have a lot more work to do, and calling that 100 million times will take a lot longer than 4 seconds in total, even if it doesn't write anything out. (I could be wrong, of course - you'd have to try that yourself.)
这就是调用该方法 1亿次 - 优化获得了几秒钟左右的时间。我怀疑真正的日志记录方法还有很多工作要做,调用 1 亿次总共需要超过 4 秒的时间,即使它没有写出任何内容。(当然,我可能是错的 - 你必须自己尝试。)
In other words, as normal, I'd go with the most readable code rather than micro-optimising.
换句话说,像往常一样,我会使用最易读的代码而不是微优化。
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;
}
}
回答by bruno conde
The GetType()
function is marked with the special attribute [MethodImpl(MethodImplOptions.InternalCall)]
. This means its method body doesn't contain IL but instead is a hook into the internals of the .NET CLR. In this case, it looks at the binary structure of the object's metadata and constructs a System.Type
object around it.
该GetType()
函数标有特殊属性[MethodImpl(MethodImplOptions.InternalCall)]
。这意味着它的方法体不包含 IL,而是一个连接到 .NET CLR 内部的钩子。在这种情况下,它查看对象元数据的二进制结构并System.Type
围绕它构建一个对象。
EDIT:I guess I was wrong about something ...
编辑:我想我错了……
I said that: "because GetType()
requires a new Object to be build" but it seems this is not correct. Somehow, the CLR caches the Type
and always returns the same object so it doesn't need to build a new Type object.
我说:“因为GetType()
需要构建一个新对象”,但这似乎是不正确的。不知何故,CLR 缓存Type
并始终返回相同的对象,因此它不需要构建新的 Type 对象。
I'm based on the following test:
我基于以下测试:
Object o1 = new Object();
Type t1 = o1.GetType();
Type t2 = o1.GetType();
if (object.ReferenceEquals(t1,t2))
Console.WriteLine("same reference");
So, I don't expect much gain in your implementation.
所以,我不希望你的实施有太大的收获。
回答by JaredPar
I doubt you are going to get a satisfying answer from SO on this subject. The reason being that performance, especially scenarios of this type, are highly application specific.
我怀疑您是否会在这个问题上从 SO 那里得到令人满意的答案。原因是性能,尤其是这种类型的场景,是高度特定于应用程序的。
Someone may post back with a quick stopwatch example of which would be faster in terms of raw miliseconds. But frankly that doesn't mean anything for your application. Why? It depends highly on the usage pattern around that particular scenario. For instance ...
有人可能会发回一个快速秒表示例,其中在原始毫秒方面会更快。但坦率地说,这对您的应用程序没有任何意义。为什么?这在很大程度上取决于围绕该特定场景的使用模式。例如 ...
- How many types do you have?
- How big are you methods?
- Do you do this for every method, or only big ones?
- 你有几种类型?
- 你的方法有多大?
- 你是对每种方法都这样做,还是只对大方法这样做?
These are just a few of the questions that will greatly alter the relevance of a straight time benchmark.
这些只是将极大地改变直接时间基准的相关性的几个问题。
回答by Sam Meldrum
The difference is probably negligible as far as application performance is concerned. But the first approach where you cache the type should be faster. Let's go and test.
就应用程序性能而言,这种差异可能可以忽略不计。但是第一种缓存类型的方法应该更快。我们去测试一下。
This code will show you the difference:
此代码将向您展示差异:
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) {
}
}
}
On my machine, this gave results of approx. 1500 milliseconds for the first approach and approx. 2200 milliseconds for the second.
在我的机器上,这给出了大约的结果。第一种方法需要 1500 毫秒,大约需要 1500 毫秒。秒为 2200 毫秒。
(code and timings corrected - doh!)
(代码和时间更正 - 哦!)
回答by Teter28
using field is the best way and it avoid internal dictionary lock causing by typeof() and GetType() to keep unique reference.
使用字段是最好的方法,它避免了由 typeof() 和 GetType() 引起的内部字典锁定以保持唯一引用。
回答by user305874
I get very different results.
For this I created a new console app in another project, and used a class with inheritance.
I created an empty loop to withdraw from the results, for a clean comparison.
I created a const and a static for the cycles (manually switching which to use).
Something very interesting happend.
When using the const, the empty loop become slow, but the buffered var test becomes slightly faster.
A change that should affect none or all tests, only affect 2.
Cycles for each test : 100000000
Using static cycle:
我得到非常不同的结果。
为此,我在另一个项目中创建了一个新的控制台应用程序,并使用了一个具有继承的类。
我创建了一个空循环以从结果中退出,以进行干净的比较。
我为循环创建了一个常量和一个静态(手动切换使用哪个)。
发生了一件非常有趣的事情。
使用 const 时,空循环会变慢,但缓冲的 var 测试会稍微快一点。
应该不影响任何测试或所有测试的更改,只会影响 2.
每个测试的
周期:100000000使用静态周期:
Object.GetType : 1316 TypeOf(Class) : 1589 Type var : 987 Empty Loop : 799 Clean overview: Object.GetType : 517 TypeOf(Class) : 790 Type var : 188
Using const cycle:
使用常量循环:
Object.GetType : 1316 TypeOf(Class) : 1583 Type var : 853 Empty Loop : 1061 Clean overview: Object.GetType : 255 TypeOf(Class) : 522 Type var : -208
I ran these multiple times, and with some small changes, and with 10 times more cycles, to reduce the risk of background processes affecting results. Almost same results as these 2 above.
It does seem that Object.GetType()
is 1.5-2 times as fast as typeof(class)
.
The buffered var seem to be 1.5-2 times as fast as Object.GetType()
.
I the right application, this is not just micro-optimising.
If you sacrifice small things here and there, they will easily slow more than the one big thing you made 30% faster.
Again as JaredPar answered, these kind of tests, is unreliable for telling about your specific application, as we have proven here.
All our tests giving quite different results, and things seemingly unrelated to the code at hand, can affect the performance.
The test:
我多次运行这些,并进行了一些小的更改,循环次数增加了 10 倍,以降低后台进程影响结果的风险。与上面这两个几乎相同的结果。
它似乎Object.GetType()
是 1.5-2 倍的速度typeof(class)
。
缓冲的 var 似乎是 1.5-2 倍Object.GetType()
。
我是正确的应用程序,这不仅仅是微优化。
如果你在这里和那里牺牲小东西,它们很容易比你做的一件大事慢 30%。
正如 JaredPar 再次回答的那样,正如我们在此处证明的那样,这些类型的测试对于说明您的特定应用程序是不可靠的。
我们所有的测试给出了完全不同的结果,而且看起来与手头的代码无关的事情会影响性能。
考试:
.NetCore 2.1
namespace ConsoleApp1
{
class Program
{
public const int Cycles = 100000000;
public static int Cycles2 = 100000000;
public static QSData TestObject = new QSData();
public static Type TestObjectType;
static void Main(string[] args)
{
TestObjectType = TestObject.GetType();
Console.WriteLine("Repeated cycles for each test : " + Cycles.ToString());
var test1 = TestGetType();
Console.WriteLine("Object.GetType : " + test1.ToString());
var test2 = TestTypeOf();
Console.WriteLine("TypeOf(Class) : " + test2.ToString());
var test3 = TestVar();
Console.WriteLine("Type var : " + test3.ToString());
var test4 = TestEmptyLoop();
Console.WriteLine("Empty Loop : " + test4.ToString());
Console.WriteLine("\r\nClean overview:");
Console.WriteLine("Object.GetType : " + (test1 - test4).ToString());
Console.WriteLine("TypeOf(Class) : " + (test2 - test4).ToString());
Console.WriteLine("Type var : " + (test3 - test4).ToString());
Console.WriteLine("\n\rPush a button to exit");
String input = Console.ReadLine();
}
static long TestGetType()
{
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < Cycles; i++)
{
Type aType = TestObject.GetType();
}
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
static long TestTypeOf()
{
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < Cycles; i++)
{
Type aType = typeof(QSData);
}
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
static long TestVar()
{
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < Cycles; i++)
{
Type aType = TestObjectType;
}
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
static long TestEmptyLoop()
{
var stopwatch = new Stopwatch();
stopwatch.Start();
for (int i = 0; i < Cycles; i++)
{
Type aType;
}
stopwatch.Stop();
return stopwatch.ElapsedMilliseconds;
}
}
}