C# 动态关键字 —运行时惩罚?

发布于 2024-09-24 13:58:46 字数 193 浏览 3 评论 0原文

在 C# 中将实例定义为动态是否意味着:

  1. 编译器不执行编译时类型检查,但会像对待所有实例一样进行运行时检查。

  2. 编译器不执行编译时类型检查,但会进行运行时检查,这与任何其他非动态实例不同。

  3. 与 2 相同,这会带来性能损失(微不足道?可能很重要?)。

Does defining an instance as dynamic in C# mean:

  1. The compiler does not perform compile-time type checking, but run-time checking takes place like it always does for all instances.

  2. The compiler does not perform compile-time type checking, but run-time checking takes place, unlike with any other non-dynamic instances.

  3. Same as 2, and this comes with performance penalty (trivial? potentially significant?).

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(6

中二柚 2024-10-01 13:58:46

这个问题很令人困惑。

在 C# 中将实例定义为动态意味着:

“定义实例”是指“声明变量”吗?

编译器不会执行编译时类型检查,但会像对所有实例一样执行运行时检查。

“像往常一样进行运行时检查”是什么意思?您想要什么运行时检查?您是否正在考虑由 IL 验证程序执行的检查,或者您是否正在考虑由强制转换引起的运行时类型检查,或者什么?

也许最好简单地解释一下“动态”的作用。

首先,动态编译器的角度来看是一种类型。从CLR的角度来看,不存在动态这样的东西;当代码实际运行时,生成的代码中所有“dynamic”实例都已替换为“object”。

编译器将动态类型的表达式对象类型的表达式完全相同,只是对该表达式的值的所有操作都在运行时<进行分析、编译和执行< /em> 基于实例的运行时类型。目标是执行的代码具有相同的语义,就好像编译器在编译时已知运行时类型一样。

你的问题似乎与性能有关。

回答性能问题的最佳方法是尝试并找出 - 如果您需要硬数字,您应该做的是以两种方式编写代码,使用动态和使用已知类型,然后退出秒表并比较时间。这是唯一知道的方法。

但是,让我们在抽象级别考虑某些操作的性能影响。假设您:

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 的类型分别编译过一次?”

在这种情况下,答案是否定的。然后,DLR 启动一个特殊版本的 C# 编译器,该编译器分析加法表达式,执行重载解析,并生成一个描述将两个整数相加的 lambda 的表达式树。然后,DLR 将该 lambda 编译为动态生成的 IL,然后由 jit 编译器进行 jits。然后,DLR 会缓存该编译状态,以便在您请求时第二次,编译器不必再次完成所有工作。

这需要比一纳秒更长的时间。这可能需要数千纳秒。

这能回答你的问题吗?我不太明白你在这里问什么,但我正在做出最好的猜测。

The question is very confusing.

Does defining an instance as dynamic in C# mean:

By "defining an instance" do you mean "declaring a variable"?

The compiler does not perform compile-time type checking, but run-time checking takes place like it always does for all instances.

What do you mean by "run-time checking like it always does"? What run-time checking did you have in mind? Are you thinking of the checking performed by the IL verifier, or are you thinking of runtime type checks caused by casts, or what?

Perhaps it would be best to simply explain what "dynamic" does.

First off, dynamic is from the perspective of the compiler a type. From the perspective of the CLR, there is no such thing as dynamic; by the time the code actually runs, all instances of "dynamic" have been replaced with "object" in the generated code.

The compiler treats expressions of type dynamic exactly as expressions of type object, except that all operations on the value of that expression are analyzed, compiled and executed at runtime based on the runtime type of the instance. The goal is that the code executed has the same semantics as if the compiler had known the runtime types at compile time.

Your question seems to be about performance.

The best way to answer performance questions is to try it and find out - what you should do if you need hard numbers is to write the code both ways, using dynamic and using known types, and then get out a stopwatch and compare the timings. That's the only way to know.

However, let's consider the performance implications of some operations at an abstract level. Suppose you have:

int x = 123;
int y = 456;
int z = x + y;

Adding two integers takes about a billionth of a second on most hardware these days.

What happens if we make it dynamic?

dynamic x = 123;
dynamic y = 456;
dynamic z = x + y;

Now what does this do at runtime? This boxes 123 and 456 into objects, which allocates memory on the heap and does some copies.

Then it starts up the DLR and asks the DLR "has this code site been compiled once already with the types for x and y being int and int?"

The answer in this case is no. The DLR then starts up a special version of the C# compiler which analyzes the addition expression, performs overload resolution, and spits out an expression tree describing the lambda which adds together two ints. The DLR then compiles that lambda into dynamically generated IL, which the jit compiler then jits. The DLR then caches that compiled state so that the second time you ask, the compiler doesn't have to do all that work over again.

That takes longer than a nanosecond. It takes potentially many thousands of nanoseconds.

Does that answer your questions? I don't really understand what you're asking here but I'm making a best guess.

摇划花蜜的午后 2024-10-01 13:58:46

据我所知,答案是 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);

