【发布时间】:2011-04-16 14:33:45
【问题描述】:
在 C# 中将实例定义为动态是否意味着:
编译器不执行编译时类型检查,但运行时检查会像对所有实例一样进行。
编译器不执行编译时类型检查,但会进行运行时检查,这与任何其他非动态实例不同。
与 2 相同,但这会带来性能损失(微不足道?可能很重要?)。
【问题讨论】:
-
做一些计时赛。我看到amazing performance。
标签: c# performance dynamic
在 C# 中将实例定义为动态是否意味着:
编译器不执行编译时类型检查,但运行时检查会像对所有实例一样进行。
编译器不执行编译时类型检查,但会进行运行时检查,这与任何其他非动态实例不同。
与 2 相同,但这会带来性能损失(微不足道?可能很重要?)。
【问题讨论】:
标签: c# performance dynamic
我做了一个简单的测试:100000000 次对变量的动态赋值与相同数量的直接双重赋值,比如
int numberOfIterations = 100000000;
Stopwatch sw = new Stopwatch();
sw.Start();
for (int i = 0; i < numberOfIterations; i++)
{
var x = (dynamic)2.87;
}
sw.Stop();
sw.Restart();
for (int i = 0; i < numberOfIterations; i++)
{
double y = 2.87;
}
sw.Stop();
在第一个循环中(使用动态)大约需要 500 毫秒;在第二个大约 200 毫秒。当然,性能损失取决于您在循环中执行的操作,这些表示可能的最简单操作。
【讨论】:
这个问题很混乱。
在 C# 中将实例定义为动态是否意味着:
“定义一个实例”是指“声明一个变量”吗?
编译器不执行编译时类型检查,但运行时检查就像它对所有实例所做的一样。
“像往常一样进行运行时检查”是什么意思?你想到了什么运行时检查?您是在考虑 IL 验证器 执行的检查,还是考虑由强制类型转换引起的运行时类型检查,还是什么?
或许最好简单解释一下“动态”的作用。
首先,dynamic 从编译器的角度来看是一个类型。从CLR的角度来看,没有动态这样的东西;到代码实际运行时,所有“动态”实例都已在生成的代码中替换为“对象”。
编译器将动态类型的表达式完全视为对象类型的表达式,除了对该表达式的值的所有操作都被分析、编译和执行在运行时 基于实例的运行时类型。目标是执行的代码具有相同的语义,就好像编译器在编译时知道运行时类型一样。
您的问题似乎与性能有关。
回答性能问题的最佳方法是尝试并找出答案 - 如果您需要硬数字,您应该做的就是以两种方式编写代码,使用动态类型和使用已知类型,以及然后拿出秒表并比较时间。只有这样才能知道。
但是,让我们在抽象级别考虑一些操作对性能的影响。假设你有:
int x = 123;
int y = 456;
int z = x + y;
如今,在大多数硬件上,添加两个整数大约需要十亿分之一秒。
如果我们让它动态化会发生什么?
dynamic x = 123;
dynamic y = 456;
dynamic z = x + y;
现在这在运行时做了什么?这会将 123 和 456 放入对象中,这些对象在堆上分配内存并进行一些复制。
然后它启动 DLR 并询问 DLR“这个代码站点是否已经编译过一次,x 和 y 的类型是 int 和 int?”
在这种情况下,答案是否定的。然后,DLR 启动一个特殊版本的 C# 编译器,它分析加法表达式,执行重载解析,并输出一个描述将两个整数加在一起的 lambda 的 表达式树。然后 DLR 将该 lambda 编译为动态生成的 IL,然后 jit 编译器会对其进行 jits。然后,DLR 缓存该编译状态,以便在您询问的 秒 次时,编译器不必重新执行所有这些工作。
这需要比一纳秒更长的时间。这可能需要数千纳秒。
这能回答你的问题吗?我真的不明白你在这里问什么,但我正在做一个最好的猜测。
【讨论】:
x,y(假设有+ 运算符)是引用类型怎么办? DLR 的行为是否与您的示例中的行为相同?
Dictionary<Type, dynamic> 与Dictionary<Type, object> 不会提高性能 - 因为两者都需要对字典值进行装箱和拆箱?用于在字典中存储泛型类型
X<object> 类型和X<dynamic> 类型没有任何区别;到运行时,dynamic 已被删除并替换为object。区别完全在于当您使用字典时编译器将生成的代码;如果字典值类型为object,d[foo].bar() 将是一个错误。如果是dynamic,那么这将是合法的,并且对bar 的调用将在运行时进行分析。
int 值存储在对象字典中的拳击惩罚,您将获得相同的惩罚,将 int 值存储在动态词典中.同样,动态只是戴着滑稽帽子的对象。它是“对象,但将对该表达式的进一步分析推迟到运行时”。
据我所知,答案是 3。
你可以这样做:
dynamic x = GetMysteriousObject();
x.DoLaundry();
由于编译器不对x 进行类型检查,它会编译这段代码,假设您知道自己在做什么。
但这意味着必须进行额外的运行时检查:即检查 x 的类型,查看它是否有一个不接受任何参数的 DoLaundry 方法,然后执行它。
换句话说,上面的代码有点就像这样做(我不是说它是一样的,只是画一个比较):
object x = GetMysteriousObject();
MethodInfo doLaundry = x.GetType().GetMethod(
"DoLaundry",
BindingFlags.Instance | BindingFlags.Public
);
doLaundry.Invoke(x, null);
这绝对不是小事,但这并不是说您可以用肉眼看到性能问题。
我相信dynamic 的实现涉及一些非常漂亮的幕后缓存,这些缓存会为您完成,因此如果您再次运行此代码并且x 是同一类型,它会运行得更快。
不过,不要让我这么说。我对dynamic 没有太多经验;这只是我理解它的工作方式。
【讨论】:
BindingFlags 用于什么的快速问题?
DoLaundry 是一个静态方法,上面的代码将找不到它(您需要添加BindingFlags.Static)。
嗯,变量被静态类型化为 dynamic 类型,但据我所知,编译器不会做任何检查。
类型绑定是在运行时完成的,是的,有一个惩罚,但如果dynamic 是唯一的选择,那又如何。如果您可以使用静态类型解决问题,请这样做。话虽如此,DLR 确实调用了站点缓存,这意味着减少了一些开销,因为在某些情况下可以重用管道。
【讨论】:
将变量声明为 dynamic 类似于将其声明为 object。 Dynamic 只是获取另一个标志,表明 成员解析被推迟到运行时。
就性能损失而言 - 这取决于底层对象是什么。这就是动态对象的全部意义,对吧?底层对象可以是 Ruby 或 Python 对象,也可以是 C# 对象。 DLR 将在运行时计算出如何解决对该对象的成员调用,而此解决方法将确定性能损失。
话虽如此 - 肯定会有性能损失。
这就是为什么我们不会简单地开始在所有地方使用动态对象。
【讨论】:
据我所知,dynamic 它只绕过编译时检查。类型的解析发生在运行时,就像它对所有类型一样。所以我不认为有任何与之相关的性能损失。
【讨论】: