One reason is that the generated IL (intermediate language) code is more costly when a function is declared async
. The C# async
/await
feature is not a feature that's available at the IL level, so the compiler has to generate some extra code to make that feature work in IL. This could result in less efficient performance (both in speed and memory usage) at runtime.
For example, compare the two methods in this SharpLab demo.
public async Task<int> NotReallyAsynchronous(){
return 5;
}
public int Sync(){
return 7;
}
In IL, Sync
is just a single method, short enough to copy here:
.method public hidebysig
instance int32 Sync () cil managed
{
// Method begins at RVA 0x208b
// Code size 2 (0x2)
.maxstack 8
IL_0000: ldc.i4.7
IL_0001: ret
} // end of method C::Sync
Whereas NotReallyAsynchronous
has a method, but also a nested type <NotReallyAsynchronous>d__0
containing two fields and two methods, the latter of which are not trivial and too large for me to paste here. Maybe not a huge amount of code generally speaking, but clearly if you can reduce it (across a large number of cases) by omitting async
, it could have an impact.