Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can async functions be inlined?

Inlining functions is a compiler optimization that replaces the function call site with the body of the callee. This optimization is supported for regular C# functions.

Async functions that support the async-await pattern in C# 5.0 have a special declaration that involves the async modifier and wrapping return values with Task<>.

Can async functions be inlined as well?

Example:

Suppose I have these functions:

private async Task<int> CalleeAsync() {
    return await SomeOperationAsync();
}

private async Task<int> CallerAsync() {
    return await CalleeAsync();
}

Could they be optimized to:

private async Task<int> CallerAsync() {
    return await SomeOperationAsync();
}

Extra Credit:

If it is supported, who can decide what's inlined? The compiler? The JIT? me?

If it's not supported, should I worry about this and avoid excessive wrappers I sometimes add for readability?

like image 433
talkol Avatar asked Sep 02 '13 07:09

talkol


People also ask

Can async method have multiple awaits?

For more information, I have an async / await intro on my blog. So additionally, if a method with multiple awaits is called by a caller, the responsibility for finishing every statement of that method is with the caller.

Do async functions run synchronously?

Top-level code, up to and including the first await expression (if there is one), is run synchronously. In this way, an async function without an await expression will run synchronously. If there is an await expression inside the function body, however, the async function will always complete asynchronously.

Does async await run in parallel?

In order to run multiple async/await calls in parallel, all we need to do is add the calls to an array, and then pass that array as an argument to Promise. all() .

Does async use threading?

Async methods don't require multithreading because an async method doesn't run on its own thread. The method runs on the current synchronization context and uses time on the thread only when the method is active. You can use Task.


1 Answers

Considering the complexity of the transformations caused by async/await, I don't think the code is inlineable: async/await cause your method to be tranformed in an hidden class and your code becomes a state machine, with various parts of the code becoming different states.

To give an example, a simple method as CalleeAsync() is transformed to a monster like:

[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;
    }
}

(note that on this machine I still have the Visual Studio 2010 with the Async CTP, with .NET 4.5 the generated code could be different).

Do you think something like that is inlineable?

like image 96
xanatos Avatar answered Sep 18 '22 12:09

xanatos