Mono 2.4 Components
Mono 2.4 consists of three groups of components:
- Core components
- Mono/Linux/GNOME development stack
- Microsoft compatibility stacks
The core components include the C# compiler, the virtual machine, and the base class libraries. These components are based on the Ecma-334 and Ecma-335 standards, allowing Mono to provide a standards-compliant, free and open source CLI virtual machine.
The Mono/Linux/GNOME development stack provides tools for application development, while leveraging existing GNOME and free and open source libraries such as:
- Gtk# for GUI development
- Mozilla libraries for working with the Gecko rendering engine
- UNIX integration libraries
- Database connectivity libraries
- A security stack
- The XML schema language RelaxNG
Gtk# allows Mono applications to integrate into the GNOME desktop as native applications. The database libraries provide connectivity to MySQL, SQLite, PostgreSQL, Firebird, Open Database Connectivity (ODBC), Microsoft SQL Server (MSSQL), Oracle, the object-relational database db4o, and many others. The Mono project tracks developing database components at its web site.
The Microsoft compatibility stack provides a pathway for porting Windows .NET applications to Linux. This group of components includes ADO.NET, ASP.NET, and Windows Forms, among others. As these components are not covered by ECMA standards, some of them remain subject to patent protection.
The Mono C# compiler (MCS) is considered feature complete for C# versions 1.0, 2.0, and 3.0. The compiler is able to compile itself and many more C# programs. It is routinely used to compile Monoroughly four million lines of C# codeand a few other projects. The compiler is also fairly fast. On an IBM ThinkPad t40, it compiles 18,000 lines of C# code per second. You can also use the compiler as a service by using the Mono.CSharp.Evaluator class in the Mono.Sharp.dll
assembly. Starting with Mono version 2.6, a new compiler dmcs
will be available as a preview of C# 4.0 (it is a preview because Mono 2.6 will ship before C# 4.0 is finalized).
The compiler performs a number of simple optimizations on its input, including constant folding (which is required by the C# language spec) and dead-code elimination. Other more interesting optimizations, such as hoisting, are not possible yet because the compiler output at this point does not generate an intermediate representation that is suitable to perform basic block computation.
Adding an intermediate layer to enable basic block computation to the compiler should be a simple task, but the Mono team is considering having a generic CIL optimizer. Because all the information required to perform basic block-based optimizations is available at the CIL level, the Mono team might just skip this step altogether and have just a generic CIL optimizer that would perform hoisting on arbitrary CIL programs, not only those produced by MCS.
If this generic IL optimizer is further expanded to perform constant folding (not needed for the MCS, as it is already in there) and dead-code elimination, other compiler authors might be able to use this tool in their projects, reducing their time to develop a production compiler.
The Mono Runtime
The Mono runtime engine provides a JIT, an Ahead-of-Time compiler (AOT), a library loader, a garbage collector (the Boehm conservative garbage collector), a threading system, and interoperability functionality. You can use the Mono runtime as a stand-alone process, or it can be embedded into applications.
Embedding the Mono runtime allows applications to be extended in C# while reusing all of the existing C and C++ code.
The Mono developers have re-written the JIT compiler to support a number of features that were missing: AOT
compilation, simple porting, and a solid foundation for compiler optimizations. The idea of AOT compilation is to allow developers to pre-compile their code to native code to reduce startup time, and to compile the working set that is used at runtime in the just-in-time compiler. When an assembly (a Mono/.NET executable) is installed in the system, it is then possible to pre-compile the code, and have the JIT compiler tune the generated code to the particular CPU on which the software is installed. In the .NET world, you do this with a tool called ngen.exe
The code produced by Mono's AOT compiler is Position Independent Code (PIC), which tends to be a bit slower than regular JITed code, but what you lose in performance you gain by being able to use all the available optimizations.
Mono also offers bundles. Bundles merge your application, the libraries it uses, and the Mono runtime into a single executable image. You can think of bundles as "statically linking Mono" into your application.
Platform for Code Optimizations
Beyond the use of the Mono VM as JIT compiler, the Mono team needs to make Mono code generation as efficient as possible. Such a design called for a good architecture that would enable various levels of optimizations; some optimizations are better performed on high-level intermediate representations, some on medium-level, and some at low-level.
Also, you should be able to turn these on or off conditionally. Some optimizations are too expensive to be used in JIT compilation scenarios, but they can be turned on for AOT compilations or when using profile-guided optimizations on a subset of the executed methods.
Mono today uses Boehm's GC as its garbage collection engine. The Mono development team is also working on a precise and compacting GC engine specific to Mono. The GC interface is being isolated to allow for more than one GC engine to be used or for the GC to be tuned for specific tasks.
The GC scans a few areas for pointers to managed objects:
- The heap (where other managed objects are allocated) The heap is currently handled in a mostly precise mode. The GC almost always will consider only memory words that contain only references to the heap, so there is very little chance of pointer misidentification and hence memory retention. The new GC requires a fully precise mode to improve things marginally. The details about mostly precise have to do with large objects with sparse bitmaps of references and the handling of multiple AppDomains safely.
- Thread stacks and registers These are always scanned conservatively. This will be true for the new GC too (at least for the initial versions, where the mono team will have to track the bugs that a moving generational GC will expose). Later, they will conservatively scan only the unmanaged parts of the stacks.
- The static data area The Mono team already optimized this both with Boehm and the current GC to work in precise mode.
- Data structures allocated by the runtime The Mono team already optimized this to work in mostly precise mode (i.e., some data structures are dealt with precisely, others not yet). The team needs to do more work in this area, especially for the new GC, where having pinned objects can be a significant source of pain.
The Mono team is developing a new generational, precise, and compacting GC, which is currently available from SVN releases of Mono. This new compacting GC was implemented to work around some of the limitations in a purely conservative collector, specifically the memory consumption caused by heap fragmentation.
Although this GC is currently available on SVN, it is not a supported configuration and will not be a supported configuration for the Mono 2.4 release. It is available for developers who might be interested in testing their applications or who might want to work on it.
The implementation details are available on the Mono Compacting GC page.
The JIT engine implements a number of optimizations:
- Opcode cost estimates (the architecture allows the Mono team to generate different code paths dynamically depending on the target CPU).
- Constant folding, copy propagation, and dead-code elimination (Although compilers typically do constant folding, the combination of inlining with constant folding produces some very good results.)
- Linear scan register allocation
- SSA-based framework (Various optimizations are implemented on top of this framework.)
The Mono team continues to improve its engine, but many optimizations can not be effectively done by the runtime. You will get very good results if you profile and study your application patterns. Read the Mono performance tips article for various ideas on how to tune your software.