这绝对不是微不足道的,尽管这并不是说你'我们将能够用肉眼看到性能问题。

相信动态的实现涉及一些非常贴心的幕后缓存,这些缓存会为您完成,因此,如果您再次运行此代码并且x< /code> 是相同的类型,它的运行速度会快很多。

不过,别让我这么做。我对动态没有太多经验;这只是我理解它的工作方式。

As far as I know, the answer is 3.

You can do this:

dynamic x = GetMysteriousObject();
x.DoLaundry();

Since the compiler does no type checking on x, it will compile this code, the assumption being that you know what you're doing.

But this means extra run-time checking has to occur: namely, examining x's type, seeing if it has a DoLaundry method accepting no arguments, and executing it.

In other words the above code is sort of like doing this (I'm not saying it's the same, just drawing a comparison):

object x = GetMysteriousObject();

MethodInfo doLaundry = x.GetType().GetMethod(
    "DoLaundry",
    BindingFlags.Instance | BindingFlags.Public
);

doLaundry.Invoke(x, null);

This is definitely not trivial, though that isn't to say you're going to be able to see a performance issue with your naked eye.

I believe the implementation of dynamic involves some pretty sweet behind-the-scenes caching that gets done for you, so that if you run this code again and x is the same type, it'll run a lot faster.

Don't hold me to that, though. I don't have all that much experience with dynamic; this is merely how I understand it to work.

平定天下 2024-10-01 13:58:46

将变量声明为动态与将其声明为对象类似。 Dynamic 只是获取另一个标志,指示成员解析被推迟到运行时

就性能损失而言 - 这取决于底层对象是什么。这就是动态对象的全部意义,对吧?底层对象可以是 Ruby 或 Python 对象,也可以是 C# 对象。 DLR 将在运行时确定如何解析对此对象的成员调用,并且此解析方法将确定性能损失。

话虽如此 - 肯定会造成性能损失。

这就是为什么我们不只是开始到处使用动态对象。

Declaring a variable as dynamic is similar to declaring it as object. Dynamic simply gets another flag indicating that member resolution gets deferred to run-time.

In terms of the performance penalty - it depends on what the underlying object is. That's the whole point of dynamic objects right? The underlying object can be a Ruby or Python object or it can be a C# object. The DLR will figure out at run-time how to resolve member calls on this object and this resolution method will determine the performance penalty.

Having said that - there definitely is a performance penalty.

That's why we're not simply going to start using dynamic objects all over the place.

雄赳赳气昂昂 2024-10-01 13:58:46

我做了一个简单的测试:对变量进行 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 毫秒。当然,性能损失取决于您在循环中执行的操作,这些代表可能的最简单的操作。

I made a simple test: 100000000 assignments to a variable as a dynamic vs. the same number of direct double assignments, something like

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();

In the first loop (with dynamic) it took some 500ms; in the second one about 200ms. Certainly, the performance loss depends of what you do in your loops, these representing a simplest action possible.

多孤肩上扛 2024-10-01 13:58:46

好吧,该变量被静态类型化为动态类型,但据我所知,除此之外编译器不会进行任何检查。

类型绑定是在运行时完成的,是的,有一个惩罚,但如果动态是唯一的选择那又怎样。如果您可以使用静态类型解决问题,请这样做。话虽这么说,DLR 确实有调用站点缓存,这意味着一些开销会减少,因为在某些情况下可以重用管道。

Well, the variable is statically typed to be of the type dynamic but beyond that the compiler doesn't do any checking as far as I know.

Type binding is done at runtime and yes, there's a penalty, but if dynamic is the only option then so what. If you can solve the problem using static typing do so. That being said, the DLR does call site caching which means some of the overhead is reduced as the plumbing can be reused in some cases.

简单气质女生网名 2024-10-01 13:58:46

据我了解dynamic它只绕过编译时检查。类型解析发生在运行时,就像所有类型一样。所以我不认为有任何与之相关的性能损失。

As far i undesrtand dynamic it only bypasses compile time check. resolution of type happens at runtime as it does for all types. so i dont think there is any performance penalty associated with it.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文