我通常使用的语言是零成本抽象概念,比如C++和Rust。
目前,我正在一个使用C#语言的项目中工作。因此,我想知道我是否能够在不影响性能的情况下安全地创建抽象和高级代码。
这在C#中是可能的,还是对于性能关键的代码,我应该尽可能地做低级别的代码?
正如我在代码中遇到的一个例子(不要过多地关注这个示例,我的问题是更高的级别),我需要一个返回多个值的函数,为此,我的第一种方法是使用tuple,所以如下所示:
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#中是不可能的,或者是没有保证的。
发布于 2018-09-29 21:43:37
首先,我认为说语言“有零成本的抽象”是没有意义的。考虑函数的抽象。是零成本吗?一般来说,只有当它是内联的时候,它才是零成本。虽然C++编译器对内联函数很在行,但它们并不是所有函数都内联,所以C++中的函数严格地说不是零成本的抽象。但这种差异在实践中很少起作用,这就是为什么你通常可以认为一个函数是零成本的。
现在,现代C++和Rust的设计和实现方式使抽象尽可能多地成为零成本。这在C#中是不同的吗?有点。C#的设计并没有如此注重于零成本抽象(例如,在C#中调用lambda总是涉及到什么实际上是虚拟调用;在C++中调用lambda则不需要,这使得它更容易使其为零成本)。而且,JIT编译器通常不能花那么多时间在优化(比如内联)上,因此他们为抽象生成的代码比C++编译器更糟糕。(尽管这在将来可能会发生变化,因为.Net Core2.1引入了一个分层的JIT,这意味着它有更多的时间进行优化。)
另一方面,对JIT编译器进行了调整,使其能够很好地工作于真正的代码,而不是微基准(我认为这就是如何得出返回struct
性能较差的结论)。
在我的微基准测试中,使用struct
确实具有更差的性能,但这是因为JIT决定不使用该版本的Function
,而不是因为创建struct
的成本或诸如此类的事情。如果我通过使用[MethodImpl(MethodImplOptions.AggressiveInlining)]
修复了这个问题,那么两个版本的性能都是相同的。
因此,返回struct
在C#中可以是一个零成本的抽象。不过,在C#中发生这种情况的可能性确实比在C++中小。
如果您想知道在out
参数之间切换并返回一个struct
的实际效果,我建议您编写一个更现实的基准,而不是一个微基准,看看结果是什么。(假设我做对了,你使用了一个微基准。)
https://stackoverflow.com/questions/46017522
复制相似问题