为什么Roslyn中的异步状态机类(而不是结构体)?
让我们考虑一下这个非常简单的异步方法:
static async Task myMethodAsync()
{
await Task.Delay(500);
}
当我编译VS2013(Roslyn编译器之前)时,生成的状态机是一个结构体。
private struct <myMethodAsync>d__0 : IAsyncStateMachine
{
...
void IAsyncStateMachine.MoveNext()
{
...
}
}
当我用VS2015(Roslyn)编译它时,生成的代码是这样的:
private sealed class <myMethodAsync>d__1 : IAsyncStateMachine
{
...
void IAsyncStateMachine.MoveNext()
{
...
}
}
正如你可以看到Roslyn生成一个类(而不是一个结构)。 如果我没有记错,旧编译器(CTP2012,我猜)中的异步/等待支持的第一个实现也生成了类,然后从性能原因改为struct。 (在某些情况下,你可以完全避免装箱和堆分配...)(看到这个)
有谁知道为什么在Roslyn再次改变它? (我对此没有任何问题,我知道这种改变是透明的,不会改变任何代码的行为,我只是好奇)
编辑:
来自@Damien_The_Unbeliever(以及源代码:))的答案imho解释了一切。 Roslyn描述的行为只适用于调试版本(因为评论中提到的CLR限制,这是需要的)。 在发布它也生成一个结构(具有所有的好处..)。 所以这似乎是一个非常聪明的解决方案,可以同时支持编辑和继续以及更好的生产性能。 有趣的东西,感谢所有参与者!
我没有预先知道这件事,但是由于Roslyn现在是开源的,我们可以通过代码寻找解释。
在这里,在AsyncRewriter的第60行,我们发现:
// The CLR doesn't support adding fields to structs, so in order to enable EnC in an async method we need to generate a class.
var typeKind = compilationState.Compilation.Options.EnableEditAndContinue ? TypeKind.Class : TypeKind.Struct;
因此,尽管使用struct
可以struct
,但允许Edit和Continue在async
方法中工作的巨大胜利显然是更好的选择。
很难为这样的事情给出明确的答案(除非编译团队的某个人下降:)),但有几点你可以考虑:
绩效结构的“奖金”总是一种折衷。 基本上,你会得到以下结果:
这是什么意思等待案件? 呃,其实......什么都没有。 状态机在堆栈上只有很短的时间 - 记住, await
有效地return
,所以方法堆栈就会死掉; 状态机必须保存在某个地方,而这个“某个地方”肯定在堆上。 堆栈生存期不适合异步代码:)
除此之外,状态机违反了定义结构的一些好的指导原则:
struct
s最多应为16个字节 - 状态机包含两个指针,它们自己在64位上整齐地填充16个字节的限制。 除此之外,还有国家本身,所以它超过了“极限”。 这不是什么大问题,因为它很可能只是被引用传递过来,但是请注意它如何不适合结构体的用例 - 一个基本上是引用类型的结构体。 struct
s应该是不可变的 - 好吧,这可能不需要太多评论。 这是一台状态机。 同样,这不是什么大问题,因为这个结构是自动生成的代码和私有的,但是... struct
s在逻辑上应该表示一个值。 这里绝对不是这种情况,但是这种情况本来就是从首先有一个可变状态开始的。 当然,所有这些都是在没有关闭的情况下。 当你有遍历await
的locals(或字段)时,状态会进一步膨胀,限制使用struct的有用性。
考虑到所有这些,类方法绝对更清晰,我不希望使用struct
代替任何显着的性能增加。 所有涉及的对象都具有相似的生命周期,所以提高内存性能的唯一方法就是使它们全部struct
(例如,存储在某个缓冲区中) - 当然这在一般情况下是不可能的。 大多数情况下,你首先会使用await
(也就是说,一些异步I / O工作)已经涉及其他类 - 例如数据缓冲区,字符串......这是不太可能的,你会await
一些简单的返回42
没有做任何堆分配。
最后,我认为你真正看到性能差异的唯一地方就是基准测试。 对基准进行优化是一个愚蠢的想法,至少可以说...
链接地址: http://www.djcxy.com/p/80603.html上一篇: Why are async state machines classes (and not structs) in Roslyn?
下一篇: Performance differences between debug and release builds