当前位置: 首页 > 知识库问答 >
问题:

C#编译器会优化此代码吗?

曹德明
2023-03-14

我经常遇到这种情况。乍一看,我认为,“这是糟糕的编码;我正在执行一个方法两次,必然会得到相同的结果。”但想到这里,我不得不怀疑编译器是否像我一样聪明,并能得出相同的结论。

var newList = oldList.Select(x => new Thing {
    FullName = String.Format("{0} {1}", x.FirstName, x.LastName),
    OtherThingId = x.GetOtherThing() != null : x.GetOtherThing().Id : 0 // Might call x.GetOtherThing() twice?
});

编译器的行为是否取决于 GetOtherThing 方法的内容?假设它看起来像这样(有点类似于我现在的真实代码):

public OtherThing GetOtherThing() {
    if (this.Category == null) return null;
    return this.Category.OtherThings.FirstOrDefault(t => t.Text == this.Text);
}

除非对这些对象来自的任何存储进行处理不当的异步更改,否则如果连续运行两次,肯定会返回相同的内容。但是,如果它看起来像这样(为了论证而无意义的例子):

public OtherThing GetOtherThing() {
    return new OtherThing {
        Id = new Random().Next(100)
    };
}

连续运行两次将导致创建两个不同的对象,很可能具有不同的 Id。在这些情况下,编译器会怎么做?它是否像我在第一个列表中展示的那样效率低下?

< s >我运行了与第一个代码清单非常相似的代码,并在< code>GetOtherThing实例方法中放置了一个断点。断点被命中一次。所以,看起来结果确实被缓存了。在第二种情况下会发生什么,方法可能每次都返回不同的东西?编译器会不会优化不正确?对我发现的结果有什么警告吗?

编辑

这一结论是无效的。参见@usr的回答下的评论。

共有2个答案

罗业
2023-03-14

如果您无法区分差异,编译器只能应用优化。在你的“随机”例子中,你可以清楚地看出其中的区别。它不能以这种方式“优化”。这将违反C#规范。事实上,该规范并没有大量讨论优化。它只是说明了你应该观察这个程序做什么。在这种情况下,它指定应绘制两个随机数。

在第一个示例中,可以应用此优化。它在实践中永远不会发生。以下是一些使它变得困难的事情:

  • 查询操作的数据可以通过您的虚拟函数调用或您的 lambdat =

所有这些都必须在非内联方法和程序集之间工作。

C#编译器无法做到这一点,因为它无法查看mscorlib。修补程序版本可能会随时更改mscorlib。

JIT是一个糟糕的JIT(唉),它针对编译速度进行了优化(唉)。它没有做到这一点。如果您怀疑当前的JIT是否会进行一些高级优化,可以肯定它不会。

呼延宪
2023-03-14

这里需要考虑两个编译器:将 C# 转换为 IL 的 C# 编译器,以及将 IL 转换为机器代码的 IL 编译器 - 称为抖动,因为它及时发生。

微软C#编译器当然不会进行这样的优化。方法调用是作为方法调用生成的,仅此而已。

如果无法检测到抖动,则允许抖动执行您描述的优化。例如,假设您有:

y = M() != 0 ? M() : N()

static int M() { return 1; }

允许抖动将此程序转换为:

y = 1 != 0 ? 1 : N()

或者就此而言

y = 1;

抖动是否这样做是实现细节;如果你关心的话,你必须询问抖动方面的专家它是否真的执行了这种优化。

同样,如果你

static int m;
static int M() { return m; }

然后抖动可以将其优化为

y = m != 0 ? m : N()

或者甚至变成:

int q = m;
y = q != 0 ? q : N();

因为允许抖动将连续两个没有干预写入的字段读取转换为单个字段读取,前提是该字段不易失。同样,它是否这样做是一个实现细节;询问抖动开发人员。

然而,在后一个示例中,抖动无法消除第二个调用,因为它有副作用。

我运行了一些与第一个代码清单非常相似的东西,并在 GetOtherThing 实例方法中放置了一个断点。断点命中一次。

这是极不可能的。调试时,几乎所有优化都会关闭,这恰恰是为了更容易调试。正如福尔摩斯从未说过的那样,当你排除不可能的事情时,最可能的解释是原始海报是错误的。

 类似资料:
  • 问题内容: 假设我在C代码中有类似的内容。我知道您可以使用a 代替,以使编译器不对其进行编译,但是出于好奇,我问编译器是否也可以解决此问题。 我认为这对于Java编译器来说更为重要,因为它不支持。 问题答案: 在Java中,if内的代码甚至都不是已编译代码的一部分。它必须编译,但不会写入已编译的字节码。它实际上取决于编译器,但我不知道没有对它进行优化的编译器。规则在JLS中定义: 优化的编译器可能

  • 我编译了以下C代码: 使用命令 .下面是输出中的 Bar 函数: 我有几个关于这个汇编代码的问题: > 如果函数体中既没有使用也没有使用rsp,那么"",""和""的目的是什么? 为什么和自动包含C函数的参数(分别为和)而不从堆栈中读取它们? 我尝试将Foo的大小增加到88字节(11s),指令变成了。将我的结构设计为“圆形”大小以避免乘法指令(以优化数组访问)是否有意义?指令被替换为:

  • 本文向大家介绍C/C++ 编译器优化介绍,包括了C/C++ 编译器优化介绍的使用技巧和注意事项,需要的朋友参考一下 0. gcc -o gcc -o 的优化仍然是机械的,想当然的。只有做到深入理解计算机系统,加深对编程语言的理解,才能写出最优化的代码。 Linux下gcc 优化级别的介绍  · gcc -o0 ⇒ 不提供任何优化;  · gcc -o1 ⇒ 最基本的优化,主要对代码的分支、表达式、

  • 问题内容: 我目前正在翻译中编写一个针对Java字节码的玩具编译器。 我想知道是否可以在编写.class文件之前在发出的字节码中进行各种简单的窥孔优化的目录,也许是摘要。我实际上知道一些具有此功能的库,但是我想自己实现。 问题答案: 您知道Proguard吗?http://proguard.sourceforge.net/ 这是一个很棒的字节码优化器,它实现了很多优化。请参阅常见问题解答以获取列表

  • 问题内容: 输出始终为。 但是,绝对可以使循环多次遍历1s 。 我认为,在闭包是在FUNC。 请参见下面的代码。 在多行“ +1”之后,输出正好是预期的很大数目。 问题答案: 记忆模型 2014年5月31日版本 介绍 Go内存模型指定了一种条件,在这种条件下,可以保证在一个goroutine中读取变量可以观察到在不同goroutine中写入同一变量所产生的值。 忠告 修改由多个goroutine同

  • 在 C 或 C 中,如果编译器遇到一个 循环,其中计数器从 计数到 n, n 是一个变量(不是函数调用,也不是常量),编译器是否会通过检查变量 (绑定变量)是否会在循环期间更改(访问写入, 例如: 可以是循环前计算的字符串的长度),通过优化这里,我的意思是将其值复制到寄存器以避免内存访问? 下面是一个示例: 编译器会注意到这一点并对其进行优化吗?