<story><title>Asynchronous Programming in C#</title><url>https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md</url></story><parent_chain><item><author>bob1029</author><text>We use async&amp;#x2F;await pretty much universally throughout our codebase today.&lt;p&gt;One thing to keep in mind is that this mode of programming is actually not the most performant way to handle many problems. It is simply the most expedient way to manage I&amp;#x2F;O and spread trivial things across many cores in large, complex codebases. You can typically retrofit an existing code pile to be async-capable without a whole lot of suffering.&lt;p&gt;If you are trying to go as fast as possible, then async is not what you want at all. Consider that the minimum grain of a Task.Delay is 1 millisecond. A millisecond is quite a brutish unit when working with a CPU that understands nanoseconds. This isn&amp;#x27;t even a &lt;i&gt;reliable&lt;/i&gt; 1 millisecond delay either... There is a shitload of context switching and other barbarism that occurs when you employ async&amp;#x2F;await.&lt;p&gt;If you are seeking millions of serialized items per second, you usually just want 1 core to do that for you. Any degree of context switching (which is what async&amp;#x2F;await does for a living) is going to chop your serialized throughput numbers substantially. You want to batch things up and process them in chunks on a single thread that never gets a chance to yield to the OS. Only problem with this optimization is that it usually means you rewrite from zero, unless you planned for this kind of thing in advance.</text></item></parent_chain><comment><author>reubenbond</author><text>&amp;gt; Consider that the minimum grain of a Task.Delay is 1 millisecond.&lt;p&gt;The minimum here is contingent on a few things. The API can accept a TimeSpan which can express durations as low as 100ns (10M ticks per second: &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;docs.microsoft.com&amp;#x2F;dotnet&amp;#x2F;api&amp;#x2F;system.timespan.tickspersecond&quot; rel=&quot;nofollow&quot;&gt;https:&amp;#x2F;&amp;#x2F;docs.microsoft.com&amp;#x2F;dotnet&amp;#x2F;api&amp;#x2F;system.timespan.ticksp...&lt;/a&gt;). The actual delay is subject to the timer frequency, which can be as high as 16ms and depends on the OS configuration (eg, see &lt;a href=&quot;https:&amp;#x2F;&amp;#x2F;stackoverflow.com&amp;#x2F;a&amp;#x2F;22862989&amp;#x2F;635314&quot; rel=&quot;nofollow&quot;&gt;https:&amp;#x2F;&amp;#x2F;stackoverflow.com&amp;#x2F;a&amp;#x2F;22862989&amp;#x2F;635314&lt;/a&gt;). However, I&amp;#x27;m not sure how any of this relates to &amp;quot;go[ing] as fast as possible&amp;quot;, since surely you would simply not use a Task.Delay in that case.&lt;p&gt;&amp;gt; There is a shitload of context switching and other barbarism that occurs when you employ async&amp;#x2F;await.&lt;p&gt;Async&amp;#x2F;await reduces context switching over the alternative of having one thread per request (i.e, many more OS threads than cores) and it (async&amp;#x2F;await) exhibits the same amount of context switching as Goroutines in Go and other M:N schedulers. If there is work enqueued to be processed on the thread pool, then that work will be processed without yielding back to the OS. The .NET Thread Pool dynamically sizes itself depending on the workload in an attempt to maximize throughput. If your code is not blocking threads during IO, you would ideally end up with 1 thread per core (you can configure that if you want).&lt;p&gt;Async&amp;#x2F;await can introduce overhead, though, so if you&amp;#x27;re writing very high-performance systems, then you may want to consider when to use it versus when to use other approaches as well as the relevant optimizations which can be implemented. I&amp;#x27;d recommend people take the simple approach of using async&amp;#x2F;await at the application layer and only change that approach if profiling demonstrates that it&amp;#x27;s becoming a performance bottleneck.</text></comment>
<story><title>Asynchronous Programming in C#</title><url>https://github.com/davidfowl/AspNetCoreDiagnosticScenarios/blob/master/AsyncGuidance.md</url></story><parent_chain><item><author>bob1029</author><text>We use async&amp;#x2F;await pretty much universally throughout our codebase today.&lt;p&gt;One thing to keep in mind is that this mode of programming is actually not the most performant way to handle many problems. It is simply the most expedient way to manage I&amp;#x2F;O and spread trivial things across many cores in large, complex codebases. You can typically retrofit an existing code pile to be async-capable without a whole lot of suffering.&lt;p&gt;If you are trying to go as fast as possible, then async is not what you want at all. Consider that the minimum grain of a Task.Delay is 1 millisecond. A millisecond is quite a brutish unit when working with a CPU that understands nanoseconds. This isn&amp;#x27;t even a &lt;i&gt;reliable&lt;/i&gt; 1 millisecond delay either... There is a shitload of context switching and other barbarism that occurs when you employ async&amp;#x2F;await.&lt;p&gt;If you are seeking millions of serialized items per second, you usually just want 1 core to do that for you. Any degree of context switching (which is what async&amp;#x2F;await does for a living) is going to chop your serialized throughput numbers substantially. You want to batch things up and process them in chunks on a single thread that never gets a chance to yield to the OS. Only problem with this optimization is that it usually means you rewrite from zero, unless you planned for this kind of thing in advance.</text></item></parent_chain><comment><author>torginus</author><text>async&amp;#x2F;await is not for CPU-intensive parallelism. I think that&amp;#x27;s pretty much stated in the .NET docs. That&amp;#x27;s why Parallel Compute APIs like Parallel.ForEeach&amp;#x2F;For are not async. Their purpose is to enable non-blocking waits for IO, as well as to do stuff like animation on UI where you might want to execute procedural code over a larger timeframe.</text></comment>