微信公众号搜"智元新知"关注
微信扫一扫可直接关注哦!

c#编译器由于缓存代理而奇怪的行为

假设我有以下程序:
static void SomeMethod(Func<int,int> otherMethod)
{
    otherMethod(1);
}

static int OtherMethod(int x)
{
    return x;
}

static void Main(string[] args)
{
    SomeMethod(OtherMethod);
    SomeMethod(x => OtherMethod(x));
    SomeMethod(x => OtherMethod(x));
}

我不明白编译的il代码(它使用太多的代码).这是简化版本:

class C
{
    public static C c;
    public static Func<int,int> foo;
    public static Func<int,int> foo1;
    static C()
    {
        c = new C();
    }
    C(){}
    public int b(int x)
    {
        return OtherMethod(x);
    }
    public int b1(int x)
    {
        return OtherMethod(x);
    }
}

static void Main()
{
    SomeMethod(new Func<int,int>(OtherMethod));
    if (C.foo != null)
        SomeMethod(C.foo)
    else
    {
        C.foo = new Func<int,int>(c,C.b)
        SomeMethod(C.foo);
    }
    if (C.foo1 != null)
        SomeMethod(C.foo1)
    else
    {
        C.foo1 = new Func<int,C.b1)
        SomeMethod(C.foo1);
    }
}

为什么编译器不会创建静态等效的方法b / b1? Equal表示它们具有相同的代码

解决方法

你的问题是:为什么编译器没有意识到这两行
SomeMethod(x => OtherMethod(x));
SomeMethod(x => OtherMethod(x));

是一样的,写为

if ( delegate is not created ) 
  create the delegate and stash it away
SomeMethod( the delegate );
SomeMethod( the delegate );

?那么让我以几种方式回答这个问题.

首先,编译器允许进行优化?是.该规范呼吁,C#编译器允许使两个完全相同的东西的lambdas成为一个委托.实际上,你可以看到它已经完成了这个优化:它创建了每个代理一次,并将其保存起来,以便在再次调用代码时不必再创建它.请注意,在代码只被调用一次的情况下,这是浪费内存.

其次,编译器是否需要进行缓存优化?不,规范要求编译器只允许进行优化,但不是必需的.

编译器是否需要进行优化?显然不是,因为没有.允许,也可能是将来版本的编译器.编译器是开源的;如果您关心此优化,请写入并提交拉取请求.

第三,是否可以进行优化?是.编译器可以使用同一方法中出现的所有成对的lambdas,将它们编译为内部树形格式,并进行树比较以查看它们是否具有相同的内容,然后为两者生成相同的静态后台字段.

所以现在我们有一个情况:编译器被允许进行特定的优化,而不是.你问“为什么不”?这是一个容易解答的问题:所有的优化都没有实现,直到有人花费了相当多的时间和精力:

>仔细设计优化:在什么条件下优化触发而不触发?优化应该如何通用?你建议他们检测到类似的lambda体,但为什么要停在那里?你有两个相同的代码语句,所以为什么不为这些语句生成一次而不是两次的代码?如果你有一连串的陈述呢?这里有大量的设计工作.
>特别是,设计的一个重要方面是:用户可以合理地进行“手动”优化,同时保持代码的可读性.在这种情况下,是的,他们可以很容易.只需将重复的lambda分配给变量,然后使用该变量.自动执行的用户可以轻松完成自己的优化不是一个非常有趣或引人注目的优化.
你的例子是微不足道的现实世界的代码不是.您提出的设计与相同的嵌套羊羔有什么关系?等等.
>您的优化是否导致调试器中代码的行为“看起来很奇怪”?您可能已经注意到,当调试已经打开优化的代码进行调试时,调试器似乎有奇怪的作用;这是因为生成代码和原始代码之间不再有明确的映射.你的优化是否更糟?用户可以接受吗?调试器是否需要注意优化?如果是这样,您将不得不更改调试器.在这种情况下,可能不是,但这些是你必须问和回答的问题.
>获得专家审查的设计;这占用了他们的时间,并且可能会导致设计的变化
>估计优化的优缺点 – 优化通常有隐藏的成本,像我之前提到的内存泄漏.特别地,优化通常排除可能更好的其他优化.
>估算这个优化的全球储蓄总额.优化实际上是否影响现实世界的代码?它是否改变了该代码的正确性?是否有任何生产代码,世界上任何地方,会打破这种优化,并导致X公司的首席技术官调用微软CTO要求修复?如果答案是肯定的,那么也许你可能不想做这个优化. C#不是玩具.数以百万计的人每天依靠正确的操作.
>在编译时进行优化的估计负担是多少?编译不必在击键之间发生,但它必须相当快.在编译器中的通用代码路径中引入超线性算法的任何内容将是不可接受的.您可以实现优化,使其在代码大小上是线性的吗?请注意,我之前草绘的算法 – 比较所有对 – 在代码大小上是超线性的. (练习:在所有成对的羊羔上进行树比较,最糟糕的情况是渐近表现?)
>实际实现优化.我鼓励你这样做
>测试优化;它实际上是否产生更好的代码?关于什么指标?不改变任何度量的优化不是一个优化.
>注册以修复优化中的错误永远.

你想要的优化根本不符合条件.没有人写这样的代码.如果他们这样做,他们关心它复制了一个对象,他们可以很容易地修复它.因此,优化优化了不存在的代码,以获得在程序将分配的数百万和数百万个对象中构建单个对象的“赢”.不值得.

但是再一次,如果你认为是这样,请继续执行并提交一个请求.请务必提交上述调查结果,因为那些是真实的工作.实施通常是在功能上花费的总计努力的最小部分;这就是为什么C#是一个成功的语言.

原文地址:https://www.jb51.cc/csharp/96265.html

版权声明:本文内容由互联网用户自发贡献,该文观点与技术仅代表作者本人。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如发现本站有涉嫌侵权/违法违规的内容, 请发送邮件至 dio@foxmail.com 举报,一经查实,本站将立刻删除。

相关推荐