Why wrapping awaitable async method into Task.Run improves the performance at least twice?
<p>I have a simple implementation of HTTP Server. The code is shown below. It was tested on the server machine with 32cores. If I wrap the processContext method into Task.Run call, then the performance doubles (at least). Considering that this gave me a performance gain in this particular case, I am confused now: having some method returning a Task, which I don't wish to wait for, what strategy should I follow? Should I call it directly or should I wrap inside Task.Run?</p>

<pre><code>class Program
{
    private static ConcurrentBag&lt;DateTime&gt; _trails = new ConcurrentBag&lt;DateTime&gt;();

    static void Main(string[] args)
    {
        Console.WriteLine($"Is server GC: {(GCSettings.IsServerGC ? "true" : "false")}");

        string prefix = args[0];

        CancellationTokenSource cancellationSource = new CancellationTokenSource();

        HttpListener httpListener = new HttpListener();

        httpListener.Prefixes.Add(prefix);

        httpListener.Start();

        Task.Run(async () =&gt;
        {
            while (!cancellationSource.Token.IsCancellationRequested)
            {
                HttpListenerContext context = null;

                try
                {
                    context = await httpListener.GetContextAsync();

                    if (cancellationSource.Token.IsCancellationRequested)
                    {
                        context.Response.Abort();

                        break;
                    }
                }
                catch (ObjectDisposedException)
                {
                    return;
                }
                catch (HttpListenerException ex)
                {
                    if (cancellationSource.Token.IsCancellationRequested &amp;&amp; ex.ErrorCode == 995)
                    {
                        break;
                    }

                    throw;
                }

                // Uncommenting below line and commenting the next one improves the performance at least twice
                // Task childProcessingTask = Task.Run(async () =&gt; await processContext(context));

                var dt = processContext(context);
            }
        });

        using (Timer t = new Timer(o =&gt; Console.Title = $"Async Server: {_trails.Count}", null, 0, 5000))
        {
            Console.WriteLine("Running...");

            Console.ReadLine();

            cancellationSource.Cancel();

            Console.WriteLine("Stopped accepting new request. Waiting for pending requests...");

            Console.WriteLine("Stopped");

            httpListener.Close();
        }

        var gTrails = _trails.GroupBy(t =&gt; new DateTime(t.Year, t.Month, t.Day, t.Hour, t.Minute, 0))
            .Select(g =&gt; new { MinuteDt = g.Key, Count = g.Count() })
            .OrderBy(x =&gt; x.MinuteDt).ToList();

        gTrails.ForEach(x =&gt; Console.WriteLine($"{x.MinuteDt:HH:mm}\t{x.Count}"));

        if (gTrails.Count &gt; 2)
        {
            decimal avg = gTrails.Take(gTrails.Count - 1).Skip(1).Average(g =&gt; (decimal)g.Count);
            Console.WriteLine($"Average: {avg:0.00}/min, {avg / 60.0m:00.0}/sec");
        }

        Console.ReadLine();
    }

    private static async Task processContext(HttpListenerContext context)
    {
        DateTime requestDt = DateTime.Now;
        Stopwatch sw = Stopwatch.StartNew();

        string requestId = context.Request.QueryString["requestId"];

        byte[] requestIdBytes = Encoding.ASCII.GetBytes(requestId);

        context.Response.ContentLength64 = requestIdBytes.Length;

        await context.Response.OutputStream.WriteAsync(requestIdBytes, 0, requestIdBytes.Length);

        try
        {
            context.Response.Close();
        }
        catch { }

        _trails.Add(requestDt);
    }
}
</code></pre>