Inlining functions是一个编译器优化,它将函数调用站点替换为被调用者的主体。 regular C# functions支持此优化。
在C#5.0中支持Async模式的 async-await函数有一个特殊的声明,它涉及async
修饰符并使用Task<>
包装返回值。
异步函数也可以内联吗?
示例:
假设我有这些功能:
private async Task<int> CalleeAsync() {
return await SomeOperationAsync();
}
private async Task<int> CallerAsync() {
return await CalleeAsync();
}
他们可以优化为:
private async Task<int> CallerAsync() {
return await SomeOperationAsync();
}
额外信用:
如果支持,谁可以决定内联的内容?编译器? JIT?我吗?
如果它不受支持,我是否应该担心这一点并避免过多的包装器,为了便于阅读,我有时会添加它?
答案 0 :(得分:4)
考虑到由async/await
引起的转换的复杂性,我不认为代码是可内联的:async/await
导致您的方法在隐藏类中转换,并且您的代码变为状态机,代码的各个部分变成了不同的状态。
举一个例子,一个简单的方法CalleeAsync()
被转换为像:
[CompilerGenerated]
private sealed class <CalleeAsync>d__2
{
private int <>1__state;
private bool $__disposing;
public System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> $builder;
public Action <>t__MoveNextDelegate;
public Program <>4__this;
private TaskAwaiter<int> <a1>t__$await4;
public void MoveNext()
{
int result2;
System.Runtime.CompilerServices.AsyncTaskMethodBuilder<int> asyncTaskMethodBuilder;
try
{
int num = this.<>1__state;
if (num != 1)
{
if (this.<>1__state == -1)
{
return;
}
this.<a1>t__$await4 = this.<>4__this.SomeOperationAsync().GetAwaiter<int>();
if (!this.<a1>t__$await4.IsCompleted)
{
this.<>1__state = 1;
this.<a1>t__$await4.OnCompleted(this.<>t__MoveNextDelegate);
return;
}
}
else
{
this.<>1__state = 0;
}
int result = this.<a1>t__$await4.GetResult();
this.<a1>t__$await4 = default(TaskAwaiter<int>);
result2 = result;
}
catch (Exception exception)
{
this.<>1__state = -1;
asyncTaskMethodBuilder = this.$builder;
asyncTaskMethodBuilder.SetException(exception);
return;
}
this.<>1__state = -1;
asyncTaskMethodBuilder = this.$builder;
asyncTaskMethodBuilder.SetResult(result2);
}
[DebuggerHidden]
public void Dispose()
{
this.$__disposing = true;
this.MoveNext();
this.<>1__state = -1;
}
[DebuggerHidden]
public <CalleeAsync>d__2(int <>1__state)
{
this.<>1__state = <>1__state;
}
}
(请注意,在这台机器上我仍然使用带有Async CTP的Visual Studio 2010,使用.NET 4.5,生成的代码可能会有所不同。)
你觉得这样的东西是可以内联的吗?
答案 1 :(得分:1)
C#编译器将为“简单”await
创建相当多的IL代码,xanatos已经展示了IL代码在转换回C#时的样子。由于JIT编译器(即内联的编译器)具有关于内联的严格规则,因此我怀疑它将内联所有代码。
Here是JIT内联的一些规则(对于当前的JIT来说可能不是这样,但这是一个好的开始)。在这里,我已经看到违反了两个规则:我们有超过32个字节的IL代码和一个异常处理块。