Combining Generics, Pre-Compilation and Sharing Between Software-Based Processes
Total Page:16
File Type:pdf, Size:1020Kb
Combining Generics, Pre-compilation and Sharing Between Software-Based Processes Andrew Kennedy Don Syme Microsoft Research Microsoft Research Cambridge, U.K. Cambridge, U.K. [email protected] [email protected] ABSTRACT isolated processes (known as application domains) and pre- We describe problems that have arisen when combining the compilation. proposed design for generics for the Microsoft .NET Com- In this section we describe Generic IL, application do- mon Language Runtime (CLR) with two resource-related mains and pre-compilation, both by reference to the way features supported by the Microsoft CLR implementation: these features appear in the prototype CLR implementation application domains and pre-compilation. Application do- performed by the authors and also by describing the under- mains are “software based processes” and the interaction lying assumptions that are relevant to this paper. between application domains and generics stems from the 1.1 Generic IL fact that code and descriptors are generated on a per- generic-instantiation basis, and thus instantiations consume Generic IL is a set of extensions to the instruction set, resources which are preferably both shareable and recover- metadata format and semantic rules for the Common IL of able. Pre-compilation runs at install-time to reduce startup the .NET Framework. In this paper we ignore most of the overheads. This interacts with application domain unload- design details of Generic IL and consider only the following ing: compilation units may contain shareable generated in- aspects of the system: stantiations. The paper describes these interactions and the • We assume a high-level language ultimately executed different approaches that can be used to avoid or ameliorate using native code (e.g. a high-level IL compiled using the problems. JIT compilation). • We assume the language allows named generic class 1. INTRODUCTION definitions, e.g. List<T>, and the instantiation of these Parametric polymorphism, also known as “generics”, is an types in client code. important new feature of the C] programming language [12, 6] and also the .NET Common Language Runtime (CLR) [7] • We assume the language supports exact runtime type that underpins C] and other languages [9]. In previous semantics, i.e. casts and instance-of tests where cast- work, the authors presented the design and implementation ing to List<string> gives accurate results. ] of generics for C and the .NET CLR [10] — in this paper • We assume the language permits instantiations at non- we call this design Generic IL. The primary novelty of the reference types, i.e. e.g. List<int>, with the expec- design is the integration of parameterized types and poly- tation that using these types gives “natural” perfor- morphic methods into the type system of the intermediate mance, i.e. that faster code is generated for these language (IL) implemented by a virtual machine, or runtime. types, and that expensive box/unbox operations are The implementation techniques described in [10] are novel not typically needed. in many ways: they include “just-in-time” specialization of classes and code, code-sharing between distinct instantia- • We consider cases where execution can require the dy- tions of generic classes, and the efficient implementation of namic loading of components, where new components runtime types using dictionaries of type representations and may declare new instantiations of generic code. computations. This paper describes the interaction between Generic IL • We assume IL code is arranged into components called and two resource-related features supported by the Mi- assemblies, which are the unit of software installation crosoft CLR implementation: code sharing between software- and versioning. In this paper we use the words assem- bly and component interchangeably. It is possible to implement Generic IL in a number of funda- mentally different ways, and the choice affects the severity of the interactions described in this paper. However the in- teractions never disappear altogether for any of the design choices. Here we recap the primary implementation tech- niques for Generic IL: No Code Specialization (Uniform Representations) There are several variations on this technique, with a common pattern that there is a 1:1 correspondence as part of a long-running server process, or when used as a between generic code and native code, i.e. only temporary site of computation for code generated on the fly one copy of code is required for a generic type, (complex regular expressions are evaluated in this way on which is used by all instantiations. It is used in the CLR). Typically objects may not migrate between do- many implementations of parametric polymorphism, mains, but application domains do share a common garbage and also in GJ [5]. Under some variations of this collected heap. Application domains are also related to the scheme, all values which are statically of variable management of code-access security permissions, and they type are represented as heap-allocated values. The are widely used in server-side programming. advantage of this technique is that only one copy Software components (assemblies) can be loaded as of code need ever be generated, and this code can “shared” (also called domain neutral) and the native code be associated with the component which declares for these components is shared between application domains. the named generic type. However this design choice Shared components must form a strongly connected graph. has major ramifications for performance, especially Different application domains may have different sharing when generic collection classes are instantiated with policies, an additional complexity not addressed in this pa- unboxed types. Furthermore, although only one per. copy of code is needed, type descriptors may still be Each application domain has its own copy of each static required for each generic instantiation, in order to field — this is required to ensure domain isolation. This implement exact runtime type semantics. means that when shared code executes an instruction such as a “load-static-field” the results depend on the current ap- Full Code Specialization This technique generates a plication domain, and a domain local store (DLS) operation new copy of code for each specialization. A prelim- is needed, typically implemented as a table lookup based inary performance analysis of the tradeoffs vis-a-vis on an index stored in data private to the currently execut- full code sharing has been performed by Odersky et ing thread. Thus shared code incurs a performance penalty, al. [15] with the aim that this is not significant in comparison to the savings achieved by reduced memory consumption in Mixed Code Specialization/Sharing Mixed code shar- multi-application-domain scenarios. In the prototype CLR ing/specialization places instantiations in equivalence implementation modified by the authors these DLS opera- classes of compatible representations.1 For example, tions are highly optimized but are still slower than the cor- two types may be considered compatible if they are responding non-DLS actions — for example a DLS lookup identical after erasing all reference types to (“ref- ref for a static field might take 12 processor clock cycles, where erence type”), e.g. and List<string> List<Widget> the corresponding direct lookup would take 1.3 While few are compatible if both and are refer- string Widget computations are completely dominated by such accesses, ence types. Here is known as a representation ref the costs may still be incurred on dominant paths such as type. Similar techniques have been applied in other main loops. contexts [3, 19, 11]. There is a large body of work on supporting features sim- The prototype CLR implementation modified by the authors ilar to application domains in the context of other virtual uses mixed code specialization/sharing. For most of this pa- machine designs, most recently those related to Java, but per we simply assume that some kind of code specialization also many older projects such as SPIN (Modula-3) [4]. An is being used, however in Section 5 we consider why code excellent summary is provided in [2], where a huge range sharing helps to reduce the severity of the issues described of systems is described, and in particular Alta, K0 and the in this paper. J-Kernel [18]. Some entire operating systems are based on virtual machines [8]. All of these designs typically combine 1.2 Application Domains a process model with protection, resource management and Application domains are the CLR’s notion of “in-process communication. On some occasions these designs aim for process” and can be unloaded, reclaiming the memory used more stringent accounting of resource usage than is provided by code and other VM data structures (which we will col- by application domains, e.g. hard limits can be placed on lectively refer to as simply “resources”).2 Isolation between the memory usage of software processes [1], or a GC may be application domains is ensured by type safety, and in many modified to track and limit the dynamic memory used by an situations communication between application domains (e.g. application [16]. remoted calls) can be performed more efficiently than be- tween traditional operating system processes. They give 1.3 Pre-compilation large performance gains when used correctly, e.g. when used Pre-compilation is a compilation operation typically run at install-time and was designed originally to reduce startup 1This kind of code sharing is orthogonal to the main kind of resource sharing considered in this paper, i.e. resource overheads related to JIT compilation. More recently, the sharing between application domains. increasing use of CLR-based code has seen an increase in 2“Other VM data structures” is used in this paper to cover the number of virtual machine processes that are likely to the range of supporting vtables, caches, lookup tables and be running on any one machine, and pre-compilation is a thunks used in a VM implementation, especially to imple- convenient route to enable code and other pre-compiled VM ment features such as remote invocation and code access data structures to be shared between multiple VM processes, security.