Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

When is code shared for different instantiations of generics in the CLR?

If you have a method or type Foo<T>, then the CLR may compile multiple versions for different T. I know that all reference types share the same version. How does it work for structs? Is code sometimes shared, or never shared, for different structs? I could imagine that code is shared for all structs of the same size, for example.

I'm interested because I'm wondering about the following example:

interface IBar 
{
   void DoBar();
}

struct Baz : IBar 
{
   public void DoBar(){ ... }
}

struct Quux : IBar 
{
   public void DoBar(){ ... }
}

Now if I do the following:

public void ExecuteBar<T>(T bar) where T:IBar
{
   bar.DoBar();
}

ExecuteBar(new Baz());
ExecuteBar(new Quux());

Will this generate two versions of ExecuteBar, each with a direct (non virtual) call directly to Bar.DoBar() and Quux.DoBar()? Or is the dispatch done at run time?

like image 628
Jules Avatar asked Sep 01 '14 17:09

Jules


2 Answers

There is no straight-forward answer to this question, it greatly depends on the jitter you use and what kind of code is present in the generic method.

A starting point is that the jitter does generate a distinct method for each individual value type. Even for structs that are otherwise completely identical. Something you can see with the debugger. Use Debug + Windows + Disassembly to see the generated machine code. Single step into a method, use the Debug + Windows + Registers window, the EIP/RIP register shows you the location of the method in memory.

But, generic methods like this are still eligible for the inlining optimization. Very important to perf, it makes the entire method disappear and the code in the method is injected into the caller method. In which case the distinction between generic methods does disappear. This is not something you can normally count on to happen for interface implementation methods. It does however happen for your sample code if you leave the method bodies empty. With different results for the x86 and the x64 jitter.

You can only really tell what you get by looking at the generated machine code. Do make sure that you allow the optimizer to do its job, Tools + Options, Debugging, General, untick the "Suppress JIT optimization" checkbox. And, of course, do make sure that you never depend on an accurate answer to this question. Implementation details like this are subject to change without notice.

like image 158
Hans Passant Avatar answered Sep 29 '22 04:09

Hans Passant


The generic definitions are not directly used. Instead constructed types are created at runtime.

One construct will be created for each value type argument. A unique construct will be created for all reference types arguments.

Afaik, different types do not share the same asm instructions.

like image 40
Victor Victis Avatar answered Sep 29 '22 05:09

Victor Victis