Jub*_*Jub 90 c# clr performance gettype
我有一个需要快速性能的程序.在其内部循环之一中,我需要测试对象的类型以查看它是否继承自某个接口.
一种方法是使用CLR的内置类型检查功能.最优雅的方法可能是'is'关键字:
if (obj is ISpecialType)
Run Code Online (Sandbox Code Playgroud)
另一种方法是给基类我自己的虚拟GetType()函数,它返回一个预定义的枚举值(在我的情况下,实际上,我只需要一个bool).这种方法会很快,但不那么优雅.
我听说有一个专门针对'is'关键字的IL指令,但这并不意味着它在转换为本机程序集时执行速度很快.任何人都可以分享一些关于'是'与其他方法的表现的见解吗?
更新: 感谢所有明智的答案!似乎在答案中分散了几个有用的观点:安德鲁关于'是'自动执行演员表的观点是必不可少的,但Binary Worrier和Ian收集的表演数据也非常有用.如果编辑其中一个答案以包含所有这些信息,那就太棒了.
And*_*are 109
is如果在检查类型后,使用会损害性能,则会转换为该类型. is实际上将对象强制转换为您正在检查的类型,因此任何后续转换都是多余的.
无论如何你要投,这是一个更好的方法:
ISpecialType t = obj as ISpecialType;
if (t != null)
{
// use t here
}
Run Code Online (Sandbox Code Playgroud)
Bin*_*ier 69
我和伊恩在一起,你可能不想这样做.
但是,正如您所知,两者之间的差异非常小,超过10,000,000次迭代
我个人不会这样解决这个问题,但如果我被迫选择一种方法,它将是内置的IS检查,性能差异不值得考虑编码开销.
我的基础和派生类
class MyBaseClass
{
public enum ClassTypeEnum { A, B }
public ClassTypeEnum ClassType { get; protected set; }
}
class MyClassA : MyBaseClass
{
public MyClassA()
{
ClassType = MyBaseClass.ClassTypeEnum.A;
}
}
class MyClassB : MyBaseClass
{
public MyClassB()
{
ClassType = MyBaseClass.ClassTypeEnum.B;
}
}
Run Code Online (Sandbox Code Playgroud)
JubJub:根据要求提供有关测试的更多信息.
我从控制台应用程序(调试版本)运行了两个测试,每个测试如下所示
static void IsTest()
{
DateTime start = DateTime.Now;
for (int i = 0; i < 10000000; i++)
{
MyBaseClass a;
if (i % 2 == 0)
a = new MyClassA();
else
a = new MyClassB();
bool b = a is MyClassB;
}
DateTime end = DateTime.Now;
Console.WriteLine("Is test {0} miliseconds", (end - start).TotalMilliseconds);
}
Run Code Online (Sandbox Code Playgroud)
在发行版中运行时,我得到了60到70毫秒的差异,就像伊恩一样.
进一步更新- 2012年10月25日
之后一两年离开我发现了一些关于这一点,编译器可以选择忽略bool b = a is MyClassB在其释放,因为B不在任何地方使用.
这段代码...
public static void IsTest()
{
long total = 0;
var a = new MyClassA();
var b = new MyClassB();
var sw = new Stopwatch();
sw.Start();
for (int i = 0; i < 10000000; i++)
{
MyBaseClass baseRef;
if (i % 2 == 0)
baseRef = a;//new MyClassA();
else
baseRef = b;// new MyClassB();
//bool bo = baseRef is MyClassB;
bool bo = baseRef.ClassType == MyBaseClass.ClassTypeEnum.B;
if (bo) total += 1;
}
sw.Stop();
Console.WriteLine("Is test {0} miliseconds {1}", sw.ElapsedMilliseconds, total);
}
Run Code Online (Sandbox Code Playgroud)
...始终显示is检查在大约57毫秒进入,并且枚举比较在29毫秒.
NB 我还是更喜欢is支票,差别太小而无法关心
Jar*_*rsk 22
好的,我正和某人聊聊这件事,并决定对此进行更多测试.据我所知,与测试您自己的成员或函数来存储类型信息相比,性能as和is非常好.
我用过Stopwatch,我刚学到的可能不是最可靠的方法,所以我也试过了UtcNow.后来,我也尝试了处理器时间方法,这似乎UtcNow包括不可预测的创建时间.我也尝试使基类非抽象而没有虚拟,但它似乎没有显着的效果.
我在带有16GB RAM的Quad Q6600上运行它.即使进行了50万次迭代,数字仍然会在+/- 50左右的时间内反弹,因此我不会对微小的差异进行过多的阅读.
有趣的是,x64创建得更快但执行速度比x86慢
x64发布模式:
秒表:
As:561ms
Is:597ms
基本属性:539ms
基本字段:555ms
基本RO字段:552ms
虚拟GetEnumType()测试:556ms
虚拟IsB()测试:588ms
创建时间:10416ms
UtcNow:
As:499ms
Is:532ms
Base property:479ms
Base field:502ms
Base RO field:491ms
Virtual GetEnumType():502ms
Virtual bool IsB():522ms
创建时间:285ms(这个数字似乎与UtcNow不可靠.我也得到109ms和806ms.)
x86发布模式:
秒表:
As:391ms
Is:423ms
基本属性:369ms
基本字段:321ms
基本RO字段:339ms
虚拟GetEnumType()测试:361ms
虚拟IsB()测试:365ms
创建时间:14106ms
UtcNow:
As:348ms
Is:375ms
基本属性:329ms
基本字段:286ms
基本RO字段:309ms
虚拟GetEnumType():321ms
虚拟布尔IsB():332ms
创建时间:544ms(此数字似乎与UtcNow不可靠.)
这是大部分代码:
static readonly int iterations = 50000000;
void IsTest()
{
Process.GetCurrentProcess().ProcessorAffinity = (IntPtr)1;
MyBaseClass[] bases = new MyBaseClass[iterations];
bool[] results1 = new bool[iterations];
Stopwatch createTime = new Stopwatch();
createTime.Start();
DateTime createStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
if (i % 2 == 0) bases[i] = new MyClassA();
else bases[i] = new MyClassB();
}
DateTime createStop = DateTime.UtcNow;
createTime.Stop();
Stopwatch isTimer = new Stopwatch();
isTimer.Start();
DateTime isStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i] is MyClassB;
}
DateTime isStop = DateTime.UtcNow;
isTimer.Stop();
CheckResults(ref results1);
Stopwatch asTimer = new Stopwatch();
asTimer.Start();
DateTime asStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i] as MyClassB != null;
}
DateTime asStop = DateTime.UtcNow;
asTimer.Stop();
CheckResults(ref results1);
Stopwatch baseMemberTime = new Stopwatch();
baseMemberTime.Start();
DateTime baseStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i].ClassType == MyBaseClass.ClassTypeEnum.B;
}
DateTime baseStop = DateTime.UtcNow;
baseMemberTime.Stop();
CheckResults(ref results1);
Stopwatch baseFieldTime = new Stopwatch();
baseFieldTime.Start();
DateTime baseFieldStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i].ClassTypeField == MyBaseClass.ClassTypeEnum.B;
}
DateTime baseFieldStop = DateTime.UtcNow;
baseFieldTime.Stop();
CheckResults(ref results1);
Stopwatch baseROFieldTime = new Stopwatch();
baseROFieldTime.Start();
DateTime baseROFieldStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i].ClassTypeField == MyBaseClass.ClassTypeEnum.B;
}
DateTime baseROFieldStop = DateTime.UtcNow;
baseROFieldTime.Stop();
CheckResults(ref results1);
Stopwatch virtMethTime = new Stopwatch();
virtMethTime.Start();
DateTime virtStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i].GetClassType() == MyBaseClass.ClassTypeEnum.B;
}
DateTime virtStop = DateTime.UtcNow;
virtMethTime.Stop();
CheckResults(ref results1);
Stopwatch virtMethBoolTime = new Stopwatch();
virtMethBoolTime.Start();
DateTime virtBoolStart = DateTime.UtcNow;
for (int i = 0; i < iterations; i++)
{
results1[i] = bases[i].IsB();
}
DateTime virtBoolStop = DateTime.UtcNow;
virtMethBoolTime.Stop();
CheckResults(ref results1);
asdf.Text +=
"Stopwatch: " + Environment.NewLine
+ "As: " + asTimer.ElapsedMilliseconds + "ms" + Environment.NewLine
+"Is: " + isTimer.ElapsedMilliseconds + "ms" + Environment.NewLine
+ "Base property: " + baseMemberTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base field: " + baseFieldTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Base RO field: " + baseROFieldTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Virtual GetEnumType() test: " + virtMethTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Virtual IsB() test: " + virtMethBoolTime.ElapsedMilliseconds + "ms" + Environment.NewLine + "Create Time : " + createTime.ElapsedMilliseconds + "ms" + Environment.NewLine + Environment.NewLine+"UtcNow: " + Environment.NewLine + "As: " + (asStop - asStart).Milliseconds + "ms" + Environment.NewLine + "Is: " + (isStop - isStart).Milliseconds + "ms" + Environment.NewLine + "Base property: " + (baseStop - baseStart).Milliseconds + "ms" + Environment.NewLine + "Base field: " + (baseFieldStop - baseFieldStart).Milliseconds + "ms" + Environment.NewLine + "Base RO field: " + (baseROFieldStop - baseROFieldStart).Milliseconds + "ms" + Environment.NewLine + "Virtual GetEnumType(): " + (virtStop - virtStart).Milliseconds + "ms" + Environment.NewLine + "Virtual bool IsB(): " + (virtBoolStop - virtBoolStart).Milliseconds + "ms" + Environment.NewLine + "Create Time : " + (createStop-createStart).Milliseconds + "ms" + Environment.NewLine;
}
}
abstract class MyBaseClass
{
public enum ClassTypeEnum { A, B }
public ClassTypeEnum ClassType { get; protected set; }
public ClassTypeEnum ClassTypeField;
public readonly ClassTypeEnum ClassTypeReadonlyField;
public abstract ClassTypeEnum GetClassType();
public abstract bool IsB();
protected MyBaseClass(ClassTypeEnum kind)
{
ClassTypeReadonlyField = kind;
}
}
class MyClassA : MyBaseClass
{
public override bool IsB() { return false; }
public override ClassTypeEnum GetClassType() { return ClassTypeEnum.A; }
public MyClassA() : base(MyBaseClass.ClassTypeEnum.A)
{
ClassType = MyBaseClass.ClassTypeEnum.A;
ClassTypeField = MyBaseClass.ClassTypeEnum.A;
}
}
class MyClassB : MyBaseClass
{
public override bool IsB() { return true; }
public override ClassTypeEnum GetClassType() { return ClassTypeEnum.B; }
public MyClassB() : base(MyBaseClass.ClassTypeEnum.B)
{
ClassType = MyBaseClass.ClassTypeEnum.B;
ClassTypeField = MyBaseClass.ClassTypeEnum.B;
}
}
Run Code Online (Sandbox Code Playgroud)
Ian*_*Ian 16
安德鲁是对的.实际上,通过代码分析,Visual Studio将其报告为不必要的强制转换.
一个想法(不知道你在做什么在黑暗中是一个镜头),但我总是被建议避免这样检查,而是有另一个类.因此,不是根据类型进行一些检查和采取不同的操作,而是让课程知道如何处理自己...
例如,Obj可以是ISpecialType或IType;
他们都定义了DoStuff()方法.对于IType,它可以返回或执行自定义的东西,而ISpecialType可以做其他的东西.
然后,这将完全删除任何转换,使代码更清晰,更易于维护,并且类知道如何执行它自己的任务.
Kna*_*bax 13
我对两种类型比较的可能性进行了性能比较
结果是:使用"是"快约10倍!
输出:
类型比较的时间:00:00:00.456
时间比较:00:00:00.042
我的代码:
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Diagnostics;
namespace ConsoleApplication3
{
class MyClass
{
double foo = 1.23;
}
class Program
{
static void Main(string[] args)
{
MyClass myobj = new MyClass();
int n = 10000000;
Stopwatch sw = Stopwatch.StartNew();
for (int i = 0; i < n; i++)
{
bool b = myobj.GetType() == typeof(MyClass);
}
sw.Stop();
Console.WriteLine("Time for Type-Comparison: " + GetElapsedString(sw));
sw = Stopwatch.StartNew();
for (int i = 0; i < n; i++)
{
bool b = myobj is MyClass;
}
sw.Stop();
Console.WriteLine("Time for Is-Comparison: " + GetElapsedString(sw));
}
public static string GetElapsedString(Stopwatch sw)
{
TimeSpan ts = sw.Elapsed;
return String.Format("{0:00}:{1:00}:{2:00}.{3:000}", ts.Hours, ts.Minutes, ts.Seconds, ts.Milliseconds);
}
}
}
Run Code Online (Sandbox Code Playgroud)
点Andrew Hare在执行is检查时失去了性能,然后执行了有效但在C#7.0中我们可以做的是检查女巫模式匹配以避免以后额外的演员:
if (obj is ISpecialType st)
{
//st is in scope here and can be used
}
Run Code Online (Sandbox Code Playgroud)
如果您需要在多种类型之间进行检查,还可以进一步检查C#7.0模式匹配构造现在允许您switch对类型进行检查:
public static double ComputeAreaModernSwitch(object shape)
{
switch (shape)
{
case Square s:
return s.Side * s.Side;
case Circle c:
return c.Radius * c.Radius * Math.PI;
case Rectangle r:
return r.Height * r.Length;
default:
throw new ArgumentException(
message: "shape is not a recognized shape",
paramName: nameof(shape));
}
}
Run Code Online (Sandbox Code Playgroud)
您可以在此处的文档中阅读有关C#中模式匹配的更多信息.
如果有人想知道,我已经在 Unity 引擎 2017.1 中进行了测试,在带有 i5-4200U CPU 的笔记本电脑上使用脚本运行时版本 .NET4.6(Experimantal)。结果:
Average Relative To Local Call
LocalCall 117.33 1.00
is 241.67 2.06
Enum 139.33 1.19
VCall 294.33 2.51
GetType 276.00 2.35
全文:http : //www.ennoble-studios.com/tuts/unity-c-performance-comparison-is-vs-enum-vs-virtual-call.html
| 归档时间: |
|
| 查看次数: |
46475 次 |
| 最近记录: |