“Šuo ir kariamas pripranta.”
Lithuanian proverbs
var o = new object();
Instance on heap:
Sync block address |
Method table address |
Field1 |
FieldN |
EEClass address |
Interface Map Table address |
Inherited Virtual Method addresses |
Introduced Virtual Method addresses |
Instance Method addresses |
Static Method addresses |
Static Fields values |
InterfaceN method addresses |
SOS / Son of Strike
SOSEX / SOS extensions
An example class:
public class MyClass
{
private int _myField;
public int MyMethod()
{
return _myField;
}
}
An instance:
var myClass = new MyClass();
0:003> !DumpHeap -type GenericsUnderTheHood.MyClass
Address MT Size
0000004a2d912de8 00007fff8e7540d8 24
Statistics:
MT Count TotalSize Class Name
00007fff8e7540d8 1 24 GenericsUnderTheHood.MyClass
Total 1 objects
0:003> !dumpmt -md 00007fff8e7540d8
EEClass: 00007fff8e8623f0
Module: 00007fff8e752fc8
Name: GenericsUnderTheHood.MyClass
mdToken: 0000000002000002
File: C:\Projects\my\GenericsUnderTheHood\GenericsUnderTheHood\bin\Debug\GenericsUnderTheHood.exe
BaseSize: 0x18
ComponentSize: 0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
00007fffecc86300 00007fffec8380e8 PreJIT System.Object.ToString()
00007fffeccce760 00007fffec8380f0 PreJIT System.Object.Equals(System.Object)
00007fffeccd1ad0 00007fffec838118 PreJIT System.Object.GetHashCode()
00007fffeccceb50 00007fffec838130 PreJIT System.Object.Finalize()
00007fff8e8701c0 00007fff8e7540d0 JIT GenericsUnderTheHood.MyClass..ctor()
00007fff8e75c048 00007fff8e7540c0 NONE GenericsUnderTheHood.MyClass.MyMethod()
0:003> !DumpClass 00007fff8e8623f0
Class Name: GenericsUnderTheHood.MyClass
mdToken: 0000000002000002
File: C:\Projects\my\GenericsUnderTheHood\GenericsUnderTheHood\bin\Debug\GenericsUnderTheHood.exe
Parent Class: 00007fffec824908
Module: 00007fff8e752fc8
Method Table: 00007fff8e7540d8
Vtable Slots: 4
Total Method Slots: 5
Class Attributes: 100001
Transparency: Critical
NumInstanceFields: 1
NumStaticFields: 0
MT Field Offset Type VT Attr Value Name
00007fffecf03980 4000001 8 System.Int32 1 instance _myField
HOWTO: Debugging .NET with WinDbg
Book: "Pro .NET Performance" by Sasha Goldshtein, Dima Zurbalev, Ido Flatow
An example class:
public class MyGenericClass<T>
{
private T _myField;
public T MyMethod()
{
return _myField;
}
}
Compiles to:
.class public auto ansi beforefieldinit
GenericsUnderTheHood.MyGenericClass`1<T>
extends [mscorlib]System.Object
{
.field private !T _myField
.method public hidebysig
instance !T MyMethod () cil managed
{
...
}
...
}
An instance:
var myObject = new MyGenericClass<object>();
Method table:
0:003> !DumpMT -md 00007fff8e754368
EEClass: 00007fff8e862510
Module: 00007fff8e752fc8
Name: GenericsUnderTheHood.MyGenericClass`1[[System.Object, mscorlib]]
mdToken: 0000000002000003
File: C:\Projects\my\GenericsUnderTheHood\GenericsUnderTheHood\bin\Debug\GenericsUnderTheHood.exe
BaseSize: 0x18
ComponentSize: 0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
00007fffecc86300 00007fffec8380e8 PreJIT System.Object.ToString()
00007fffeccce760 00007fffec8380f0 PreJIT System.Object.Equals(System.Object)
00007fffeccd1ad0 00007fffec838118 PreJIT System.Object.GetHashCode()
00007fffeccceb50 00007fffec838130 PreJIT System.Object.Finalize()
00007fff8e870210 00007fff8e754280 JIT GenericsUnderTheHood.MyGenericClass`1[[System.__Canon, mscorlib]]..ctor()
00007fff8e75c098 00007fff8e754278 NONE GenericsUnderTheHood.MyGenericClass`1[[System.__Canon, mscorlib]].MyMethod()
EEClass:
0:003> !DumpClass 00007fff8e862510
Class Name: GenericsUnderTheHood.MyGenericClass`1[[System.__Canon, mscorlib]]
mdToken: 0000000002000003
File: C:\Projects\my\GenericsUnderTheHood\GenericsUnderTheHood\bin\Debug\GenericsUnderTheHood.exe
Parent Class: 00007fffec824908
Module: 00007fff8e752fc8
Method Table: 00007fff8e7542a0
Vtable Slots: 4
Total Method Slots: 6
Class Attributes: 100001
Transparency: Critical
NumInstanceFields: 1
NumStaticFields: 0
MT Field Offset Type VT Attr Value Name
00007fffecf05c80 4000002 8 System.__Canon 0 instance _myField
An instance:
var myString = new MyGenericClass<string>();
Method table:
0:003> !DumpMT -md 00007fff8e754400
EEClass: 00007fff8e862510
Module: 00007fff8e752fc8
Name: GenericsUnderTheHood.MyGenericClass`1[[System.String, mscorlib]]
mdToken: 0000000002000003
File: C:\Projects\my\GenericsUnderTheHood\GenericsUnderTheHood\bin\Debug\GenericsUnderTheHood.exe
BaseSize: 0x18
ComponentSize: 0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
00007fffecc86300 00007fffec8380e8 PreJIT System.Object.ToString()
00007fffeccce760 00007fffec8380f0 PreJIT System.Object.Equals(System.Object)
00007fffeccd1ad0 00007fffec838118 PreJIT System.Object.GetHashCode()
00007fffeccceb50 00007fffec838130 PreJIT System.Object.Finalize()
00007fff8e870210 00007fff8e754280 JIT GenericsUnderTheHood.MyGenericClass`1[[System.__Canon, mscorlib]]..ctor()
00007fff8e75c098 00007fff8e754278 NONE GenericsUnderTheHood.MyGenericClass`1[[System.__Canon, mscorlib]].MyMethod()
An instance:
var myInt = new MyGenericClass<int>();
Method table:
0:003> !DumpMT -md 00007fff8e7544c0
EEClass: 00007fff8e862628
Module: 00007fff8e752fc8
Name: GenericsUnderTheHood.MyGenericClass`1[[System.Int32, mscorlib]]
mdToken: 0000000002000003
File: C:\Projects\my\GenericsUnderTheHood\GenericsUnderTheHood\bin\Debug\GenericsUnderTheHood.exe
BaseSize: 0x18
ComponentSize: 0x0
Slots in VTable: 6
Number of IFaces in IFaceMap: 0
--------------------------------------
MethodDesc Table
Entry MethodDesc JIT Name
00007fffecc86300 00007fffec8380e8 PreJIT System.Object.ToString()
00007fffeccce760 00007fffec8380f0 PreJIT System.Object.Equals(System.Object)
00007fffeccd1ad0 00007fffec838118 PreJIT System.Object.GetHashCode()
00007fffeccceb50 00007fffec838130 PreJIT System.Object.Finalize()
00007fff8e870260 00007fff8e7544b8 JIT GenericsUnderTheHood.MyGenericClass`1[[System.Int32, mscorlib]]..ctor()
00007fff8e75c0c0 00007fff8e7544b0 NONE GenericsUnderTheHood.MyGenericClass`1[[System.Int32, mscorlib]].MyMethod()
EEClass:
0:003> !DumpClass 00007fff8e862628
Class Name: GenericsUnderTheHood.MyGenericClass`1[[System.Int32, mscorlib]]
mdToken: 0000000002000003
File: C:\Projects\my\GenericsUnderTheHood\GenericsUnderTheHood\bin\Debug\GenericsUnderTheHood.exe
Parent Class: 00007fffec824908
Module: 00007fff8e752fc8
Method Table: 00007fff8e7544c0
Vtable Slots: 4
Total Method Slots: 6
Class Attributes: 100001
Transparency: Critical
NumInstanceFields: 1
NumStaticFields: 0
MT Field Offset Type VT Attr Value Name
00007fffecf03980 4000002 8 System.Int32 1 instance _myField
Note: Optimizes the generic calls in your method not your generic method
Design and Implementation of Generics for the .NET Common Language Runtime
Simplified version:
public class BaseClass<T>
{
private List<T> _list = new List<T>();
public BaseClass()
{
Enumerable.Empty<T>();
// or Enumerable.Repeat(new T(), 10);
// or even new T();
// or foreach (var item in _list) {}
}
public void Run()
{
for (var i = 0; i < 8000000; i++)
{
if (_list.Any())
// or if (_list.Count() > 0)
// or if (_list.FirstOrDefault() != null)
// or if (_list.SingleOrDefault() != null)
// or other IEnumerable<T> method
{
return;
}
}
}
}
public class DerivedClass : BaseClass<object>
{
}
Benchmark:
public class Program
{
public static void Main()
{
Measure(new DerivedClass());
Measure(new BaseClass<object>());
}
private static void Measure(BaseClass<object>> baseClass)
{
var sw = Stopwatch.StartNew();
baseClass.Run();
sw.Stop();
Console.WriteLine(sw.ElapsedMilliseconds);
}
}
"Just add two methods"
public class BaseClass<T>
{
...
public void Method1()
{
}
public void Method2()
{
}
...
}
DWORD numMethodsAdjusted =
(bmtMethod->dwNumDeclaredNonAbstractMethods == 0)
? 0
: (bmtMethod->dwNumDeclaredNonAbstractMethods < 3)
? 3
: bmtMethod->dwNumDeclaredNonAbstractMethods;
DWORD nTypeFactorBy2 = (bmtGenerics->GetNumGenericArgs() == 1)
? 2
: 3;
DWORD estNumTypeSlots = (numMethodsAdjusted * nTypeFactorBy2 + 2) / 3;
CORINFO_GENERIC_HANDLE
JIT_GenericHandleWorker(
MethodDesc * pMD,
MethodTable * pMT,
LPVOID signature)
{
CONTRACTL {
THROWS;
GC_TRIGGERS;
} CONTRACTL_END;
MethodTable * pDeclaringMT = NULL;
if (pMT != NULL)
{
SigPointer ptr((PCCOR_SIGNATURE)signature);
ULONG kind; // DictionaryEntryKind
IfFailThrow(ptr.GetData(&kind));
// We need to normalize the class passed in (if any) for reliability purposes. That's because preparation of a code region that
// contains these handle lookups depends on being able to predict exactly which lookups are required (so we can pre-cache the
// answers and remove any possibility of failure at runtime). This is hard to do if the lookup (in this case the lookup of the
// dictionary overflow cache) is keyed off the somewhat arbitrary type of the instance on which the call is made (we'd need to
// prepare for every possible derived type of the type containing the method). So instead we have to locate the exactly
// instantiated (non-shared) super-type of the class passed in.
ULONG dictionaryIndex = 0;
IfFailThrow(ptr.GetData(&dictionaryIndex));
pDeclaringMT = pMT;
for (;;)
{
MethodTable * pParentMT = pDeclaringMT->GetParentMethodTable();
if (pParentMT->GetNumDicts() <= dictionaryIndex)
break;
pDeclaringMT = pParentMT;
}
if (pDeclaringMT != pMT)
{
JitGenericHandleCacheKey key((CORINFO_CLASS_HANDLE)pDeclaringMT, NULL, signature);
HashDatum res;
if (g_pJitGenericHandleCache->GetValue(&key,&res))
{
// Add the denormalized key for faster lookup next time. This is not a critical entry - no need
// to specify appdomain affinity.
JitGenericHandleCacheKey denormKey((CORINFO_CLASS_HANDLE)pMT, NULL, signature);
AddToGenericHandleCache(&denormKey, res);
return (CORINFO_GENERIC_HANDLE) (DictionaryEntry) res;
}
}
}
DictionaryEntry * pSlot;
CORINFO_GENERIC_HANDLE result = (CORINFO_GENERIC_HANDLE)Dictionary::PopulateEntry(pMD, pDeclaringMT, signature, FALSE, &pSlot);
if (pSlot == NULL)
{
// If we've overflowed the dictionary write the result to the cache.
BaseDomain *pDictDomain = NULL;
if (pMT != NULL)
{
pDictDomain = pDeclaringMT->GetDomain();
}
else
{
pDictDomain = pMD->GetDomain();
}
// Add the normalized key (pDeclaringMT) here so that future lookups of any
// inherited types are faster next time rather than just just for this specific pMT.
JitGenericHandleCacheKey key((CORINFO_CLASS_HANDLE)pDeclaringMT, (CORINFO_METHOD_HANDLE)pMD, signature, pDictDomain);
AddToGenericHandleCache(&key, (HashDatum)result);
}
return result;
}
Generic method:
public class MyClassWithGenericMethod
{
public T MyGenericMethod<T>(T arg)
{
return arg;
}
}
Generic struct:
public struct MyGenericStruct<T>
{
private T _myField;
public T MyMethod()
{
return _myField;
}
}