我通常使用具有零成本抽象概念的语言进行编程,例如 C++ 和 Rust。
目前我正在一个使用 C# 语言的项目中工作。所以我想知道我是否可以在不影响性能的情况下安全地创建抽象和更高级别的代码。
这在 C# 或性能关键代码中是否可行,我应该尽可能地编写低级代码?
就像我在代码中遇到的一个例子(不要过多关注这个例子,我的问题更高级),我需要一个可以返回多个值的函数,为此,我的第一种方法是使用元组,所以像这样:
public (int, int, float) Function();
或将此元组抽象为一个结构:
public struct Abstraction { int value1; int value2; float value3; };
public Abstraction Function();
我期望的是编译器会优化
Tuple
或 Abstraction struct
并直接使用原始值。但我发现使用 out
参数编写代码会提高性能:public void Function(out int value1, out int value2, out float value3);
我猜原因是因为在
out
函数中,没有 Tuple
或 Abstraction struct
创建。out
函数版本的问题是我真的很讨厌使用参数作为返回值,因为它看起来更像是对语言限制的一种破解。所以,最后我不确定我是否只是没有使用正确的配置,所以 JIT 可以使用零成本抽象,或者这在 C# 中根本不可能或不能保证。
最佳答案
首先,我认为说语言“具有零成本抽象”是没有意义的。考虑函数的抽象。是零成本吗?一般而言,只有内联时才为零成本。虽然 C++ 编译器往往非常擅长内联函数,但它们不会内联所有函数,因此严格来说 C++ 中的函数不是零成本的抽象。但是这种差异在实践中很少见,这就是为什么您通常可以将函数视为零成本的原因。
现在,现代 C++ 和 Rust 的设计和实现方式使它们尽可能地使抽象零成本。这在 C# 中有所不同吗?有点儿。 C# 的设计并没有如此关注零成本抽象(例如,在 C# 中调用 lambda 总是涉及有效的虚拟调用;在 C++ 中调用 lambda 则不然,这使得零成本更容易)。此外,JIT 编译器通常无法在诸如内联之类的优化上花费太多时间,因此它们生成的抽象代码比 C++ 编译器更差。 (虽然这在 future 可能会改变,因为 .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/