我们的应用程序中有很多日志记录调用。我们的记录器采用System.Type参数,因此它可以显示创建调用的组件。有时,当我们受到困扰时,我们会做类似的事情:
class Foo
{
private static readonly Type myType = typeof(Foo);
void SomeMethod()
{
Logger.Log(myType, "SomeMethod started...");
}
}
因为这需要只获取一次Type对象。但是,我们没有任何实际指标。任何人都知道,每次我们记录时,这节省了多少.GetType()?
(我意识到我可以自己完成指标并没有什么大问题,但是嘿,什么是StackOverflow?)
答案 0 :(得分:82)
我强烈怀疑GetType()将比任何实际日志记录花费更少的时间。当然,你对Logger.Log的调用可能不会做任何实际的IO ......我仍然怀疑差异是无关紧要的。
编辑:基准代码位于底部。结果:
typeof(Test): 2756ms
TestType (field): 1175ms
test.GetType(): 3734ms
这就是调用100 百万次的方法 - 优化会在几秒钟内完成。我怀疑真正的日志记录方法还有很多工作要做,并且调用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;
}
}
答案 1 :(得分:17)
GetType()
函数标有特殊属性[MethodImpl(MethodImplOptions.InternalCall)]
。这意味着它的方法体不包含IL,而是一个挂钩到.NET CLR内部的钩子。在这种情况下,它会查看对象元数据的二进制结构,并在其周围构建一个System.Type
对象。
编辑:我想我错了......
我说过:“因为GetType()
需要构建一个新的对象”,但这似乎不正确。不知何故,CLR缓存Type
并始终返回相同的对象,因此不需要构建新的Type对象。
我基于以下测试:
Object o1 = new Object();
Type t1 = o1.GetType();
Type t2 = o1.GetType();
if (object.ReferenceEquals(t1,t2))
Console.WriteLine("same reference");
所以,我认为你的实施不会有太大的好处。
答案 2 :(得分:9)
我怀疑你会在这个问题上得到满意的答案。原因是性能,特别是这种类型的场景,是高度特定于应用程序的。
有人可能会用一个快速的秒表示例回复,其原始毫秒数会更快。但坦率地说,这对您的申请没有任何意义。为什么?它在很大程度上取决于特定场景的使用模式。例如......
这些只是一些会大大改变直线时间基准相关性的问题。
答案 3 :(得分:2)
就应用程序性能而言,差异可能微不足道。但是缓存类型的第一种方法应该更快。我们去试试吧。
此代码将向您显示差异:
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!)
答案 4 :(得分:2)
您是否考虑过使用@flying-sheep运营商?
答案 5 :(得分:0)
使用字段是最好的方法,它避免了由typeof()和GetType()引起的内部字典锁定以保持唯一引用。
答案 6 :(得分:-1)
我得到非常不同的结果。
为此,我在另一个项目中创建了一个新的控制台应用程序,并使用了具有继承性的类。
我创建了一个空循环以退出结果,以进行比较。
我为循环创建了一个const和一个static(手动切换要使用的循环)。
发生了一件非常有趣的事情。
使用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
使用const循环:
Object.GetType : 1316 TypeOf(Class) : 1583 Type var : 853 Empty Loop : 1061 Clean overview: Object.GetType : 255 TypeOf(Class) : 522 Type var : -208
我多次运行这些命令,并进行了一些小的更改,并运行了10倍以上的循环,以减少背景处理影响结果的风险。几乎与上述2个结果相同。
看来Object.GetType()
的速度是typeof(class)
的1.5-2倍。
缓冲的var似乎是Object.GetType()
的1.5-2倍。
在正确的应用程序中,这不仅是微优化。
如果您在这里和那里牺牲一些小东西,它们将比您使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;
}
}
}