2013-09-02 3 views
6

Inlining functions - это оптимизация компилятора, которая заменяет сайт вызова функции телом вызываемого абонента. Эта оптимизация поддерживается для regular C# functions.Могут ли функции async быть встроенными?

Async функции, которые поддерживают шаблон async-await в C# 5.0 имеют специальную декларацию, которая включает в себя 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

Не нужно создавать еще одну задачу. 'private Task CalleeAsync() { return SomeOperationAsync(); } ' – I4V

+0

@ I4V Конечно. Имейте в виду, что это просто упрощенный концептуальный пример. Сценарии реальной жизни, вероятно, будут немного сложнее :) – talkol

ответ

4

Учитывая сложность преобразований, вызванных async/await, я не думаю, что код inlineable: async/await вызвать ваш метод быть tranformed в скрытом классе и ваш код становится состояние машины, с различными частями кода становится разные состояния.

К примеру, простой метод, как 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; 
    } 
} 

(обратите внимание, что на этой машине я до сих пор Visual Studio 2010 с асинхронным CTP, с .NET 4.5 сгенерированный код может быть другим).

Считаете ли вы, что что-то подобное является inlineable?

+2

Ну, если мне нужно было пойти с тем, что, как я думал, я бы сказал, что большинство оптимизаций компилятора невозможны :) Что делать, если компилятор попытался встроить перед компиляцией в эту машину состояний монстр?Я вижу, что это происходит на уровне предварительного процессора (если он существует), хотя я не знаю, что я говорю о – talkol

+0

@talkol. Компилятор C# не делает ничего особенно интересного и редко модифицирует слишком много кода , Я не думаю, что это делает inlining (я думаю, что JIT, который строит код) – xanatos

1

Компилятор C# создаст довольно много кода IL для «простого» await, xanatos уже показал, как выглядит код IL, когда он будет переведен на C#. Поскольку JIT-компилятор (это тот, кто делает inlining) имеет строгие правила о встраивании, я сомневаюсь, что он будет встроить весь этот код.

Here - это некоторые правила для встраивания JIT (это может быть неверно для текущего JIT, но это хороший старт). Здесь я уже вижу два нарушения правил: у нас есть более 32 байт кода IL и блок обработки исключений.

Смежные вопросы