October 2010

Volume 25 Number 10

CLR Inside Out - New Features and Improved Performance in Silverlight 4

By Andrew Pardoe | October 2010

One of the biggest changes in Silverlight 4 was moving to a new version of the CLR for the core execution engine. Every release of the .NET Framework has used the same CLR as its core, from the Microsoft .NET Framework 2.0 through the .NET Framework 3.5 SP1. The .NET Framework 4 made some changes—even some very large changes, such as factoring out the easy-to-download Client Profile and decreasing startup time by optimizing layout of native binaries—but we’ve always been restricted by the high compatibility bar imposed by being an in-place update.

With the .NET Framework 4 release, we were able to make major changes to the CLR itself, while still remaining highly compatible with previous versions. Silverlight 4 uses the new CLR as the basis for its CoreCLR and brings all of its improvements from the desktop to the Web. Some of the most notable runtime improvements are a change in the default garbage collector (GC) behavior and the fact that we no longer just-in-time (JIT) compile Silverlight Framework binaries every time a Silverlight program executes. In the base classes we’ve made improvements throughout, including enhancements to isolated storage and changes in System.IO that enable direct access to the file system from Silverlight applications running with elevated permissions.

Let’s start with a little background on how the CoreCLR GC works.

Generational GC

CoreCLR uses the same GC as the desktop CLR. It’s a generational GC, which means that its operations are based upon the heuristic that the most recently allocated objects are the most likely to be garbage at the next collection time. This heuristic is apparent in small scopes: Function locals are not program-reachable immediately after the function returns. This heuristic is generally applicable for larger scopes as well: Programs usually hold some global state in objects that live the length of the program’s execution.

Objects are usually allocated in the youngest generation—what we refer to as Generation 0—and are promoted during garbage collections (if they survive the collection) to older generations until they reach the maximum generation (Generation 2 in the current CLR GC implementation).

We have another generation in the CLR GC called the Large Object Heap (LOH). Large objects—currently defined as objects greater than 85,000 bytes—are allocated directly into the LOH. This heap is collected at the same time as Generation 2.

Without a generational GC, the GC needs to inspect the entire heap in order to know what memory is reachable and what memory is garbage before collecting the unused memory. With a generational GC we don’t need to look at the whole heap for every collection. Because the duration of a collection is directly related to the size of the generations being collected, the GC is optimized to only collect Generation 2 (and the LOH) less frequently. Collections are almost instant on small heaps and take longer as the heaps grow—Generation 0 collections can take as little as tens of microseconds.

For most programs, Generation 2 and the LOH are much larger than Generation 0 and Generation 1, so inspecting all the memory on these heaps takes longer. Keep in mind that the GC always collects Generation 0 when it collects Generation 1, and collects all of the heaps when it collects Generation 2. This is why a Generation 2 collection is called a full collection. For more details on the performance of the different heap collections, see the October 2009 CLR Inside Out column at msdn.microsoft.com/magazine/ee309515.

Concurrent GC

The straightforward algorithm to perform a garbage collection is to have the execution engine pause all program threads while the GC does its work. We refer to this kind of a collection as a blocking collection. These allow the GC to move non-pinned memory around—for example, to move it from one generation to the next or to compact sparse memory segments—without the program knowing that anything has changed. If memory were to move while program threads are executing, it would look to the program like memory had been corrupted.

But some of the work of a garbage collection doesn’t change memory. Since the first version of the CLR, we’ve provided a GC mode that does concurrent collections. These are collections that do most of the work of a full GC without having to pause program threads for the whole duration of that collection.

There are a number of things the GC can do without changing any state that’s visible to the program, for example, the GC can find all program-reachable memory. Program threads can continue to execute while the GC inspects the heaps. Before doing the actual collection, the GC just needs to discover what has changed while it was inspecting memory—for example, if the program allocated a new object, it needs to be marked as reachable. At the end of this, the GC asks the execution engine to block all threads—just as in a non-concurrent GC—and proceeds to finish all the reachable memory at that point.

Background GC

Concurrent GC has always provided a great experience in most scenarios, but there’s one scenario that we improved greatly. Remember that memory is allocated in the youngest generation or on the LOH. Generations 0 and 1 are located on a single segment—we call it the ephemeral segment because it holds short-lived objects. When the ephemeral segment fills up, the program can no longer create new objects because there’s no room for them on the ephemeral segment. The GC needs to do a collection on the ephemeral segment to free some space and allow allocation to continue.

