2

I'm looking for why this might be or ways that I can speed it up.

I essentially construct an object with code like this

(pseudo code)

Person p = new Person();

Address a = new Address() { ... properties set here ... };
Employer e = new Employer() { ... properties set here ... };
// etc.

p.Employer = e;
p.Address = a;
//etc.

And finally:

_context.Person.Add(p);

When I profile the code, the construction of the person takes 0ms it's lightning fast. When I add the person the context however, it takes about 1500ms. Could it be to do with constraint checking? The database is quite large although it's not loaded into memory or anything so at the point it does the Add it shouldn't have touched the database.

Only when I do _context.SaveChanges() should it be written.

Any suggestions to improve speed?

Edit: Benchmark is done like this: (sw is a new Stopwatch())

sw.Restart();
context.Person.Add(p);
Console.WriteLine("Person added to context: " + sw.ElapsedMilliseconds + "ms");

Edit 2: The plot thickens.

As I'm loading in a huge number of people and adding them into the database, one of the first operations I do is see if the person already exists in the database. For speed reasons I pull a full list of Person.PersonId into a hashtable and check them for each record to be inserted.

If the person already exists in the list, I pull them out of the database. This takes ~5ms and I don't do anything with the data (as I haven't written that part yet) and just skip to the next one.

However when it reaches the new entries that are being added to the context, it is that line that causes a massive slowdown. Here's the code:

matchPerson = _context.Person.SingleOrDefault(c => c.PersonId == intPersonId);

So it pulls the person out of the database if they are already in it via the 0ms hashtable check (it's instant).

It takes ~5ms to do it.

If I do the above _context.Add when this line is present, it goes super slow. If I comment it out, it takes about 40ms and with each added record increases (so after 2,000 records it takes about 200ms).

There's no link I can see between the two lines, so perhaps the context query resets the state of the context and slows down the next operation?

NibblyPig
  • 51,118
  • 72
  • 200
  • 356
  • 3
    1500 ms is too much even if it's writing in db. Are you sure that your benchmarks are correct (or nothing else is involved in operation)? – Leri Aug 29 '13 at 09:37
  • I think so, I just wrap a stopwatch around that line. I've added code to the question. – NibblyPig Aug 29 '13 at 09:39
  • Are you performing the test in the IDE? Did you compile to release or debug mode? See [here](http://tech.pro/blog/1293/c-performance-benchmark-mistakes-part-one) for common benchmarking mistakes. – DGibbs Aug 29 '13 at 09:40
  • Yeah, it's in debug, I'll try it compiled in release mode but I wouldn't have thought it would make enough of a difference as I'm not trying to shave milliseconds off. I'll let you know shortly... – NibblyPig Aug 29 '13 at 09:46
  • Are you using an SQLExpress database on Localhost? Do you get the same slow time if you later on, in your program, do the same thing? When I'm running code like that it takes the same time for me in the beginning when I startup the application and I'm running the first inserts into the database, but later on it goes quicker. I've just assumed it's a startup time for all the database files. – Ohlin Aug 29 '13 at 09:55
  • 2
    Are the entities particularly large/complex? Potentially related to automatic change detection (http://stackoverflow.com/questions/5943394/why-is-inserting-entities-in-ef-4-1-so-slow-compared-to-objectcontext) although it seems unlikely from what you're saying =o. Good point by Ohlin, does this occur on every Add, or just the first time? – Chris Aug 29 '13 at 09:57
  • @SLC `Add` method may be calling `Equals`, if it's overriden it might be a bottleneck as well. – Leri Aug 29 '13 at 09:58
  • Added more information. They are quite large, there are a lot of properties and some subproperties. It occurs every time although see my edit, it seems to be slow if I access the context while I am adding to it. – NibblyPig Aug 29 '13 at 09:59
  • You beauty, I disabled automatic change detection and it now takes 0ms!!! Pop that in the answer box for a gold star. I think just the savechanges part is slow now. – NibblyPig Aug 29 '13 at 10:01
  • @SLC In that case, this should be closed as dupe. No point in copy/pasting answer. :) – Leri Aug 29 '13 at 10:05
  • I think that's for duplicate questions, not duplicate answers. But let the democracy decide! – NibblyPig Aug 29 '13 at 10:06
  • Added an answer with a link to the proper answer (as it's not really my answer), in case someone ends up here =D – Chris Aug 29 '13 at 10:17

1 Answers1

1

As requested in the comments (in case this isn't closed a duplicate), the slowdown was related to automatic change detection, which is on by default in the DbContext API.

To disable automatic change detection:

context.Configuration.AutoDetectChangesEnabled = false;

A much more complete/full description (which I certainly can't better here) can be found in this accepted answer:

Why is inserting entities in EF 4.1 so slow compared to ObjectContext?

Community
  • 1
  • 1
Chris
  • 8,268
  • 3
  • 33
  • 46
  • Thanks. It was instant with this. I'd still be interested to know why doing a query on the context in between saving changes has such a performance cost. – NibblyPig Aug 29 '13 at 12:35
  • I'm not sure if the first link in the comments of that answer (http://stackoverflow.com/questions/5917478/what-causes-attach-to-be-slow-in-ef4/5921259#5921259) sheds any additional light (relates to the actual process of taking the object snapshots, with some benchmarks). – Chris Aug 29 '13 at 13:53