It’s been a long time since this blog has seen any posts about Cider. I haven’t been sleeping. Instead, I’ve been tuning. For the last six months I’ve been heads down focusing on the performance of Cider in VS 2010 both for WPF and for Silverlight. ScottGu’s post about creating another public release candidate hopefully has you convinced that we’re very serious about ensuring that the performance of the product meets your expectations. I thought it would be helpful if I talked about some of the specific things we’ve done in Cider to help with performance.
Speeding Up Beta 2
Cider in VS 2010 is a rather different beast than the version we shipped in VS 2008. It uses the Blend rendering engine and we completely rewrote how we access reflection information. Those two pieces are core to the designer and it isn’t that surprising that our first real focus on performance would find lots of places to optimize and fix. We took performance fixes for Cider to VS shiproom right up until Beta 2 was out the door. One issue didn’t make it. If you read the Beta 2 readme, you’ll see this little blurb of information:
Now would be a good time to make jokes about taking out the slow code. It seems as if we left some slow code in, only to allow it to be removed via a registry key. What this key does is change the mode Visual Studio’s app domain uses to load and share assemblies. We found a bug that was preventing .NET from configuring itself to use “shared” mode. Without this configuration value set, secondary app domains (like the one Cider uses) cannot share the nGen images of the framework and must JIT the entire .NET framework. This is what professionals refer to as “a whole bag of suck” because it takes several seconds to JIT something so large.
What’s unfortunate is that we found this bug after we had completed testing on VS for Beta 2. We couldn’t be sure that enabling sharing would be safe for the rest of the product. We took the safe route by fixing the bug but setting a registry key that effectively disabled it.
If you’re using Cider and seeing slow load performance, you should definitely try setting this key to the value specified in the readme.
The other piece of advice I would give you for Beta 2 is to make sure you’re running either Vista or Windows 7. Most of VS 2010’s performance testing was done using Vista and rather late we found that SuperFetch was giving us a profound improvement in performance. You won’t get that on XP or server operating systems.
RC and Beyond
I wasn’t sure we could get much more out of Cider for RC and beyond. I’d focused on loading scenarios for months, looking at literally gigabytes of data every day. I thought I had found all there was to find. But here I am, still finding some good performance wins. Here are some of my favorites.
Silverlight Jit: I’ve always had a performance problem with Silverlight I couldn’t solve. Cider runs in multiple app domains and we create new app domains after build to reload new assemblies, so we’re very sensitive to cold app domain startup. I don’t want to JIT any more than I have to, and I’d prefer to JIT once per process instead of once per domain. Silverlight throws a wrinkle in here: we load Silverlight assemblies dynamically from the Silverlight SDK directory. Assemblies are not eligible for app domain code sharing unless they are in the GAC or were loaded via probing. That means all of Silverlight has to JIT for each Cider app domain. Thanks to some clever sleuthing by the CLR and VS platform teams I found a way to dynamically rewrite Cider’s app domain configuration so I can load Silverlight via the standard probing mechanism. This takes about two seconds off designer reload for Silverlight.
HwndSource Resize: Because Cider runs in a secondary domain, it “isolates” itself from the rest of VS via an HWND boundary. We use HwndSource to create a boundary at the designer, property window and document outline. When HwndSource sees a size change message, it asks WPF to perform a synchronous layout. This causes Cider to recompute adorner locations and scene layout more than once (a minimum of three times actually), which slows down loading of the designer. My fix for this was to write a DeferredHwndSource that simply waits for the dispatcher to be ready before plugging in the root visual. The improvement you’ll see here is relative to the complexity of your XAML page. I’ve seen it take three seconds off of really large and complex pages, but 250ms is more typical.
F5: Starting the app was taking way too long. A big part of this is that F5 builds, and after a build Cider reloads its app domain. It turns out that our app domain reload had a whole pile of low hanging fruit. Here’s the before and after graphs of the timing breakdown:
Here I fixed an obvious bug where we actually reloaded the app domain twice. Then, since the designer is in a read-only state during debugging anyway, I deferred the actual app domain reload until after debugging completes so you never see it during launch.
I’m hoping you’ll find the performance of VS 2010 RC much better than Beta 2. In addition to timings, many of us spent a lot of time reducing thread count, memory usage and file IO across most aspects of VS 2010 so the RC should be generally much snappier. A quick scan of the VS 2010 bug database shows that we fixed over six thousand performance issues. We’re almost done, but not quite yet. We are still actively investigating performance issues customers raise on Connect, and we’re also dogfooding the product heavily and responding to feedback we get from across the company. If you have performance issues with the RC (or even with the Beta), let us know through connect. I’m sure there are scenarios we haven’t seen yet.