Real World NHibernate: Reducing startup times for large amount of entities
The scenario that Christiaan Baes need to solve is reducing the startup time of a Win Forms application. The main issue here is that the initial load of the application should be fast, but in this case, we are feeding NHibernate about a hundred entities, so it take a few seconds to run them.
I asked Christiaan to send me profiler results of the code, and it looked all right on his end, so it was time to look at NHibernate and see what she had to say about that.
The test scenario was startup time for a thousands entities. I think that this is a nice shiny number that probably represent way too much entities per application, but let us go with it.
Initial testing showed that for this amount, we get about 14 seconds startup time, just to get the session factory started. Now that wasn't right, I felt. NHibernate does a lot of reflection on startup, but even so, 14 seconds was quite a bit. A deeper analysis showed that I was wrong, it wasn't reflection that was costing so much, it was actually building the Configuration that took the time, not building the session factory.
Why was it taking so long? Well, NHibernate is using XML for configuration, and that xml is validating using a schema. And that took 11 seconds for a thousand documents.
I played a bit with the code, but it remained steady on 11 seconds just for reading the configuration in. Eventually I tried to do merge the 1000 XML documents to a single big document, and that had a significant effect, it dropped the time to just over 3 seconds. To an overall startup time of 5.5 seconds on a 1000 entities session factory.
If you are going in this route, I would strongly suggest that you would do this merging as a pre build step, rather than try to work with a single unwieldy artifact.
Still not good enough, I hear you think, right?
Here are a few other suggestions that are worth keeping in mind:
- Why are you initializing it on startup? And does it have to be on the main thread? If you can push it to a background thread, then this is usually all that you would need to do.
- Do you really need all the entities, from the get go? If not, you can create two session factories. One to serve as a fast initializing factory, able to respond to the initial requests. At the same time, initial the global session factory, and then replace them.
- Do you really need all those entities in a single session factory? If you have a lot of entities, usually this is a sign of several mixed domains that are involved. It would probably be better to split them up to different session factories.
If all of the above still isn't enough for you, the next step is persistable configuration itself (probably using serialization. That is not supported by NHibernate at the moment, although I am more than willing to accept a patch that would add this functionality.
Hope this helps...