Performance issues

Topics: EF Runtime
Nov 29, 2012 at 8:41 PM

We were doing a performance test of EF 5 and EF 6(alpha) against NHibernate.

Our test consisted of inserting records in one single table (without constraints, etc), running multiple threads. We found that EF 5 was of an order of magnitude slower than NHibernate and EF 6 was a bit faster but still about 3 times slower than NHibernate. 

EF 6 was inserting approx 75K records in a minute and NHibernate managed about 250K/per minute. The entity in both the cases were POCO. We would like to know the reason for such a low throughput, since we are evaluating both ORMs for our new project and would prefer to go with EF since we are a MS shop.

Developer
Nov 30, 2012 at 6:22 PM

Hi axceler,

Many things can impact performance and it's to identify what can be done without more details. Can you post the test code you are using?

Thanks,
Arthur

Nov 30, 2012 at 7:08 PM

Hi Arthur,

Thanks for your reply. We are inserting one row at a time in a loop, the loop is in a function which is called from many threads. 

This is the NHibernate code

 		while (!_isDone)
                {
                 using (var session = _sessionFactory.OpenSession ())
                 {
                     using (var transaction = session.BeginTransaction ())
                     {
                         var qrow = new row ()
                         {
                             ThreadId = qthread_id,
                             Text = qtext,
                             Timestamp = qnow,
                             Secs = qnow.Millisecond,
                         };
 
                         session.SaveOrUpdate (qrow);
                         transaction.Commit ();
 
                         }
                     }
                }

 

 

This is the EF6 code

	  while (!_isDone)
             {
                 using (entities qdb = new entities ())
                 {
                     var qrow = new row()
                     {
                         ThreadId = qthread_id,
                         Text = qtext,
                         Timestamp = qnow,
                         Secs = qnow.Millisecond,
                     };
 
                     qdb.Loads.Add (qrow);
                     qdb.SaveChanges ();
 
                 }
             }

We noticed a minor difference in the sql that is generated by NHibernate and EF6 (EF6 selects the RowCount).

Please let us know if we should do something different.

 

Thanks,

 

Mahesh Menghani @ axceler 

 

Developer
Nov 30, 2012 at 8:13 PM

axceler,

One thing to try is disabling DetectChanges on the context using:

    entities.Configuration.AutoDetectChangesEnabled = false;

Beyond that, I'm not sure what else to suggest but I'll let some other people in the team look at it to see if they have other ideas.

Thanks,
Arthur

 

Dec 4, 2012 at 1:22 PM

Thanks Arthur, we had already tried that, it did not make any difference.

Jan 8, 2013 at 12:54 AM

I've found that a DbContext is quite a bit more expensive to create than an NHibernate session. Just like you reuse your NHibernate session factory, so can you reuse a DbContext.

However, DbContext isn't threadsafe so when I have a class that is going to be called from many threads I manage my context with a thread local variable like this:

    private readonly ThreadLocal<DataContext> _contextProvider = new ThreadLocal<DataContext>(() => new DataContext());

Then in the method I just use _contextProvider.Value as I would a new context.

Brad

Jan 23, 2013 at 6:11 AM

If you didn't want to simulate a web sever scenario where you have a new context per request, you could also move SaveChanges out of the loop. did this in a Seed method, that inserted a few thousand test entities with many references.

One call to SaveChanges in every iteration (like in your test code) or only one at the end can be very slow, but calling SaveChanges every 100 iterations gave me much better performance (from 10 minutes to 42 seconds). I'm not sure if this applies to entities without any references, too...

Developer
Jan 23, 2013 at 4:04 PM

@mat1024 It is interesting that you say "only one at the end can be very slow". It is a common to use the EF context as a Unit of Work pattern. That is, you create a context instance, setup all your current work (for example, setting up your seed data) in the context, and then call SaveChanges once at the end. If it is actually faster to call SaveChanges once every 100 iterations that would be quite interesting and I would like to see a repro if possible.

Jan 23, 2013 at 6:40 PM
Edited Jan 23, 2013 at 6:42 PM

@ajcvickers: Please have a look at the following answers at stackoverflow:

SaveChanges every n iterations and using a new context seems to deliver the best performance.

Mat