I have a Windows Forms app written in C# that takes approximately 12 seconds to load on a fast machine (P4 2.4Ghz 1GB memory, Windows XP Pro) when run the first time after booting. After I've run it once, the next time I run it the app was taking about 6 seconds. I made the following optimizations:
1. Added all strongly-named assemblies to the GAC on the target machines (mainly Infragistics and ComponentOne controls) so that the CLR doesn't do security checking on them every time the app is loaded.
2. N-Gen'ed ALL of the application's assemblies and dependent assemblies.
These improvements caused the 6 second time to drop to about 3.5 seconds, which is very acceptable. However, after freshly booting, and waiting for the hard drive to stop chirping and the cpu to settle down, it again takes 12 seconds to load the application. The second time I run, I'm back to about 3.5 seconds. The problem is, this application is typically run only once after booting, so the subsequent load times are largely irrelevant.
This is the fastest of my target machines. I have lower-end machines (233MHz PII/128MB memory) that take a full minute to load even after the optimizations.
The Compuware DevPartner profiler shows that roughly 50% of the time is spent in obvious user interface code (Infragistics, ComponentOne, System.Drawing, System.Windows.Forms, etc.).
The CLR JIT performance counter shows that without NGen, roughly 5700 methods are jitted every time I start this app, and another 100 or so when shutting it down (???). When I NGen every assembly, I still take a hit for about 500 to 600 JIT methods.
Am I just seeing the results of disk caching? The disk is being hit quite a bit the first time I run it, but hardly at all the subsequent times. The performance continues to be good, even after logout/login (same user) as long as the machine is not rebooted. I have observed this behavior on both machines.
On another machines (PIII 1GHz with plenty of memory) the load time after booting is always about 25 seconds, but about 8-10 seconds on subsequent invocations.
Am I just stuck with slow initial load times for .NET apps, or are there more optimizations I can do?