In the previous post we discussed about what are native assembly images and how it can help us in improved application startup and facilitate code sharing. In this post, we are going to discuss another CLR feature provided in .net framework 4.5. The feature is called Profile Guided Optimization. The only difference is that this profiling would not be based on historical usage of the application by the user but would be based on some test scenarios. The profile optimization is about improving JIT compilation, on the other hand, profile guided optimization is improving the performance by the layout of native assemblies.
It must be remembered that this is the same optimization that is used by .net framework assemblies. Profilers have been helping the software developers with code optimization. They are used to measure various statistics for the running software including memory footprints, memory leaks, CPU and I/O usage. The feature just creates the profile data for assemblies for generation of better native binary images by NGEN [Native Image Generator].
How it works?
The workflow involving the use of MPGO is very simple. It is fed with the list of IL assemblies with the scenarios to run. It generates the updated IL images in the specified folder. The simplified workflow can be presented as follows:
The first part of this procedure happens on vendor side creating providing the software. The vendor uses [MPGO] and associates profile data with the IL assemblies. Now the assemblies are provided to a client in a deployment package. Here they can be NGENed manually during installation. They can also be queued for NGENing for later when the machine is idle to be taken care of by Native Image Service [ Or Native Image Task for Windows 8 machines]. They can also be automatically NGENed by the framework when no such specification is provided by the installation package.
MPGO can be used with Developer Command Prompt for Visual Studio 2012 Ultimate. Please make sure that you are running the command prompt with administrator privileges.
It might ask for administrator privileges if your Developer Command prompt is not running with it. You will get an access denied for not granting them.
MPGO & Application Types
It must be remembered that neither the native images nor MPGO are the silver bullets to improve application performance. They are recommended for very large applications where start-up time can be an issue. It must be remembered that they are only recommended for desktop applications. Both of these tools are not recommended for ASP.net and WCF applications because of their dynamic deployment model and significantly less importance of start-up time for those application types. MPGO is not supported for Windows Store apps.
MPGO & Application Deployment
As we have discussed MPGO requires Visual Studio 2012 Ultimate license. Our clients definitely wouldn't have this license on their machines. Actually they don't need to and this tool is also not part of .net framework. It's rather a Visual Studio tool. We can create IL assemblies optimized through MPGO. We can include these assemblies in our deployment package. While installation we can run NGEN to copy these assemblies to the native image cache for manual native images creation.
If your clients are Windows 8 machines, then these native images can also be automatically created when the application is running for future application startup performance gain.
Let's use some existing code for doing some cool stuff with [MPGO]. We can use the project from one of our previous posts. This is the project we developed while discussing Portable Class Libraries in the context of MVVM based designs. You can find the project here. You can also download the code.
We will be setting the output folder for all the projects in the solution to [././ILAssemblies]. This would create a folder on the same level as [bin] folder.
This is where all the assemblies would get copied once you build the project.
Let's create another folder where we you want the optimized image of the assemblies is copied to. The optimized assemblies would be generated by [MPGO].
Running MPGO is easy. We need to specify the required switches to help the tool. Here we are running it for the assemblies (including DLLs and EXEs) in [ILAssemblies folder). We are generating the optimized images in [OptimizedILImages] folder. [Scenario] specifies how we want to run our application including the start-up arguments. The other option is to use the profile data from some other assemblies. In that case, we don't need to run the application. This is supported through another switch [-Import]. Please remember that [-Scenario] and [-Import] are used exclusively.
When we run this, MPGO launches the application. We need to run the application through the pre-thought scenarios and close the application. As soon as we close it, MPGO has all the data to optimize the application (alternatively it also supports Timeout where it would just profile it for the specified duration and uses the profile data). So it runs our [Coffee House App] as follows:
As soon as we close the application, we have our optimized assemblies in the [OutDir] folder.
[MPGO] doesn't override the optimized assemblies in the [OutDir] folder. It just appends them with the next available integer. In this case, I have run the tool again and look at how it has appended "-1" to the name of the assemblies.
[Note] I don't want you to miss one important detail. Two of these assemblies are portable class libraries. It is really great to see that the tool supports them for generation of optimized images.
Mechanism of Optimized Assembly Generation
This should be enough to discuss about the tool. But we need to see what is happening under the hood. Let's discuss briefly what is actually happening under the cover. When we run MPGO it basically compiles our assemblies and their dependencies into native code using NGEN and copies only the native images of source assemblies in the [OutDir] folder.
We can use Process Explorer to see how MPGO is using NGEN to created native images. Remember we discussed about MSCORSVW.exe in the previous post?
You can see the details about the assemblies in registry as follows:
When compilation of these assemblies finishes, [MPGO] adds instrumentation information to those assemblies.
After instrumentation of the assemblies, MPGO launches the application as specified in [-Scenario] switch. It is launched from the source directory. It is done for assemblies individually as they are individually compiled they are picked up for instrumentation.
It then runs the application with the scenario. It waits until the application finishes or timeout expires. During this time, MPGO profiles the application and keeps the details in a temporary storage. The tool creates IBC data files in the [OutDir] folder. They are saved with [IBC] extension as follows:
As soon as the scenario finishes or timout expires, the instrumentation assemblies are uninstalled.
Since MPGO has profiled the application while running the scenario, it just copies the assemblies from the source folder and merges the profile data with the assemblies. It then removes the IBC data files. Now we have the assemblies with the profile data.
Using Optimized data from Previously optimized Assemblies
Once we have the optimized assemblies with the profile information, we don't need to run the scenario every time there is a new version of the assembly is created. We can just source the profile information from previously optimized assemblies and create newer version of the assemblies for native image generation. Let's create another image, called [ReOptimizedILImages], in the same folder as follows:
We can specify the new versions of assemblies with -assemblyList or -assemblyListFile switch. The ones to use the scenarios from could be provided with -Import switch. MPGO processes it and generates the newer versions of assemblies in -OutDir folder.
Here is how we can specify the same in Command Prompt.
Finally we have the merged assemblies in the [-OutDir] folder.
It must be remembered that if an assembly (specified in -assemblyList / -assemblyListFile) has no corresponding assembly in the assemblies containing the profile data (specified as -Import) then it would just fail for the particular assembly. The rest of the assemblies still get merged successfully. In order to see that let's just remove [CoffeeHouseApp.PortableViewModels] assembly from the [-Import] folder. Now execute the same command as above and notice the output in command prompt.
MPGO In Enterprise Eco System
In the enterprise world, MPGO might be made part of the process. As you know we first need to run scenarios. This can be done on a sample machine. Running the scenarios on an application would created assemblies with the profile data. These assemblies can be uploaded to some central location (Or checked into a source control).
Now Build servers (like TeamCity / Cruise Control) come into action. We can make this as one of the last build steps. We can use the -assemblyList from the repository assemblies from current build and provide the -Import details as the checked-in assemblies. Now when MPGO is run, it would create the merged assemblies in the specified folder. It would be tricky though in case of Build Triggers and dependencies if this build would be triggering some other builds on the build server.
[Note] If we are using Visual Studio, we can include a post-build event with MPGO command and have the merged assemblies in the specified folder.