Este artigo é baseado em alguns estudos que estou realizando e na busca de manter esse conhecimento, decidi escreve-lo para compartilhar o que tenho aprendendo.

Se você é um dev .Net com certeza já utilizou ou está utilizando métodos async/await. Vamos ver que mesmo algo que foi pensado para nos auxiliar em performance, se utilizado de “forma errada”, pode prejudicar a performance.

Vou mostrar que podemos ganhar em performance utilizando o async/await. Claro, sempre vai depender de onde você vai utilizar. Por exemplo, caso você precisa fazer uma consulta no banco, você pode chamar o método do seu “BaseRepository” sem utilizar o “await”. Vou mostrar isso em seguida. Para estes testes, estou utilizando o .NET 8.0.10 (8.0.1024.46610), X64.

Aqui utilizei o site SharpLab para pegar o código gerado.

Show me the code!

Nossa classe de teste. Nossa classe está muito simples, é apenas para exibir a diferença em chamar o método “corretamente”.

image_2024-11-20_102246119 Async/Await pode estar te prejudicando em performance

Nossa classe onde executamos nosso teste.

image_2024-11-20_102317415 Async/Await pode estar te prejudicando em performance

Resultado do nosso teste.

image_2024-11-20_102407100 Async/Await pode estar te prejudicando em performance

Como podemos ver, sem o “await” dentro do nosso método (ReturnWithoutAwaitAsync) foi aproximadamente 6 vezes mais rápido. Mas agora, vamos ver esses métodos “por dentro”.

Vou resumir aqui o que acontece com o “async/await”, mas pretendo fazer um novo artigo com mais detalhes sobre isso. Mas quando criamos um método “async/await”, criamos uma máquina de estado. Nessa máquina de estado, temos algumas informações do estado do “await”, se já foi executado, se será a primeira vez, etc e, temos uma “cópia” do método a ser executado. Veja a quantidade de código que foi criado “a partir do await”.