The problem with concurrent GC is that it can’t do either of these things while a concurrent collection is taking place. The GC thread can’t move any memory around while program threads are running (so it can’t promote older objects to Generation 2) and because there’s already a GC in progress it can’t start an ephemeral collection. But the GC needs to free some memory on the ephemeral segment before the program can continue. It’s pretty much stuck; the program threads have to be paused, not because concurrent GC is changing program-visible state, but because the program can’t allocate. If a concurrent collection finds that the ephemeral segment is full once it’s found all reachable memory, it will pause all threads and do a blocking compaction.

This problem explains the motivation behind the development of background GC. It works like the concurrent GC in that the GC always does most of the work of a full collection on its own thread in the background. The main difference is that it allows an ephemeral collection to take place while the full collection gathers data. This means programs can continue to execute when the ephemeral segment fills. The GC just does an ephemeral collection and everything goes on as expected.

The impact of background GC on program latency is significant. When running the background GC, we observed far fewer pauses in program execution and those that remained were shorter in duration.

Background GC is the default mode for Silverlight 4 and is only enabled on Windows platforms because OS X lacks some of the OS support the GC needs to run in background or concurrent mode.

NGen Performance Improvements

The compilers for managed languages such as C# and Visual Basic don’t directly produce code that can be executed on the user’s machine. These compilers produce an intermediate language called MSIL that’scompiled to executable code at program execution using a JIT compiler.

Using MSIL has a lot of benefits, ranging from security to portability, but there are two tradeoffs with JIT-compiled code. First, a lot of .NET Framework code has to be compiled before your program’s Main function can be compiled and executed. This means your user has to wait for the JIT before the program starts running. Second, any .NET Framework code that gets used has to be compiled for every Silverlight program executing on the user’s machine.

NGen helps with both of these issues. NGen compiles the .NET Framework code at install time so that it’s already compiled when your program starts executing. Code that’s been compiled with NGen can often be shared across multiple programs so the working set on the user’s machine is reduced when running two or more Silverlight programs. If you want to know more about how NGen improves startup time and working set, see the May 2006 CLR Inside Out column at msdn.microsoft.com/magazine/cc163610.

Code from the .NET Framework makes up a large portion of Silverlight programs, so not having NGen available in Silverlight 2 and 3 made a noticeable difference in startup time. The JIT compiler was taking far too long to optimize and compile the library code on the startup path of every program.

Our solution to this problem was to not have the JIT compileroptimize code generation in Silverlight 2 and 3. The code still needed to be compiled, but because the JIT compiler was producing simple code, it didn’t take very long to compile. Compared to traditional desktop applications, most programs written for rich Internet application Web scenarios are small and don’t run for very long. Even more importantly, they’re usually interactive programs, meaning they spend most of their time waiting for the user’s input. In the scenarios targeted by Silverlight 2 and 3, having quick startup time was far more important than generating optimized code.

As Silverlight Web apps have evolved, we’ve made changes to keep the experience positive. For example, we added support for installing and running Silverlight applications from the desktop in Silverlight 3. Normally, these applications are larger and do more work than the small, interactive applications found in the classic Web scenario. Silverlight itself has added a lot of computationally intensive functionality, such as support for touch input under Windows 7 and rich photo manipulation as you see on the Bing Maps Web site. All of these scenarios require that the code be optimized to perform efficiently.

Silverlight 4 gives you startup performance and optimized code. The JIT compiler now uses the same optimizations in Silverlight as it does for desktop .NET applications. We’re able to do the optimizations because we’ve enabled NGen for the Silverlight .NET Framework assemblies. When you install Silverlight, we automatically compile all of the managed code that comes in the Silverlight runtime and save it on your hard disk. When a user executes your Silverlight program, it starts executing without having to wait for any of the Framework code to compile. Just as importantly, we now optimize the code in your Silverlight program so that your programs run faster, and we can share the Framework code between multiple Silverlight programs executing on the user’s machine.

Silverlight 4 creates native images for the .NET Framework assemblies during installation. There are some applications where startup performance is the only performance that matters. Think of Notepad as an example: it’s important that it starts quickly, but once you start typing it doesn’t matter how fast Notepad runs (provided it runs faster than you type). For this class of programs, the time it takes to JIT compile the application startup code may cause a performance decrease. Most applications will start up 400 ms to 700 ms more quickly in Silverlight 4 and will see up to a 60 percent performance improvement during execution.

