GENERICSSasha Goldshteinblog.sashag.net | @goldshtn
What Is This Talk?• Understanding how generics are implemented in  C++, Java and .NET at the runtime and machine-code  lev...
Why Do We Want Them?• “Pure” object-oriented programming does not always  provide a clean and type-safe solution with good...
The C++ Approach         “Templates, the smart macros from hell.”• Use parameterized template as a sketch• No constraints ...
C++ Template Definitiontemplate <typename T>class vector {  T* data; int size; int cap;public:  vector(int capacity) { ......
C++ Template InstantiationYou say:vector<int> v(2);Compiler says:class __vector__int__ {  int* data; int size; int cap;pub...
C++ Template InstantiationYou say:vector<int> v(2);v.push_back(42);Compiler says:class __vector__int__ {  int* data; int s...
C++ Template InstantiationYou say:vector<EmptyClass> v(2);sort(v.begin(), v.end());Compiler says:error C2784: bool std::op...
The C++ Approach—Pros and Cons          Pros                       Cons• No performance cost      • Can’t share templates•...
The Java Approach• Use parameterized template as a compiler aid• Constraints used to prove things to the compiler• Erase t...
Java Generic Type ErasureThere is just one type (raw type) at runtime:public class LinkedList {  private LinkedList head; ...
Java Generic Type ConstraintsCannot use anything but java.lang.Object methodswithout specifying constraint (wildcard):publ...
The Java Approach—Pros and Cons         Pros                       Cons• Backwards compatible    • Can’t use generics with...
The .NET Approach• Use parameterized template as a compiler aid and a  runtime code generation sketch for the JIT• Constra...
Digression: .NET Object Layout
.NET Generic Types at Runtime• There is a separate type at runtime for each generic  instantiation, but not necessarily a ...
.NET Generic Code Sharing
Concrete Example: Stack PushBasicStack`1[[System.__Canon, mscorlib]].Push(System.__Canon)00260360 57              push    ...
Concrete Example: Stack PushBasicStack`1[[System.Int32, mscorlib]].Push(Int32)002603c0 57              push    edi002603c1...
Concrete Example: Stack PushBasicStack`1[[System.Double, mscorlib]].Push(Double)00260420 56              push    esi002604...
Type-Specific Code• What about new T[12] or typeof(T).FullName?• When .NET generic methods need access to T, they get it f...
Generics and Reflection• Because generic types are first-class citizens, they are accessible to Reflection at runtime  Typ...
Generic Constraints• .NET constraints restrict type parameters at compile-  time, very similar to Java’s• Only a limited s...
Case Study: IEquatable<T>     public static void CallEquals<T>(T inst) {       inst.Equals(inst);     }public struct Point...
Case Study: IEquatable<T>• CallEquals has no constraints, so the C# compiler  chooses the Object.Equals(Object) virtual me...
Sorting “If Possible”, a la C++public class List<T> {  T[] items; ...  public void Add(T item) { ... }  public void Sort(S...
Sorting “If Possible”, a la C++public abstract class SortProvider<T> {  public abstract void Sort(T[] items);  public stat...
Getting Generic Math Right in .NET• Pretty nasty:• Consider Complex<T>: you can’t implement operators…• Solution sketch:  ...
The .NET Approach—Pros and Cons           Pros                          Cons• Constraint violation        • Constraints ar...
QUESTIONS?Sasha Goldshteinblog.sashag.net | @goldshtn
Upcoming SlideShare
Loading in...5
×

Generics in .NET, C++ and Java

4,691

Published on

Implementation details and performance traits of generics in .NET, Java and C++. Presentation for the Jerusalem .NET/C++ User Group by Sasha Goldshtein.

Published in: Technology, Business
0 Comments
0 Likes
Statistics
Notes
  • Be the first to comment

  • Be the first to like this

No Downloads
Views
Total Views
4,691
On Slideshare
0
From Embeds
0
Number of Embeds
3
Actions
Shares
0
Downloads
34
Comments
0
Likes
0
Embeds 0
No embeds

No notes for slide

Generics in .NET, C++ and Java

  1. 1. GENERICSSasha Goldshteinblog.sashag.net | @goldshtn
  2. 2. What Is This Talk?• Understanding how generics are implemented in C++, Java and .NET at the runtime and machine-code level• Understanding the performance implications and other pros/cons of each mechanism• We will not learn how to use generics
  3. 3. Why Do We Want Them?• “Pure” object-oriented programming does not always provide a clean and type-safe solution with good performance• In other words, what’s wrong here? public class ArrayList { object[] items; public void Add(object item) { ... } public object ElementAt(int index) { ... } }
  4. 4. The C++ Approach “Templates, the smart macros from hell.”• Use parameterized template as a sketch• No constraints on the original template code• Everything happens at compile-time template <typename RanIt> void sort(RanIt begin, RanIt end) { … if (*begin < *(begin+1)) … }
  5. 5. C++ Template Definitiontemplate <typename T>class vector { T* data; int size; int cap;public: vector(int capacity) { ... } void push_back(const T& datum) { ... } T operator[](int index) const { ... }};
  6. 6. C++ Template InstantiationYou say:vector<int> v(2);Compiler says:class __vector__int__ { int* data; int size; int cap;public: vector(int capacity) { ... }};
  7. 7. C++ Template InstantiationYou say:vector<int> v(2);v.push_back(42);Compiler says:class __vector__int__ { int* data; int size; int cap;public: vector(int capacity) { ... } void push_back(const int& datum) { ... }};
  8. 8. C++ Template InstantiationYou say:vector<EmptyClass> v(2);sort(v.begin(), v.end());Compiler says:error C2784: bool std::operator <(const std::vector<_Ty,_Alloc> &,conststd::vector<_Ty,_Alloc> &) : could not deduce template argument for conststd::vector<_Ty,_Alloc> & from EmptyClass vector(1724) : see declaration of std::operator < templatesstuff.cpp(20) : see reference to function template instantiation void sort<std::_Vector_iterator<_Myvec>>(RanIt,RanIt) being compiled with [ _Myvec=std::_Vector_val<std::_Simple_types<EmptyClass>>, RanIt=std::_Vector_iterator<std::_Vector_val< std::_Simple_types<EmptyClass>>> ]
  9. 9. The C++ Approach—Pros and Cons Pros Cons• No performance cost • Can’t share templates• Very flexible between translation• Full compile-time type units safety • Can’t share templates between libraries (code bloat) • Can’t reliably export templates from libraries • No constraints = no readable compiler errors
  10. 10. The Java Approach• Use parameterized template as a compiler aid• Constraints used to prove things to the compiler• Erase type information at runtime public class LinkedList<E> { private LinkedList<E> head; private E value; public void add(E element) { ... } public E getAt(int index) { ... } }
  11. 11. Java Generic Type ErasureThere is just one type (raw type) at runtime:public class LinkedList { private LinkedList head; private Object value; public void add(Object element) { ... } public Object getAt(int index) { ... }}
  12. 12. Java Generic Type ConstraintsCannot use anything but java.lang.Object methodswithout specifying constraint (wildcard):public class SortedList<E extends Comparable<E>> { ... public void add(E element) { ... if (element.compareTo(other)) ... }}
  13. 13. The Java Approach—Pros and Cons Pros Cons• Backwards compatible • Can’t use generics with with non-generic Java primitive types versions • Can’t distinguish• Constraint violation between generic class results in clear instantiations compiler error • Can’t instantiate• Can share generic generic type types and objects parameters (“new E”) between • Can’t use type packages/applications parameters in static methods or fields
  14. 14. The .NET Approach• Use parameterized template as a compiler aid and a runtime code generation sketch for the JIT• Constraints used to prove things to the compiler public class List<T> { T[] items; int size; int cap; public void Add(T item) { ... } public T this[int index] { get { ... } set { ... } } }
  15. 15. Digression: .NET Object Layout
  16. 16. .NET Generic Types at Runtime• There is a separate type at runtime for each generic instantiation, but not necessarily a separate copy of the methods’ code• Does this method’s machine code depend on T? public void Add(T item) { if (size < items.Length – 1) { items[size] = item; ++size; } else AllocateAndAddSlow(item); }
  17. 17. .NET Generic Code Sharing
  18. 18. Concrete Example: Stack PushBasicStack`1[[System.__Canon, mscorlib]].Push(System.__Canon)00260360 57 push edi00260361 56 push esi00260362 8b7104 mov esi,dword ptr [ecx+4]00260365 8b7908 mov edi,dword ptr [ecx+8]00260368 8d4701 lea eax,[edi+1]0026036b 894108 mov dword ptr [ecx+8],eax0026036e 52 push edx0026036f 8bce mov ecx,esi00260371 8bd7 mov edx,edi00260373 e8f4cb3870 call clr!JIT_Stelem_Ref (705ecf6c)00260378 5e pop esi00260379 5f pop edi0026037a c3 ret
  19. 19. Concrete Example: Stack PushBasicStack`1[[System.Int32, mscorlib]].Push(Int32)002603c0 57 push edi002603c1 56 push esi002603c2 8b7104 mov esi,dword ptr [ecx+4]002603c5 8b7908 mov edi,dword ptr [ecx+8]002603c8 8d4701 lea eax,[edi+1]002603cb 894108 mov dword ptr [ecx+8],eax002603ce 3b7e04 cmp edi,dword ptr [esi+4]002603d1 7307 jae 002603da002603d3 8954be08 mov dword ptr [esi+edi*4+8],edx002603d7 5e pop esi002603d8 5f pop edi002603d9 c3 ret002603da e877446170 call clr!JIT_RngChkFail (70874856)002603df cc int 3
  20. 20. Concrete Example: Stack PushBasicStack`1[[System.Double, mscorlib]].Push(Double)00260420 56 push esi00260421 8b5104 mov edx,dword ptr [ecx+4]00260424 8b7108 mov esi,dword ptr [ecx+8]00260427 8d4601 lea eax,[esi+1]0026042a 894108 mov dword ptr [ecx+8],eax0026042d 3b7204 cmp esi,dword ptr [edx+4]00260430 730c jae 0026043e00260432 dd442408 fld qword ptr [esp+8]00260436 dd5cf208 fstp qword ptr [edx+esi*8+8]0026043a 5e pop esi0026043b c20800 ret 80026043e e813446170 call clr!JIT_RngChkFail (70874856)00260443 cc int 3
  21. 21. Type-Specific Code• What about new T[12] or typeof(T).FullName?• When .NET generic methods need access to T, they get it from the method table (this or hidden parameter)• …Unless the type parameters are value types, in which case the MT is hard-coded into the method:C#:Foo<T>() { … typeof(T) … } T=intMachine code:mov ecx,offset 798b6844 (MT: System.Int32)call clr!JIT_GetRuntimeType (6ca40aa8)
  22. 22. Generics and Reflection• Because generic types are first-class citizens, they are accessible to Reflection at runtime Type to = typeof(Dictionary<,>); Type tc = to.MakeGenericType( typeof(string), typeof(int)); to = typeof(List<double>).GetGenericTypeDefinition(); tc = to.MakeGenericType(typeof(int)); //List<int>
  23. 23. Generic Constraints• .NET constraints restrict type parameters at compile- time, very similar to Java’s• Only a limited set of constraints available: • Interface constraint: where T : IComparable<T> • Base constraint: where T : UserControl • Category constraint: where T : class or where T : struct • Constructor constraint: where T : new() Note that constraints don’t break the machine code equivalence for reference types. Why?
  24. 24. Case Study: IEquatable<T> public static void CallEquals<T>(T inst) { inst.Equals(inst); }public struct Point { public int X, Y; public override bool Equals(object o) { if (o is Point) return Equals((Point)o); return false; } public bool Equals(Point pt) { ... }}
  25. 25. Case Study: IEquatable<T>• CallEquals has no constraints, so the C# compiler chooses the Object.Equals(Object) virtual method• We can add an interface constraint with a strongly-typed Equals method—now the compiler prefers it • Note: the interface call has no virtual cost on value types public static void CallEquals<T>(T inst) where T : IEquatable<T> { inst.Equals(inst); }
  26. 26. Sorting “If Possible”, a la C++public class List<T> { T[] items; ... public void Add(T item) { ... } public void Sort(SortProvider<T> sorter = null) { sorter = sorter ?? SortProvider<T>.GetDefault(); if (sorter == null) throw new NotImplementedException(); sorter.Sort(items); }}
  27. 27. Sorting “If Possible”, a la C++public abstract class SortProvider<T> { public abstract void Sort(T[] items); public static SortProvider<T> GetDefault() { if (T is IComparable<T>) return new DefaultSortProvider<T>(); if (T is IGreaterthanable<T>) return new GreaterThanSortProvider<T>(); return null; }}internal class DefaultSortProvider<T> : SortProvider<T> where T : IComparable<T> { //Use T.CompareTo for sorting}
  28. 28. Getting Generic Math Right in .NET• Pretty nasty:• Consider Complex<T>: you can’t implement operators…• Solution sketch: • Define ICalculator<T> with methods instead of operators • Implement ICalculator<T> for each T • Choose between ICalculator<T>’s implementations at runtime, and use them in your generic math code • For more: http://www.codeproject.com/Articles/8531/Using-generics-for-calculations
  29. 29. The .NET Approach—Pros and Cons Pros Cons• Constraint violation • Constraints are not results in clear compiler enough for everything error (e.g., generic math)• Can share generic types • No meta-programming and objects between abilities (advantage?) packages/applications• Can use generics efficiently with value types• Can use Reflection to query over generic types
  30. 30. QUESTIONS?Sasha Goldshteinblog.sashag.net | @goldshtn
  1. A particular slide catching your eye?

    Clipping is a handy way to collect important slides you want to go back to later.

×