c# - C# 是否具有零成本抽象?

标签 c# optimization compiler-optimization jit

我通常使用具有零成本抽象概念的语言(例如 C++ 和 Rust)进行编程。

目前我在一个使用 C# 语言的项目中工作。所以我想知道我是否可以在不影响性能的情况下安全地创建抽象和更高级别的代码。

这在 C# 中是否可行,或者对于性能关键代码,我应该只编写尽可能低级别的代码吗?

就像我在代码中遇到的一个例子(不要太关注这个例子,我的问题更高层次),我需要一个返回多个值的函数,为此,我的第一个方法是使用一个元组,所以像这样:

public (int, int, float) Function();

或者将这个元组抽象成一个结构:

public struct Abstraction { int value1; int value2; float value3; };

public Abstraction Function();

我期望的是编译器会优化 TupleAbstraction struct 并直接使用原始值。但我发现使用 out 参数编写代码会提高性能:

public void Function(out int value1, out int value2, out float value3);

我猜原因是因为在 out 函数中,没有创建 TupleAbstraction struct

out 函数版本的问题是我真的很讨厌使用参数作为返回值,因为它看起来更像是对语言限制的破解

所以,最后我不确定我是否只是没有使用正确的配置,所以 JIT 可以使用零成本抽象,或者这在 C# 中根本不可能或不能保证。

最佳答案

首先,我认为说语言“具有零成本抽象”是没有意义的。考虑函数的抽象。是零成本吗?一般来说,只有内联才零成本。虽然 C++ 编译器往往非常擅长内联函数,但它们不会内联所有函数,因此严格来说 C++ 中的函数不是零成本抽象。但这种差异在实践中并不重要,这就是为什么您通常可以认为一个函数是零成本的。

现在,现代 C++ 和 Rust 的设计和实现方式尽可能使抽象成本为零。这在 C# 中有什么不同吗?有点儿。 C# 的设计并没有如此关注零成本抽象(例如,在 C# 中调用 lambda 总是涉及有效的虚拟调用;在 C++ 中调用 lambda 则不会,这使得零成本变得更容易)。此外,JIT 编译器通常不能花那么多时间在内联等优化上,因此它们生成的抽象代码比 C++ 编译器差。 (虽然这在未来可能会改变,因为 .Net Core 2.1 introduced a tiered JIT ,这意味着它有更多的时间进行优化。)

另一方面,JIT 编译器经过调整,可以很好地处理实际代码,而不是微基准测试(我假设这是您得出返回 struct 性能较差的结论的原因)。

在我的微基准测试中,使用 struct 确实性能较差,但这是因为 JIT 决定不内联那个版本的 Function,而不是因为成本创建一个 struct 或类似的东西。如果我使用 [MethodImpl(MethodImplOptions.AggressiveInlining)] 解决了这个问题,那么两个版本都实现了相同的性能。

因此,返回一个 struct 可以是 C# 中的零成本抽象。尽管在 C# 中发生这种情况的可能性确实比在 C++ 中要小。

如果你想知道在 out 参数之间切换并返回一个 struct 的实际效果是什么,我建议你写一个更现实的基准测试,而不是微基准测试,看看结果是什么。 (假设我没看错你使用了微基准测试。)

关于c# - C# 是否具有零成本抽象?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/46017522/

相关文章:

compiler-construction - 你最喜欢的抽象语法树优化

c# - 如何使用 lambda 和非 lambda linq 有条件地排序?

c# - 我可以锁定类实例变量的副本吗?

c# - 从任务获取 HttpResponseMessage<HttpResponseMessage>

java - 大输入上的慢速字符串连接

c++ - 编译器是否优化了 `while(true)` 循环的条件检查? (C++)

C# 执行、等待、读取命令的输出

C++ : In how many ways compiler optimizes away our code?

c - 为什么调用函数时会有开销?

c - 神秘的内存管理