When the C and C++ programming languages were invented, computers were slow, memory was limited, and compilers were simple and memory challenged, so a practical language could be little more than a veneer for assembly language. Times change, though, and so do the limits on software development. Today, program performance is seldom limited by raw processor speed. Instead, latency—memory, disk, network, database—often determines performance. Moreover, software dependability—an amalgam of reliability, availability, safety, and security—has become a challenge that often dominates software development.
Safe programming languages can increase dependability by preventing (or, at least detecting) many common programming errors. Safety has two parts: type and memory. Type safety means that a program can't treat an object of type A as if it was of an unrelated type (unchecked casts in C/C++ offer this "feature"). Memory safety means that a program can't reference beyond the bounds of an object or construct a pointer to an object, for example, by casting some random integer to a pointer.
No doubt you are thinking: "yeah, yeah, I've heard all that before, but safe languages are too slow or hog too much memory for my application." In fact, your problem may have more to do with the implementation of your programming language or operating system than with language safety.
To explore these issues, our team in Microsoft Research built a new system to study trade-offs in the pervasive use of safe languages and to demonstrate that they need not incur large performance penalties. Singularity is a new operating system, written almost entirely in C#, which executes only verifiably safe programs. Singularity's kernel and runtime libraries are the only parts of the system containing unsafe code and, even in those sections, most code is written in safe C#.
Singularity achieves good performance by reinventing the environment in which code executes. In existing systems, safe code is an exotic newcomer who lives in a huge, luxurious home in an elegant, gated community with its own collection of services. Singularity, in contrast, has architected a single world in which everyone can be safe, with performance comparable to the unsafe world of existing systems.
A key starting point is Singularity processes, which start empty and add features only as required. Modern language runtimes come with huge libraries and expressive, dynamic language features such as reflection. This richness comes at a price. Features such as code access security or reflection incur massive overhead, even when never used.
A Singularity application specifies which libraries it needs, and the Bartok compiler brings together the code and eliminates unneeded functionality through a process called "tree shaking," which deletes unused classes, methods, and even fields. As a result, a simple C# "Hello World" process in Singularity requires less memory than the equivalent C/C++ program running on most UNIX or Windows® systems. Moreover, Bartok translates from Microsoft® intermediate language (MSIL) into highly optimized x86 code. It performs interprocedural optimization to eliminate redundant run-time safety tests, reducing the cost of language safety.
Aggressive interprocedural optimization is possible because Singularity processes are closed—they do not permit code loading after the process starts executing. This is a dramatic change, since dynamic code loading is a popular, but problematic, mechanism for loading plug-ins. Giving plug-ins access to a program's internals presents serious security and reliability problems (did you know that 85 percent of blue screens in Windows are caused by third-party plug-ins and device drivers?). Dynamic loading frustrates program analysis in compilers or defect-detection tools, which can't see all code that might execute. To be safe, the analysis must be conservative, which precludes many optimizations and dulls the accuracy of defect detection.
Plug-ins in Singularity execute in their own processes and communicate with carefully verified communication channels. Plug-ins can fail without killing the other. This architecture is practical because Singularity processes are inexpensive to create and communicate between since they rely on language safety, not virtual memory hardware, to enforce isolation.
Rethinking the environment in which a program executes can dramatically reduce the penalty of modern, safe programming languages. Singularity is reinventing systems from the hardware up so programmers no longer need to sacrifice safety for speed.
Singularity Revisited, Channel 9 video and blog, Redmond, WA, December 13, 2005.
Singularity: A research OS written in C#, Channel 9 video and blog, Redmond, WA, August 23, 2005.