NHibernate, manual flushing, performance and you!

Recently on my current project we decided to sit down and take a look at performance problems that we were having.  The main cause for concern was actually our most common and important operation since it would take anywhere from 20 to 40 minutes to complete. 

The first obvious step was to run a profiler against our code to see where we were actually spending our time and for this we choose DotTrace by JetBrains.  I really liked the ANTS Profiler time line view but for reasons that I can't recall right now we decided DotTrace was a better fit for our needs (I think it had to do with ease of comparing different runs).  So with DotTrace in hand we quickly found out that most of the time in our application was actually being spent in NHibernate (specifically in analyzing entity relationships).  This came as quite a shock to me since I know NHibernate is widely used and I had never heard of anything even approaching what we were seeing.

To cut a long story short while I did look into the NHibernate code it was eventually a bit of experimentation in our own code base that helped resolve the issue.  You see in our code it's very important that we track every step in our main process and to do this tracking we were using the actual entity Db Ids and the only way we knew to do so was to tell NHibernate to save the entity and then force a flush.  By removing the flush I was able to cut a 40 minute process down to 4 minutes!  The theory I ended up with is that each time we called Flush we were creating more and more stuff that NHibernate had to parse through each time Flush was called.  By only calling it once at the end it started with a nice clean slate.

So with all that said my advice to those of you who want to manually call Flush is.. DON'T! Not unless you really really need to and even then you should seriously consider other options/designs.

Oh and as for the whole Id thing we ended up using Session.Persist(entity) to get the Ids we need.  There is a bit of confusion around this method and if it's guaranteed to provide an Id but so far with our mappings we haven't had an issue.