[CompilerGenerated] privatesealed class <M>d__1 : IAsyncStateMachine  { public int <>1__state;  public AsyncTaskMethodBuilder <>t__builder;   [Nullable(0)] public C <>4__this;   [Nullable(0)] private string <message>5__1;   [Nullable(0)] private string <>s__2;   [Nullable(newbyte[] {0,1 })] private TaskAwaiter<string> <>u__1;  privatevoidMoveNext()  {   // Copia o estado atual para a variável local  // Valor inicial será -1 intnum=<>1__state; try  {  // Variável que irá manter o retorno da nova task TaskAwaiter<string>awaiter; if (num!=0)  {  awaiter=<>4__this.Print().GetAwaiter(); if (!awaiter.IsCompleted)  {  num= (<>1__state=0); <>u__1= awaiter; <M>d__1stateMachine=this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter,ref stateMachine);   // Primeira chamada: Libera a CPU ou Thread para quem chamou o método return;  }  } else  {  // Chamada de "despertar": Aque pegamos o "awaiter" do contexto de execução  awaiter=<>u__1; <>u__1=default(TaskAwaiter<string>);  num= (<>1__state=-1);  } <>s__2= awaiter.GetResult(); <message>5__1=<>s__2; <>s__2=null;  Console.WriteLine(<message>5__1);  } catch (Exceptionexception)  { <>1__state=-2; <message>5__1=null; <>t__builder.SetException(exception); return;  } <>1__state=-2; <message>5__1=null; <>t__builder.SetResult();  }  voidIAsyncStateMachine.MoveNext()  {  //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext();  }   [DebuggerHidden] privatevoidSetStateMachine(IAsyncStateMachinestateMachine)  {  }  voidIAsyncStateMachine.SetStateMachine(IAsyncStateMachinestateMachine)  {  //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine);  }  }

Vamos ver como fica um método onde chamamos o await dentro dele. Aqui mostro o mesmo método que utilizei no teste, veja que ele acabou criando 2 máquinas de estado.

image_2024-11-20_102633069 Async/Await pode estar te prejudicando em performance

Resultado do código acima:

[CompilerGenerated] privatesealed class <M>d__2 : IAsyncStateMachine  { public int <>1__state;  public AsyncTaskMethodBuilder <>t__builder;   [Nullable(0)] public C <>4__this;   [Nullable(0)] private string <message>5__1;   [Nullable(0)] private string <>s__2;   [Nullable(newbyte[] {0,1 })] private TaskAwaiter<string> <>u__1;  privatevoidMoveNext()  { intnum=<>1__state; try  { TaskAwaiter<string>awaiter; if (num!=0)  {  awaiter=<>4__this.MyMethodWithAwaitAsync().GetAwaiter(); if (!awaiter.IsCompleted)  {  num= (<>1__state=0); <>u__1= awaiter; <M>d__2stateMachine=this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter,ref stateMachine); return;  }  } else  {  awaiter=<>u__1; <>u__1=default(TaskAwaiter<string>);  num= (<>1__state=-1);  } <>s__2= awaiter.GetResult(); <message>5__1=<>s__2; <>s__2=null;  } catch (Exceptionexception)  { <>1__state=-2; <message>5__1=null; <>t__builder.SetException(exception); return;  } <>1__state=-2; <message>5__1=null; <>t__builder.SetResult();  }  voidIAsyncStateMachine.MoveNext()  {  //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext();  }   [DebuggerHidden] privatevoidSetStateMachine(IAsyncStateMachinestateMachine)  {  }  voidIAsyncStateMachine.SetStateMachine(IAsyncStateMachinestateMachine)  {  //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine);  }  }    [CompilerGenerated] privatesealed class <MyMethodWithAwaitAsync>d__1 : IAsyncStateMachine  { public int <>1__state;   [Nullable(0)] public AsyncTaskMethodBuilder<string> <>t__builder;   [Nullable(0)] public C <>4__this;   [Nullable(0)] private string <>s__1;   [Nullable(0)] private TaskAwaiter<string> <>u__1;  privatevoidMoveNext()  { intnum=<>1__state; stringresult; try  { TaskAwaiter<string>awaiter; if (num!=0)  {  awaiter= Task.FromResult("Hello World!").GetAwaiter(); if (!awaiter.IsCompleted)  {  num= (<>1__state=0); <>u__1= awaiter; <MyMethodWithAwaitAsync>d__1stateMachine=this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter,ref stateMachine); return;  }  } else  {  awaiter=<>u__1; <>u__1=default(TaskAwaiter<string>);  num= (<>1__state=-1);  } <>s__1= awaiter.GetResult();  result=<>s__1;  } catch (Exceptionexception)  { <>1__state=-2; <>t__builder.SetException(exception); return;  } <>1__state=-2; <>t__builder.SetResult(result);  }  voidIAsyncStateMachine.MoveNext()  {  //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext();  }   [DebuggerHidden] privatevoidSetStateMachine(IAsyncStateMachinestateMachine)  {  }  voidIAsyncStateMachine.SetStateMachine(IAsyncStateMachinestateMachine)  {  //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine);  }  }

Agora vamos ver sem o await interno.

image_2024-11-20_102824660 Async/Await pode estar te prejudicando em performance

Resultado do código acima:

[CompilerGenerated] privatesealed class <M>d__2 : IAsyncStateMachine  { public int <>1__state;  public AsyncTaskMethodBuilder <>t__builder;   [Nullable(0)] public C <>4__this;   [Nullable(0)] private string <message>5__1;   [Nullable(0)] private string <>s__2;   [Nullable(newbyte[] {0,1 })] private TaskAwaiter<string> <>u__1;  privatevoidMoveNext()  { intnum=<>1__state; try  { TaskAwaiter<string>awaiter; if (num!=0)  {  awaiter=<>4__this.MyMethodWithAwaitAsync().GetAwaiter(); if (!awaiter.IsCompleted)  {  num= (<>1__state=0); <>u__1= awaiter; <M>d__2stateMachine=this; <>t__builder.AwaitUnsafeOnCompleted(ref awaiter,ref stateMachine); return;  }  } else  {  awaiter=<>u__1; <>u__1=default(TaskAwaiter<string>);  num= (<>1__state=-1);  } <>s__2= awaiter.GetResult(); <message>5__1=<>s__2; <>s__2=null;  } catch (Exceptionexception)  { <>1__state=-2; <message>5__1=null; <>t__builder.SetException(exception); return;  } <>1__state=-2; <message>5__1=null; <>t__builder.SetResult();  }  voidIAsyncStateMachine.MoveNext()  {  //ILSpy generated this explicit interface implementation from .override directive in MoveNext this.MoveNext();  }   [DebuggerHidden] privatevoidSetStateMachine(IAsyncStateMachinestateMachine)  {  }  voidIAsyncStateMachine.SetStateMachine(IAsyncStateMachinestateMachine)  {  //ILSpy generated this explicit interface implementation from .override directive in SetStateMachine this.SetStateMachine(stateMachine);  }  }

Está muito mais simples. Criou apenas uma máquina de estado.

Apenas para comparação e curiosidade, com o await interno, temos 197 linhas. Sem o await interno, temos 116 linhas.

Conclusão

Utilize sim o async/await que você terá performance, principalmente em escalabilidade. Claro que nem em todo método será possível utilizar a “técnica” apresentada aqui.

Pretendo em um próximo artigo, trazer um pouco mais de detalhes sobre a máquina de estado que o asyn/await cria.

Obrigado pela sua leitura.

Share this content: