Let me finally discuss some changes in product packaging; they will are
important for performance and reliability.
Understanding the Build Performance Issue
Many people complained about PostSharp performance and wondered if the new
release would bring some improvement. The short answer is: yes.
The computing cost of PostSharp can be broken down into the following items:
- Initialization Time. It takes time to start a CLR
process, load assemblies in the AppDomain, JIT-compile code, and so on. For
long-running applications, you usually don't see the difference (we are talking of
tenths of seconds), but PostSharp is a process that is typically triggered
every couple of seconds, so this is really important.
- Processing Time. It's the time PostSharp takes to
analyze and process your code and dependencies. Here, I want to be very
clear: the code is extremely fast and very difficult to further optimize. PostSharp
is designed from ground to be fast. For instance, some say that PostSharp
parses the output of ILDASM. This is a myth; it is plainly false. PostSharp has a
super-fast binary module reader written in unsafe C# code and doing a lot of
pointer arithmetic (something I learned 15+ years ago on Turbo C 2.0).
- ILASM Time. Oh, yes. PostSharp relies on ILASM. This is
true. There are excellent reasons for that: it was much cheaper to implement
than a binary writer, has decent performance on Windows, makes it easier to
debug code generators, and provides an extra layer verifying generated code.
This was an excellent choice.
Therefore, there are 3 things we could do to improve build
performance:
- Improve initialization time. That's what we did in PostSharp 2.0. I'll
explain how below.
- Parallelize PostSharp processing. Ough. That's maybe the more difficult
since all threads would access the same object model concurrently.
- Writing an binary module writer instead of MSIL. This is doable at a
much lower cost than parallelizing. But even if we write a binary writer
some time, we will never scrap the ILASM back-end.
Improvement in PostSharp 2.0: Initialization Time
With PostSharp 1.5, you had to install PostSharp and generate native images
(ngen) to get decent initialization time. Otherwise, PostSharp was continuously
JIT-compiled and your build time suffered. The problem is we can't just tell people to
install PostSharp globally: in many teams, all build tools are checked in source
control.
PostSharp 2.0 addresses this problem efficiently. First, it runs as a pipe
server. When a project needs to be built, the pipe server is started, and it is
stopped only after a long period of inactivity. So we don't have to continuously
load and unload PostSharp.
But we only won the half of the battle. Since PostSharp loads your executable
code and executes it (aspects are instantiated at build time), we need to create
a new application domain for each assembly being processed. So anyway assemblies
need to be loaded and JIT-compiled over and over again, unless... unless we host
the CLR and tell it that PostSharp assemblies, and all GAC assemblies, are
domain neutral. From this moment, they are loaded only once, and compiled only
once. Technically, hosting CLR means writing a half a dozen of COM classes in
C++ and somewhat tweaking an API that has been designed on-purpose for SQL
Server 2005, not for PostSharp.
The results: I have a test suite of 135 micro-projects I run in MSBuild
(that's part of the way you do unit testing of a build tool). The average execution time of PostSharp is the following (release
build, no ngen):
- Without Pipe Server: 1.21 s per project
- With Pipe Server: 0.94 s per project
- Difference: 0.22 s per project
So, theoretically, you should see some significant improvement if you have a
lot of small projects (to give you an idea, PostSharp still takes 10x more time
than the compiler itself).
In order to disable the Pipe Server, use the MSBuild property
PostSharpUsePipeServer=False.
Multi-Platform
PostSharp 2.0 finally solves the multi-platform problem. PostSharp 1.5 worked
fine with platform-neutral assemblies, but you had to tweak to support x86 or
x64 specific platforms. This is now addressed in PostSharp 2.0. The platform is
detected automatically, and the proper PostSharp process is started accordingly.
The same mechanism will allow a single distribution of PostSharp to target
both the CLR 2.0 and 4.0. Unfortunately -- and I will disappoint more than one
-- .NET 4.0 will not be supported in the first CTP. There are sometimes hard
choices to be done, and we preferred to deliver higher quality than more
features. Be patient! It's the next feature on the list. (I do confirm here that
.NET 4.0 will be supported in PostSharp 2.0 -- but not in its first CTP).
Diagnostic Mode
Another area of improvement: PostSharp 2.0 is more supportable than previous
versions. The installer (now a single package for both x86 and x64 platforms)
will contain two builds: release and diagnostic. Release is fast, but is not
able to trace and has no precondition checking. It's the version you use when
everything is right. When something goes wrong, build your project in diagnostic
mode: detailed logging will be available.
You can use the MSBuild property PostSharpBuild=Diag, or launch the PostSharp
Diagnostic Console:
We are working close with guys of Gibraltar Software
to make the support experience compelling. If you can't solve your problem
yourself (or if you think it's a PostSharp's bug), you can send us support
details, which we can then open using
Gibraltar
Analyst. Actually, PostSharp integrates with Gibraltar, and
Gibraltar
integrates with PostSharp. I have a long due post about that on my to-do
list.
Summary
The way PostSharp 2.0 is started is significantly different than previously
and has several advantages:
- Build time improvement
- Reliability of the assembly search algorithm
- Multi-platform
- Diagnostic mode with comprehensive logging and support for Gibraltar.
Happy PostSharping!
-gael