Miguel blogged yesterday about my committing a piece of code enabling sharing of methods of generic classes. His post gives the impression as if this was already fully implemented now, but it is actually just the basis for what’s to come.
Previous to this change Mono always compiled every instantiation of a method of a generic class separately. We’re trying to get to the point where all instantiations for reference types share the same native code. My first implementation only shares a method between instantiations if the method’s code doesn’t refer to the type argument of the class. As an example, let’s take this (nonsensical) class:
public class GenA<T> {
static T[] arr;
public static GenA () {
arr = new T [3];
}
public GenA () {}
public GenA<T> newGen () {
return new GenA<T> ();
}
public GenA<int> newGenInt () {
return new GenA<int> ();
}
public T[] getArr () {
return arr;
}
public T[] newArr () {
return new T [3];
}
public int hash (T obj) {
return obj.GetHashCode ();
}
public T ident (T obj) {
return obj;
}
public T cast (Object obj) {
return (T)obj;
}
}
What will be shared here are the constructor (not the static constructor, though), newGenInt, hash and ident. The static constructor and getArr will not be shared because they refer to a static member which is currently translated with the address hard-coded. Since each generic instantiation of Gen has its own static members a shared method will not work. newGen will not be shared because it depends on the type argument T to create the array. Neither will cast because it needs to know T to decide whether the cast is valid.
Right now generics sharing is still disabled by default, but can be turned on with -O=gshared. If you’re using Mono SVN and are working with generics code, then it’d be nice if you could try it out and report any problems you encounter.
So what kind of benefits are we looking at here once the whole thing is finished? I assume there’ll be a slight CPU usage and memory usage reduction in generics heavy code. Do ya have any rough numbers?
No numbers yet.
The main motivation for this is reducing memory usage. Performance improvements can come on two fronts: First, we have to do less compiling, and second we’ll have better cache behaviour.
There are performance downsides, too, though. Some operations, like access to static fields, object creation or casting, will take slightly more work compared to what we have now (only in generic code, of course). What we’re doing now, essentially, is specializing the method for each type argument, which does of course generate more efficient code.
Maybe we’ll provide command line options to only permit sharing when the generated code isn’t less efficient. It really shouldn’t be a big deal, though, so have no fear.