The Base Class Library (BCL) is at the core of the managed APIs that are now supported by NGen in Silverlight 4. Let’s take a look at what’s new in the BCL.

New BCL Functionality

Many of the new BCL enhancements in Silverlight 4 are also new in the .NET Framework 4 and have already been written about in that context. I’ll give you a brief overview of what we’ve included in Silverlight 4.

Code contracts provide a built-in way to express pre-conditions, post-conditions and object invariants in your Silverlight code. Code contracts can be used to better express assumptions in your code and can help find bugs early. There are many additional benefits to using code contracts. You can learn more in Melitta Andersen’s August 2009 CLR Inside Out column at msdn.microsoft.com/magazine/ee236408, on the Code Contracts DevLabs site at msdn.microsoft.com/devlabs/dd491992, and on the BCL team blog at blogs.msdn.com/bclteam.

Tuples are most often used to return multiple values from a method. They’re often used in functional languages such as F# and dynamic languages like IronPython, but are just as easy to use from Visual Basic and C#. You can learn more about the design of tuples in Matt Ellis’ July 2009 CLR Inside Out column at msdn.microsoft.com/magazine/dd942829.

Lazy<T> provides an easy way to lazily initialize objects. Lazy initialization is a technique that applications can use to defer loading or initializing data until it’s first needed.

The new BigInteger and Complex numeric data types are available in the Silverlight 4 SDK in System.Numerics.dll. BigInteger represents an arbitrary-precision integer and Complex represents a complex number with real and imaginary components.

Enum, Guid and Version now support TryParse like many of the other BCL data types, providing a more efficient way to create an instance from a string that doesn’t throw exceptions on errors.

Enum.HasFlag is a new convenience method that can be used to easily check whether or not a flag is set on a Flags enum, without having to remember how to use the bitwise operators.

String.IsNullOrWhiteSpace is a convenience method that checks whether or not a string is null, empty or contains only white space.

String.Concat and Join overloads now take an IEnumerable<T> parameter. These new overloads to String.Concat and Join enable concatenating any collection that implements IEnumerable<T> without the need to first convert the collection to an array.

Stream.CopyTo makes it easy to read from one stream and write the contents to another stream in one line of code.

In addition to these new features, we’ve also made some enhancements to isolated storage and enabled trusted Silverlight applications to directly access parts of the file system through System.IO.

Isolated Storage Enhancements

Isolated storage is a virtual file system that Silverlight applications can use to store data on the client. To learn more about isolated storage in Silverlight, refer to the March 2009 CLR Inside Out column at msdn.microsoft.com/magazine/dd458794.

The most notable improvement to isolated storage in Silverlight 4 is in the area of performance. Since the release of Silverlight 2, we’ve received a lot of feedback from developers regarding the performance of isolated storage. In Silverlight 3, we made some changes that significantly improved the performance of reading data out of isolated storage. In Silverlight 4, we’ve gone a step further and addressed the performance bottlenecks that developers were seeing when writing data to isolated storage. Overall, the performance of isolated storage is much improved in Silverlight 4.

We also heard from developers that there was no easy way to rename or copy files within isolated storage. In order to rename a file, you had to manually read from the original file, create and write to a new file, and then delete the original file. Renaming a directory could be accomplished in a similar manner, but requires even more lines of code, especially when the directory you want to rename contains subdirectories. This works, but is more code than you should have to write and isn’t as efficient as telling the OS to simply rename the file or directory on disk.

In Silverlight 4, we’ve added new methods to the IsolatedStorageFile class that you can call to efficiently perform these operations in single line of code: CopyFile, MoveFile and MoveDirectory. We also added new methods that provide additional information about the files and directories within isolated storage: GetCreationTime, GetLastAccessTime and GetLastWriteTime.

Another new API we added in Silverlight 4 is IsolatedStorageFile.IsEnabled. Previously, the only way to determine whether isolated storage was enabled was to try using it and then catching the subsequent IsolatedStorageException, which is thrown if isolated storage is disabled. The new static IsEnabled property can be used to more easily determine whether or not isolated storage is enabled.

