Use of SGEN.exe to avoid common XmlSerializer performance pitfalls

This week, I encountered a test scenario which makes use of an XmlSerializer on it's startup path. Unfortunately, MSDN's documentation for the type is not clear on the performance impact of using an XmlSerializer. The documentation on introducing XML serialization, found here, actually mentions the solution, but it also does not state what the problem is.

When you use an XmlSerializer, a helper assembly(1) with a custom type for serializing your type is created. The C# CodeDom is used to generate this code, which then is compiled by the C# compiler. The runtime must then load this generated assembly, and JIT the code. Additionally, a separate assembly is generated for each type you create an XmlSerializer for, each of which requires an invocation of the C# compiler.

As you can imagine, running the C# compiler potentially several times to generate these assemblies can cause a large performance hit, especially on your startup path. (On your startup path, since the application blocks while this is happening, your user will think they didn't click the right thing, or that your app is broken because it isn't loading fast enough.)

The really great news, though, is that you can avoid all of this. Starting with version V2.0 of the runtime, shipped with Visual Studio 2005, there is a new SDK tool called Sgen.exe. This tool works much like NGEN, to create serialization helper assemblies ahead of time. You can do this as a post build step, and then ship these binaries. You can (and should!) also NGEN them. When you sgen MyLibrary.dll, you'll get a file called MyLibrary.XmlSerializers.dll. This DLL will serializers for all of the types in the assembly, thereby giving you some consolidation over the non-SGEN route.

So, then, how does one use SGEN as a post build step? In your VS project's Build Events tab, you can add a line like this to the post build steps:

"<PathToSdk>\sgen.exe" "$(TargetPath)"

Where of course <PathToSdk> is whatever is correct for your environment. (If you have an SDK path environment variable, you can use it here.

Now you have a DLL you can ship and NGEN as part of your install process! Your customers will thank you.

Please post in the comments if I can clear something up.

1: You can see this happen in the debugger. You'll see a module loaded with a temp file name, like 9rg4jqp1. This assembly is typically generated in memory, but if you want to look at the generated code, add this to your application config file:
<system.diagnostics>
        <switches>
           <add name="XmlSerialization.Compilation" value="4"/>
        </switches>
</system.diagnostics>
The dlls, pdbs, and source will appear in your %TEMP% directory.