3

Any idea why this code takes 49 ms

public void OnGet(String sessionId)
{
    BackgroundEntry =  _context.BackgroundEntry.Where(x => x.Id == sessionId).ToList();
}

but this code takes 300+ ms?

public async Task OnGetAsync(String sessionId)
{
    BackgroundEntry = await _context.BackgroundEntry.Where(x => x.Id == sessionId).ToListAsync();
}

I would expect the same time for both. Tested this in various conditions and it always the same, async has 300+ ms delay.

BackgroundEntry is autogen by EF:

public partial class BackgroundEntry
{
    public string Id { get; set; }
    public string Part { get; set; }
    public long Datetime { get; set; }
    public DateTime CreatedAt { get; set; }
    public Guid Session { get; set; }
    public string Action { get; set; }
    public string Domain { get; set; }
    public string Location { get; set; }
    public long? LastEntryDatetime { get; set; }

    public BackgroundEntry BackgroundEntryNavigation { get; set; }
    public BackgroundEntry InverseBackgroundEntryNavigation { get; set; }
}

benchmark with stopwatch:

        using (Models.RecorderContext context = new Models.RecorderContext())
        {
            sw.Start();
            var BackgroundEntry = context.BackgroundEntry.Where(x => x.Id == sessionId).ToList();
            sw.Stop();
        }

        var g = sw.ElapsedMilliseconds;

        sw.Reset();
        // g = 22 ms

        using (Models.RecorderContext context = new Models.RecorderContext())
        {
            sw.Start();
            var BackgroundEntry = await context.BackgroundEntry.Where(x => x.Id == sessionId).ToListAsync();
            sw.Stop();
        }

        g = sw.ElapsedMilliseconds;

        // g = 328 ms
realPro
  • 1,713
  • 3
  • 22
  • 34
  • Can you show us how you benchmarked this? How did you find out that this particular piece of code took +300ms? Also can you share your implementation of `BackgroundEntry`? – FCin Sep 25 '18 at 18:08
  • 1
    @FCin The performance is reported by Chrome dev tools, network statistics. The page is tested on both iisexpress and as standalone core app in release mode. Running locally with local installation of SQL Server version 12.0.4522.0. EF Core version 2.1... Benchmarking a single page load... – realPro Sep 25 '18 at 19:14
  • 2
    I'd recommend doing a more through bench-mark. Wrap the calls in a `StopWatch` to make sure you are measuring just the DB call. I'd also isolate it with a new `DbContext` to make sure it isn't bringing in other cached data. Multiple calls inside a loop might also be helpful, in order to average out "noise", and to make sure you are not dealing with JIT artifacts. – Bradley Uffner Sep 25 '18 at 19:49
  • @BradleyUffner I did not implement anything here. This is the default Visual Studio template named "Razor Page using Entity Framework". The above code is the scaffolded implementation, so I will not start changing it... – realPro Sep 25 '18 at 20:22
  • I'm not suggesting you change it permanently, but if you want our help figuring this out, you are going to have to get us some diagnostic information. – Bradley Uffner Sep 25 '18 at 20:23
  • @BradleyUffner added the benchmark – realPro Sep 25 '18 at 20:44

2 Answers2

4

It's impossible to say exactly without more context, but in general, I think you're incorrectly assuming that async should be faster, when in fact it's the opposite.

Async is about scale, not performance. It allows you to use server resources more efficiently, but there is a cost, even if only minimal, to performance for that. There's overhead involved in handling async, thread-switching, etc., all of which can make the same operation actually slower than an equivalent sync operation. However, sync provides no opportunity to use the thread for other work when it's idle, so you're hamstringing your potential server throughput by using sync operations. It's a trade-off that generally falls on the side of async as the best approach.

Chris Pratt
  • 232,153
  • 36
  • 385
  • 444
  • 1
    "you're incorrectly assuming that async should be faster". Where did the OP say that? – FCin Sep 25 '18 at 13:56
  • 1
    @FCin The OP didn't need to explicitly say it. By providing a sync and async implementation and asking why the async code is running slower, one can reasonably assume they think the async code should be faster, or at least the same speed. – mason Sep 25 '18 at 14:01
  • 5
    @mason Obviously +250ms is not caused by async. If calling async would cause 1/4th of a second of delay, nobody would use it. OP wrote "I would expect the same time for both" and this is actually what should happen. Async overhead is minuscule, especially for .net core. – FCin Sep 25 '18 at 14:03
2

Adding to the answer given by @rducom here and @ChrisPratt. If you are dealing with data 1MB< this issue is still present in Microsoft.EntityFrameworkCore 6.0.0

The blocking part is actually SqlClient for Async calls and the recommended workaround by @AndriySvyryd that works on the EF core project is:

Don't use VARCHAR(MAX) or don't use async queries.

This happened to me when reading a large JSON object and Image (binary) data with async queries.

Links:

https://github.com/dotnet/efcore/issues/18571#issuecomment-545992812

https://github.com/dotnet/efcore/issues/18571

https://github.com/dotnet/efcore/issues/885

https://github.com/dotnet/SqlClient/issues/245

https://github.com/dotnet/SqlClient/issues/593

Ogglas
  • 62,132
  • 37
  • 328
  • 418
  • Wish this was the marked answer, I've spent 1/2 a day to come to the same conclusion. – Akli Nov 01 '22 at 17:24