Many browsers such as Internet Explorer, Firefox, Chrome and Safari now support a private browsing mode where browsing history, cookies and other data aren’t persisted. Silverlight 4 respects private browsing settings, preventing apps from accessing isolated storage and storing information on your local machine when the browser is in private mode. In such circumstances, the IsEnabled property will return false and any attempt to use isolated storage will result in an IsolatedStorageException, the same behavior as if isolated storage had been explicitly disabled by the user.

File System Access

Silverlight applications run in a partial-trust security sandbox. The security sandbox restricts access to the local machine and places a number of constraints on the application, preventing malicious code from causing harm. For example, partial-trust Silverlight applications cannot directly access the file system. If an app needs to store data on the client, its only option is to store data within isolated storage. Access to the broader file system can only be accomplished through the OpenFileDialog or SaveFileDialog.

Silverlight 3 added the ability to install and run apps out-of-browser. This enables some interesting offline scenarios, but such apps still run within the same sandbox as apps running inside the browser. Silverlight 4 allows out-of-browser applications to configure themselves to run in elevated trust. Such trusted applications are able to bypass some of the restrictions of the sandbox after installation. For example, trusted applications can access user files, use networking without cross-domain access restrictions, bypass user consent and initiation requirements, and access native OS functionality.

When a user installs an application that requires elevated trust, the normal installation prompt is replaced with a warning that tells users that the application can access user data and should only be installed from trusted Web sites.

Trusted applications can use the APIs in System.IO to directly access the following user directories on the file system: MyDocuments, MyMusic, MyPictures and MyVideos. File operations outside of these directories are currently not allowed and will result in a SecurityException. Within these directories, all file operations are allowed, including reading and writing. For example, a trusted photo album application can directly access all files within the MyPictures directory. A trusted video-editing app can save a movie to the MyVideos directory.

It’s important not to hardcode file system paths to these directories in your applications as the paths will be different depending on the underlying OS. File system paths are absolutely different between Windows and Mac OS X, but paths can also be different between versions of Windows. To work correctly across all platforms, System.Environment.GetFolderPath should be used to get the file system paths for these directories. The following code uses Environment.GetFolderPath to get the file system path to the MyPictures directory, finds all files within MyPictures (and subdirectories) that end with .jpg using the System.Directory.EnumerateFiles method, and adds each file path to a ListBox:

if (Application.Current.HasElevatedPermissions) {
  string myPictures = Environment.GetFolderPath(
    Environment.SpecialFolder.MyPictures);
  IEnumerable<string> files = 
    Directory.EnumerateFiles(myPictures, "*.jpg", 
    SearchOption.AllDirectories);
  foreach (string file in files) {
    listBox1.Items.Add(file);
  }
}

This code shows how to create a text file in the user’s MyDocuments directory from a trusted application:

if (Application.Current.HasElevatedPermissions) {
  string myDocuments = Environment.GetFolderPath(
    Environment.SpecialFolder.MyDocuments);
  string filename = "hello.txt";
  string file = Path.Combine(myDocuments, filename);
  try {
    File.WriteAllText(file, "Hello World!");
  }
  catch {
    MessageBox.Show("An error occurred.");
  }
}

System.IO.Path.Combine is used to combine the path to MyDocuments with the name of the file, which will insert the appropriate directory separator character for the underlying platform between the two (Windows uses \, while Mac uses /). File.WriteAllText is used to create the file (or overwrite it if it already exists) and write the text “Hello World!” to the file.

Better Performance and More Capabilities

As you’ve seen, the new CLR in Silverlight 4 includes improvements in both the runtime and base classes. The new GC behavior, the fact that we now NGen the Silverlight Framework assemblies, and the isolated storage performance improvements mean your apps will start faster and run better on Silverlight 4. Enhancements to the BCL enable apps to do more with less code, and new capabilities, such as the ability for trusted applications to access the file system, facilitate compelling new app scenarios.


Andrew Pardoe is a program manager for CLR at Microsoft. He works on many aspects of the execution engine for both the desktop and Silverlight runtimes. He can be reached at andrew.pardoe@microsoft.com.

Justin Van Patten is a program manager on the CLR team at Microsoft, where he works on the Base Class Libraries. You can reach him via the BCL team blog at https://blogs.msdn.com/b/bclteam/.

Thanks to the following technical experts for reviewing this article: Surupa Biswas, Vance Morrison and Maoni Stephens