简体   繁体   中英

Async calls slower than sync when using EF Core

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

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.

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

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM