Note 12, Programming Language Concepts (sestoft@dina.kvl.dk) 2002-04-29 ----------------------------------------------------------------------- [These notes are pretty rough still] Partial evaluation, or automatic program specialization ------------------------------------------------------- For introductions to partial evaluation, see * Torben Mogensen and Peter Sestoft: Partial Evaluation. In Encyclopedia of Computer Science and Technology, available as http://www.it-c.dk/courses/PFOO/F2002/mogensen-sestoft.pdf * John Hatcliff: An introduction to online and offline partial evaluation using a simple flowchart language. Proceedings of the DIKU 1998 International Summerschool on Partial Evaluation. Lecture Notes in Computer Science, vol. 1706. Springer-Verlag. Available as http://www.cis.ksu.edu/~hatcliff/Papers/DIKU-PE-summerschool.ps.gz A more comprehensive, if slightly dated, reference is * Neil D. Jones, Carsten Gomard, Peter Sestoft: Partial Evaluation and Automatic Program Generation, Prentice Hall International 1993. Available from http://www.dina.kvl.dk/~sestoft/pebook/pebook.html Reflection in Java and C# ------------------------- Reflection permits a running program to inspect and manipulate the classes, methods, fields, state etc that make up the program. For instance, the program may obtain a list of all methods in a class, or all those methods whose names begin with "test", or similar. A method description mo thus obtained may then be called using a reflective method call such as mo.invoke(...). Similarly, a class description obtained this way may be used to create an object of the class. More precisely, there are two operations: Reification allows a running program to obtain information about its classes, methods, fields, constructors, etc. Reflection allows a running program to use this information to create instances of a class, call a method, access or modify a field, etc. Often those two aspects are lumped together under the name `reflection', though. The Java and C# reflection mechanisms are fairly similar: Java C# ------------------------------------------------------------------------- The class of class objects java.lang.Class System.Type Get class object for class C C.class typeof(C) Get one method in class c c.getMethod(...) c.GetMethod(...) Get all methods in class c c.getMethods() c.GetMethods() Get one field in class c c.getField(...) c.GetField(...) Get all fields in class c c.getFields() c.GetFields(...) ------------------------------------------------------------------------- Reflection API java.lang.reflect System.Reflection Method description Method MethodInfo Field description Field FieldInfo Constructor description Constructor ConstructorInfo Call method mo.invoke(...) mo.Invoke(...) Get field's value fo.get(...) fo.GetValue(...) Set field's value fo.set(...) fo.SetValue(...) Call constructor co.newInstance(...) co.Invoke(...) ------------------------------------------------------------------ The files rtcg/Reflect?.cs and rtcg/Reflect?.java provide a range of examples. Examples Reflect0.cs and Reflect0.java show that for every type (and in particular, for every class) there is an object representing that type. The object has class java.lang.Class in Java and class System.Type in C#. The examples Reflect1.cs and Reflect1.java show how the object ty corresponding to a class is used to obtain an object m representing a (public) method from the class. The object has class java.reflect.Method in Java and class System.Reflection.MethodInfo in C#. Objects representing fields and constructors can be obtained similarly. The two examples also show how the object m can be used to call the method in the class represented by ty. In general, the arguments to a method called by reflection must be passed in an array of Objects, so values of primitive type must be boxed (wrapped as objects), and an array holding the argument values must be constructed. Similarly, the result of the method is returned as an object, which must then be unboxed (unwrapped) if it is a primitive type value. The examples Reflect2.cs and Reflect2.java show one way to find methods that satisfy particular criteria. In this case static methods of a class are listed, and those methods that are static and whose names begin with Test are called. Reflective method calls and field accesses provide useful extra flexibility, as shown by the latter example. One could write a general test framework that uses reflection to call those methods in a given class C whose names begin with `Test'. This is used by e.g. the jUnit program testing framework. Reflection has some drawbacks, too: (1) types are checked only at runtime, so type safety is lost at compile-time; and (2) reflective method calls, field accesses, etc are much slower than ordinary compiled method calls, field accesses, etc. Reflective method calls are inefficient because of the required wrapping of arguments as Object arrays etc. Typically a reflective method call is slower than a static or virtual method call by a factor of 40 to 120. In C# this problem can be mitigated by turning a MethodInfo object into a delegate; calling the delegate is more than 30 times faster than the basic reflective call. Java does not seem to have a similar mechanism. What is needed is a way to turn a Method object into an object of a type that has a method of the correct compile-time type. More precisely, assume we have a Method object mo that represents a method with signature (R)(T1,...,Tn). Then we want to dynamically create an object oi whose class implements a compiled interface OI which describes a method R m(T1,...,Tn) corresponding to mo. Moreover, the creation of oi must check, once and for all, that the type R m(T1,...,Tn) for m given by OI actually matches the type described by the Method object mo, and that all access restrictions (private etc) are respected, so that this need not be checked at every invocation of oi.m(...). Also, any primitive type arguments given to oi.m(...) must be passed straight to the method underlying mo, without any boxing or unboxing operations. A so-called proxy object in the Java reflection API seems to offer a way to turn the arguments of an ordinary method call on a Proxy subinterface into a reflected method call. Runtime code generation ----------------------- Runtime code generation may be used to generate code that is specialized with respect to parameters that become available only when the program is executed. Properly used, this can lead to considerable speedup. Both Java/JVM and C#/CLR support runtime code generation, but in C#/CLR the support is official through the .NET Framework namespace System.Reflection.Emit, whereas in Java/JVM one can use third party libraries. The files rtcg/RTCG1.cs through rtcg/RTCG6.cs present several toy examples of runtime code generation. A much larger example is found in the .NET Framework implementation of regular expression matching. The source code can be studied in the Microsoft `shared source' release of .NET CLR, in file sscli/fx/src/regex/system/text/regularexpressions/regexcompiler.cs Example rtcg/RTCG1.cs generates a class corresponding to this one: public class MyClass { public static void MyMethod() { System.Console.WriteLine("MyClass.MyMethod() was called"); } } The example demonstrates all the steps needed just to do this. First we generate an assembly called "myassembly", then we build a module ~mymodule" in the assembly, then we build the class ~MyClass" in the module, and finally we build the method "MyMethod" in the class. To build the method's body, we obtain an ILGenerator for the method, that is, an Intermediate Language generator ilg. Then we call ilg.Emit(...) to add instructions to the method body. Finally, the class "MyClass" is created by the call typeBuilder.CreateType(). This method call returns a Type object representing the new class. To call the newly constructed method, we use reflection on the ty object. Example rtcg/RTCG2.cs uses the same approach to generate a class containing a slightly less trivial method: public class MyClass { public static int MyMethod(int x) { System.Console.WriteLine("MyClass.MyMethod() was called"); return x + 2; } } The newly generated method is called in two different ways. First the call MyClass.MyMethod(5) is performed using reflection on the MethodInfo object, as in example RTCG1.cs above. Secondly, a delegate mm of delegate type IntInt is created from the MethodInfo object, and then the delegate is called. One the delegate has been created, the latter is far more efficient than the former because it avoids access permission checks, boxing and unboxing of the method arguments, the boxing and unboxing of method results, etc. The example rtcg/RTCG3.cs generates three different versions of the method containing a loop: public class MyClass { public static void MyMethod(int x) { do { x--; } while (x != 0); } } The purpose of this example is to compare the efficiency of the generated code, and to compare it with an ordinary compiled C# method. The result is that the generated code is just as fast as the ordinary compiled code, except for MyMethod2, which uses a Dup instruction instead of reloading the value from the local variable. Apparently, the Dup instruction prevents the CLR just-in-time code generator from generating efficient x86 code. For MyMethod1 and MyMethod3, the MS CLR can perform 400 million iterations per second on a 850 MHz Intel P3 running Windows 2000 under VmWare 3.0 under Linux. Note how a loop is generated by defining a label, marking a position in the IL code stream with that label, and using the label in a conditional branch instruction. Example rtcg/RTCG4.cs presents a potentially more useful case for runtime code generation: a runtime specialization of the Power(n,x) function, which raises x to the n'th power. Given a value of n, the PowerGen function generates IL code for efficient computation of x to the n'th, for any value of x. Note the close structural similarity between Power and PowerGen. In fact, PowerGen is a so-called generating extension for Power, obtained by classifying those parts of Power depending only on n as static, and those parts depending also on x as dynamic. PowerGen simply executes the static parts and generates code for the dynamic parts. The code generated by PowerGen contains no loops, no tests, and no computations on n; they have been performed during code generation. Even for moderate values of n (such as 16), the specialized code is therefore faster than the general code, although it has to be called through a delegate (which is slower than an ordinary method call). Example rtcg/RTCG5.cs provides a similar example of optimized evaluation of a polynomial cs[0] + cs[1] * x + cs[2] * x^2 + ... + cs[n] * x^n for a given coefficient array cs. The method Eval(cs, x) evaluates it using Horner's rule cs[0] + x * (cs[1] + x * (... (cs[n] + 0) ...)) The method EvalGen generates, at runtime, a specialized version of Eval for a given coefficient array cs. The evaluation can be performed solely on the stack, accessing only the argument x, and pushing the coefficients as literal constants. Again the specialized code contains no loops, tests, or array indexing. On some architectures (Intel Mobile P3) the specialized code is faster even for polynomials of degree 5, but on another architecture (AMD K7) there is no difference for polynomials of degree less than 20. Example rtcg/RTCG6.cs presents a rudimentary abstract syntax for expressions in one variable, as may be found in a program to interactively draw graphs or compute zeroes of functions etc. The abstract syntax permits calls to static methods in class Math. An expression in abstract syntax (subclass of abstract class Expr) has two methods: * double Eval(double x) which computes the expression as a function of x, essentially by interpreting the abstract syntax; * void Gen(ILGenerator ilg) which generates IL code for the expression. Even for small expressions the generated code is three times faster than the general interpretive evaluation by Eval. General application areas of runtime code generation: * unrolling of recursion and loops, and inlining of constants, as in the Power example, the polynomials examples, or vector dot product computation; * removal of interpretive overhead, as in the MS .NET Framework regular expression matcher; Concrete example applications of runtime code generation: * generation of efficient code for expressions or functions entered (by a user) at runtime, for instance in a graph drawing program (RTCG6.cs); * fast raytracing algorithms for a given scene (take examples from DIKU master's theses); * the bitblt example from Keppel, Eggers, Henry 1991; * specialized sorting routines with inlining of the comparison predicates, or compiling different comparison predicates dependent on the type or order of elements being sorted; see the record comparison example in Keppel, Eggers, Henry 1991; * training neural networks of a given topology (Henrik Friborg's project) More speculative examples: * improve representation of special matrices in a matrix package: diagonal matrices, band matrices, symmetric matrices, lower or upper triangular matrices, square matrices; representation as well as algorithms (addition, multiplication, inversion, solution) could be optimized; * specialization of data representation; e.g. replacement of arrays of a given size by structs of that size? * applications in bioinformatics string or profile matching algorithms? Some literature: * David Keppel, Susan J. Eggers and Robert R. Henry: A case for runtime code generation. Technical report UWCSE 91-11-04, University of Washington Department of Computer Science and Engineering, November 1991. Available as ftp://ftp.cs.washington.edu/tr/1991/11/UW-CSE-91-11-04.PS.Z This paper and the one below requires some understanding of processor architecture. * David Keppel, Susan J. Eggers, Robert R. Henry: Evaluating runtime-compiled value-specific optimizations. Technical report UWCSE 93-11-02, University of Washington Department of Computer Science and Engineering, November 1993. Available as ftp://ftp.cs.washington.edu/tr/1993/11/UW-CSE-93-11-02.PS.Z * Package gnu.bytecode from http://www.gnu.org/software/kawa/ Bytecode generation tools developed for Kawa, a JVM-based implementation of Scheme. The tools are somewhat sparsely documented. * Bytecode Engineering Library (BCEL) from http://jakarta.apache.org/bcel/ Bytecode generation tools, formerly called JavaClass, for the JVM. Seems to be used in several projects. * Dawson R. Engler: Vcode: a retargetable, extensible, very fast dynamic code generation system. PLDI '96. Available at http://www.pdos.lcs.mit.edu/~engler/vcode-pldi.ps Vcode is a portable system to generate executable code at runtime. It generates code in approximately 10 instructions per generated instruction, and is easily extendable by clients. * Dawson R. Engler, Wilson C. Hsieh and M. Frans Kaashoek: `C: a language for high-level, fast dynamic code generation POPL 1996. Available at http://www.pdos.lcs.mit.edu/~engler/pldi-tickc.ps * Antonio Cisternino and Andrew Kennedy: Language independent program generation. Unpublished manuscript, April 2002.