冗余的转换是否得到优化?

发布于 2024-10-21 20:01:24 字数 790 浏览 1 评论 0原文

我正在更新一些旧代码,并且发现了几个实例,其中每次需要调用同一对象的属性或方法之一时都会重复转换该对象。示例:

if (recDate != null && recDate > ((System.Windows.Forms.DateTimePicker)ctrl).MinDate)
{
    ((System.Windows.Forms.DateTimePicker)ctrl).CustomFormat = "MM/dd/yyyy";
    ((System.Windows.Forms.DateTimePicker)ctrl).Value = recDate;
}
else
{
    (System.Windows.Forms.DateTimePicker)ctrl).CustomFormat = " ";
}
((System.Windows.Forms.DateTimePicker)ctrl).Format = DateTimePickerFormat.Custom;

我倾向于修复这个怪物,但考虑到我的时间有限,我不想打扰任何不影响功能或性能的事情。

所以我想知道的是,这些冗余的转换是否被编译器优化掉了?我尝试通过在一个简化的示例中使用 ildasm 来自己解决这个问题,但由于不熟悉 IL,我最终变得更加困惑。

更新

到目前为止,共识似乎是:a) 不,转换未优化,但是 b) 虽然结果可能会产生一些小的性能影响,但不太可能显着,并且c) 无论如何我应该考虑修复它们。如果我有时间的话,我决心有一天解决这些问题。与此同时,我不会担心他们。

谢谢大家!

I am updating some old code, and have found several instances where the same object is being cast repeatedly each time one of its properties or methods needs to be called. Example:

if (recDate != null && recDate > ((System.Windows.Forms.DateTimePicker)ctrl).MinDate)
{
    ((System.Windows.Forms.DateTimePicker)ctrl).CustomFormat = "MM/dd/yyyy";
    ((System.Windows.Forms.DateTimePicker)ctrl).Value = recDate;
}
else
{
    (System.Windows.Forms.DateTimePicker)ctrl).CustomFormat = " ";
}
((System.Windows.Forms.DateTimePicker)ctrl).Format = DateTimePickerFormat.Custom;

My inclination is to fix this monstrosity, but given my limited time I don't want to bother with anything that's not affecting functionality or performance.

So what I'm wondering is, are these redundant casts getting optimized away by the compiler? I tried figuring it out myself by using ildasm on a simplified example, but not being familiar with IL I only ended up more confused.

UPDATE

So far, the consensus seems to be that a)no, the casts are not optimized, but b)while there may possibly be some small performance hit as a result, it is not likely significant, and c)I should consider fixing them anyway. I have come down on the side of resolving to fix these someday, if I have time. Meanwhile, I won't worry about them.

Thanks everyone!

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

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

发布评论

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

评论(4

寄与心 2024-10-28 20:01:24

对发布版本中生成的机器代码进行抽查表明,x86 抖动并未优化丢弃。

不过,你必须放眼全局。您正在分配控件的属性。它们有很多副作用。对于 DateTimePicker,分配结果会导致一条消息发送到本机 Windows 控件。这反过来又对这条消息进行了处理。与副作用的成本相比,演员的成本可以忽略不计。重写作业永远不会对速度产生明显的影响,你只会让它快一点点。

继续在一个慵懒的周五下午重写代码吧。但这只是因为它损害了可读性。可读性差的 C# 代码也会产生优化不佳的机器代码,这并不完全是巧合。

A spot check on the generated machine code in the Release build shows that the x86 jitter doesn't optimize the cast away.

You have to look at the big picture here though. You are assigning properties of a control. They have a ton of side-effects. In the case of DateTimePicker, the assignment results in a message being sent to the native Windows control. Which in turn crunches away at the message. The cost of the cast is negligible to the cost of the side effects. Rewriting the assignments is never going to make a noticeable difference in speed, you only made it a fraction of a percent faster.

Go ahead and do rewrite the code on a lazy Friday afternoon. But only because it is a blight to readability. That poorly readable C# code also produces poorly optimized machine code is not entirely a coincidence.

梦萦几度 2024-10-28 20:01:24

无论是在调试还是发布版本中,它都没有针对 IL 进行优化。

简单的 C# 测试:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RedundantCastTest
{
    class Program
    {
        static object get()
        { return "asdf"; }

        static void Main(string[] args)
        {
            object obj = get();
            if ((string)obj == "asdf")
                Console.WriteLine("Equal: {0}, len: {1}", obj, ((string)obj).Length);
        }
    }
}

相应的 IL(注意多个 castclass 指令):

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 3
    .locals init (
        [0] object obj,
        [1] bool CS$4$0000)
    L_0000: nop 
    L_0001: call object RedundantCastTest.Program::get()
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: castclass string
    L_000d: ldstr "asdf"
    L_0012: call bool [mscorlib]System.String::op_Equality(string, string)
    L_0017: ldc.i4.0 
    L_0018: ceq 
    L_001a: stloc.1 
    L_001b: ldloc.1 
    L_001c: brtrue.s L_003a
    L_001e: ldstr "Equal: {0}, len: {1}"
    L_0023: ldloc.0 
    L_0024: ldloc.0 
    L_0025: castclass string
    L_002a: callvirt instance int32 [mscorlib]System.String::get_Length()
    L_002f: box int32
    L_0034: call void [mscorlib]System.Console::WriteLine(string, object, object)
    L_0039: nop 
    L_003a: ret 
}

