Entity Framework 5 poor performance

前端 未结 1 1144
粉色の甜心
粉色の甜心 2021-01-21 01:42

I have 5 entities:

public class Album
{
    public int Id { get; set; }

    public string Title { get; set; }

    public virtual List AlbumA         


        
相关标签:
1条回答
  • 2021-01-21 02:39

    Firstly a couple of clarifications:

    EF is not significantly slower than other methods for batch based operations. in my tests you can get perhaps a 50% improvement with a raw SQL Command and maybe up to 10 times faster with SQL Bulk copy but EF is not much slower than comparative methods as a general rule (though often perceived as very slow). For most applications EF will give suitable performance numbers even in batch scenarios given the correct tuning. (see my article here: http://blog.staticvoid.co.nz/2012/3/24/entity_framework_comparative_performance and http://blog.staticvoid.co.nz/2012/8/17/mssql_and_large_insert_statements)

    Because of the way EF does change tracking its has potential to far exceed the performance of how most people would write SqlCommand based insert statements (theres a whole lot of caviats to do with query planning, round trips and transactions that make it pretty hard to write optimally performing bulk insert statements). I have proposed these additions to EF here (http://entityframework.codeplex.com/discussions/377636) but havent implemented them yet.

    You are exactly right with your decision to turn off auto detect changes, each .Add or .Attach action with detect changes on enumerates the tracking graph, therefore if you're adding 17k additions on the same context you will need to enumerate the graph 17000 times over a total of 17000 + 16999 + ...+ 2 + 1 = 144,500,000 entities, no wonder its taking so long right? (see my article here: http://blog.staticvoid.co.nz/2012/5/7/entityframework_performance_and_autodetectchanges)

    Save changes always needs to enumerate the tracking graph (it calls detect changes internally) so your first way is going to be slow as its actually going to be doing the same number of tracking calls as above.

    The second way is much better but it still has a pretty major flaw which I imagine is twofold, firstly the graph is really big when you go to save changes (bigger graphs have exponentially higher tracking times), and secondly its going to take up a lot of memory to persist the whole graph at once especially given that EF stores two copies of each of your entities.

    A better way is to persist your graph in chunks. some

    //With Auto detect changes off.
    foreach(var batch in batches)//keep batch size below 1000 items, play around with the numbers a little
    {
        using(var ctx = new MyContext())//make sure you create a new context per batch.
        {
            foreach(var entity in batch){
                 ctx.Entities.Add(entity);
            }
            ctx.SaveChanges();
        }
    }
    

    I would expect you should target around 17-30s to do all 17k rows.

    by doing this with raw SQL commands you may be able to get this to around 12-20s;

    with a reimplementation with bulk copy you could probably get this down to 2-5s

    0 讨论(0)
提交回复
热议问题