异步函数可以进行内联优化吗?

6

内联函数是一种编译器优化,它用被调用者的主体替换了函数调用站点。此优化支持常规C#函数

支持C# 5.0中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?还是我可以决定哪些内容进行内联?

如果不支持,我应该担心这个问题吗?是否应该避免为了可读性而添加过多的包装器?


私有任务<int> CalleeAsync() { 返回 SomeOperationAsync(); } - I4V
当然。请记住,这只是一个简单的概念示例。实际情况可能会稍微复杂一些 :) - talkol
2个回答

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生成的代码可能会不同。

你觉得像这样的东西可以内联吗?


2
如果我必须说出我的想法,我猜测大多数编译器优化都是不可能的 :) 如果编译器在编译成这个状态机之前尝试内联会怎样呢?我可以看到这种情况发生在预处理器层面上(如果存在的话),尽管我不太清楚我在说什么。 - talkol
@talkol C#编译器并不特别花哨,很少修改太多的代码。我认为它不会进行内联(我认为是JIT在内联代码)。 - xanatos

1

C#编译器会为“简单”的await创建相当多的IL代码,xanatos已经展示了将该IL代码翻译回C#后的样子。由于JIT编译器(即执行内联的编译器)有严格的内联规则,我怀疑它不会内联所有的代码。

这里是一些JIT内联规则(这些规则可能不适用于当前的JIT,但是是一个很好的起点)。在这里,我已经看到两个违反的规则:我们有超过32字节的IL代码和一个异常处理块。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接