它也没有从发布版本中的 IL 进行优化:

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 3
    .locals init (
        [0] object obj)
    L_0000: call object RedundantCastTest.Program::get()
    L_0005: stloc.0 
    L_0006: ldloc.0 
    L_0007: castclass string
    L_000c: ldstr "asdf"
    L_0011: call bool [mscorlib]System.String::op_Equality(string, string)
    L_0016: brfalse.s L_0033
    L_0018: ldstr "Equal: {0}, len: {1}"
    L_001d: ldloc.0 
    L_001e: ldloc.0 
    L_001f: castclass string
    L_0024: callvirt instance int32 [mscorlib]System.String::get_Length()
    L_0029: box int32
    L_002e: call void [mscorlib]System.Console::WriteLine(string, object, object)
    L_0033: ret 
}

这两种情况都意味着生成本机代码时不会优化转换 -你需要看看那里的实际机器组装。即通过运行 ngen 并反汇编。如果它没有被优化掉,我会感到非常惊讶。

无论如何,我会引用实用程序员和破窗定理:当你看到窗户坏了,就把它修好。

It is not optimized away from IL in either debug or release builds.

simple C# test:

using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;

namespace RedundantCastTest
{
    class Program
    {
        static object get()
        { return "asdf"; }

        static void Main(string[] args)
        {
            object obj = get();
            if ((string)obj == "asdf")
                Console.WriteLine("Equal: {0}, len: {1}", obj, ((string)obj).Length);
        }
    }
}

Corresponding IL (note the multiple castclass instructions):

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 3
    .locals init (
        [0] object obj,
        [1] bool CS$4$0000)
    L_0000: nop 
    L_0001: call object RedundantCastTest.Program::get()
    L_0006: stloc.0 
    L_0007: ldloc.0 
    L_0008: castclass string
    L_000d: ldstr "asdf"
    L_0012: call bool [mscorlib]System.String::op_Equality(string, string)
    L_0017: ldc.i4.0 
    L_0018: ceq 
    L_001a: stloc.1 
    L_001b: ldloc.1 
    L_001c: brtrue.s L_003a
    L_001e: ldstr "Equal: {0}, len: {1}"
    L_0023: ldloc.0 
    L_0024: ldloc.0 
    L_0025: castclass string
    L_002a: callvirt instance int32 [mscorlib]System.String::get_Length()
    L_002f: box int32
    L_0034: call void [mscorlib]System.Console::WriteLine(string, object, object)
    L_0039: nop 
    L_003a: ret 
}

Neither is it optimized from the IL in the release build:

.method private hidebysig static void Main(string[] args) cil managed
{
    .entrypoint
    .maxstack 3
    .locals init (
        [0] object obj)
    L_0000: call object RedundantCastTest.Program::get()
    L_0005: stloc.0 
    L_0006: ldloc.0 
    L_0007: castclass string
    L_000c: ldstr "asdf"
    L_0011: call bool [mscorlib]System.String::op_Equality(string, string)
    L_0016: brfalse.s L_0033
    L_0018: ldstr "Equal: {0}, len: {1}"
    L_001d: ldloc.0 
    L_001e: ldloc.0 
    L_001f: castclass string
    L_0024: callvirt instance int32 [mscorlib]System.String::get_Length()
    L_0029: box int32
    L_002e: call void [mscorlib]System.Console::WriteLine(string, object, object)
    L_0033: ret 
}

Neither case means that the casts don't get optimized when native code is generated - you'd need to look at the actual machine assembly there. i.e. by running ngen and disassembling. I'd be greatly surprised if it wasn't optimized away.

Regardless, I'll cite The Pragmatic Programmer and the broken window theorem: When you see a broken window, fix it.

记忆里有你的影子 2024-10-28 20:01:24

不; FxCop 将此标记为性能警告。请参阅此处的信息:http://msdn.microsoft.com/en-us/library /ms182271.aspx

如果您想找到需要修复的问题,我建议您在代码上运行它。

No; FxCop flags this as a performance warning. See info here: http://msdn.microsoft.com/en-us/library/ms182271.aspx

I'd recommend running that over your code if you want to find things to fix.

热鲨 2024-10-28 20:01:24

我从未听说过或见过 CLR 上的冗余转换优化。让我们尝试一个人为的示例

object number = 5;
int iterations = 10000000;
int[] storage = new int[iterations];

var sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
    storage[i] = ((int)number) + 1;
    storage[i] = ((int)number) + 2;
    storage[i] = ((int)number) + 3;
}
Console.WriteLine(sw.ElapsedTicks);

storage = new int[iterations];

sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
    var j = (int)number;
    storage[i] = j + 1;
    storage[i] = j + 2;
    storage[i] = j + 3;
}
Console.WriteLine(sw.ElapsedTicks);
Console.ReadLine();

,在我的机器上,在发布版本下运行,我始终获得大约 350k 滴答声用于冗余冗余和 280k 滴答声用于自我优化。所以不,看起来 CLR 没有对此进行优化。

I have never heard of or seen redundant cast optimizations on the CLR. Lets try a contrived example

object number = 5;
int iterations = 10000000;
int[] storage = new int[iterations];

var sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
    storage[i] = ((int)number) + 1;
    storage[i] = ((int)number) + 2;
    storage[i] = ((int)number) + 3;
}
Console.WriteLine(sw.ElapsedTicks);

storage = new int[iterations];

sw = Stopwatch.StartNew();
for (int i = 0; i < iterations; i++) {
    var j = (int)number;
    storage[i] = j + 1;
    storage[i] = j + 2;
    storage[i] = j + 3;
}
Console.WriteLine(sw.ElapsedTicks);
Console.ReadLine();

On my machine, running under release, I am consistantly getting about 350k ticks for redundant redundancy and 280k ticks for self optimzation. So no, it looks like the CLR does not optimize for this.

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