In Building NETCF for Windows Phone 7 series we put in couple of features to enhance startup performance and ensure that the working set remains small. One of these features added is code/data sharing.
Native applications have inherent sharing where multiple processes can share the same executable code. However, in case of managed code running on NETCF 3.5 even when multiple applications use the same assembly, the managed code in them are JITed in context of each process separately. This results in suboptimal resource utilization because of the following
- Repeated JITing of the same code
- Memory overhead of having identical copies of the same JITed code for every process running them. The execution engine also maintains records of the various types loaded and even though two (or more) processes may be loading the same types from the same assemblies; per process copies are maintained for them.
Together the overhead can be significant.
In the latest version of the runtime shipping with Windows Phone 7 (.NET Compact Framework 3.7) we added a feature to share both the JITed code and these type information across multiple managed processes.
The runtime essentially uses a client server architecture. We added a new kernel mode server
which is responsible of maintaining a system wide shared heap. The server on receiving requests
from the various client processes (each managed app is a client) JITs code and type information
into this shared heap. The shared heap is accessed Read-only from all the client apps and
Read/write from the server.
When a process requests for the same type to be loaded or code to be JITed it just re-uses the already loaded type info or JITed code from the shared heap.
What is shared
- Various Execution engine data-structures like loader type-information
- JITed code from a predefined list of platform assemblies (user code is not shared).
Do note that user code is not shared and neither is all platform code. Sharing arbitrarily would actually increase cost if they ultimately didn’t get reused in a lot of applications. The list of assemblies/data shared was carefully chosen and tuned to ensure only those are shared that provided the maximum performance benefit.
Sharing provides the following advantages
- Warm startup time is significantly reduced. When a new application is coming up a large
percentage of the initial code and type-info is already found on the shared heap and hence
there is significant perf boost. The exact number depends on the applications but it’s common
to get around 30% gains
- Significant reduction in net working set as a lot of commonly used platform assembly JITed code is re-used
- Obviously there are other goodness like less JITing means less processing on the device
Like user code the system is also capable of pitching the shared code when there is a low memory situation on the device.