Closed
Description
Investigation of dispatch costs
There are 3 states for an interface callsite
1. 0 attached methods : Unresolved -> Lookup stub
2. 1 attached method : Monomorphic -> Dispatch stub
3. N attached methods : Megamorphic -> Resolve stub
For a monomorphic interface call it always goes via a stub to do dispatch; which is roughly the same cost as 2 chained non-inlined direct calls:
Method | Mean | StdDev | Scaled | RPS |
---------------------------- |-------------- |----------- |------- |------------- |
-> InterfaceMonomorphic | 1,276.6333 ns | 16.6153 ns | 1.00 | 783,310.26 |
InterfacePolymorphicX2 | 1,444.7386 ns | 25.2325 ns | 1.13 | 692,166.75 |
InterfaceMegamorphicX33 | 1,428.9762 ns | 13.8861 ns | 1.12 | 699,801.72 |
InterfaceUnsafeCastConstTest | 830.3120 ns | 9.8983 ns | 0.65 | 1,204,366.58 |
InterfaceUnsafeCastVarTest | 1,198.1936 ns | 16.6743 ns | 0.94 | 834,589.69 |
-> DirectViaCastIndirected | 1,136.0911 ns | 19.6327 ns | 0.89 | 880,211.12 |
DirectViaCastNotInlined | 679.2567 ns | 9.4285 ns | 0.53 | 1,472,197.44 |
DirectViaCastInlined | 192.5118 ns | 5.2530 ns | 0.15 | 5,194,485.75 |
Which is in line with the BoTR Virtual Stub Dispatch where the call site; calls stub; which calls method
For 1 resolved type (monomorphic) it looks roughly like this (excuse the pseudo-code)
function CallInterface()
{
VirtualStub dispatch = callsite_dispatch;
dispatch.Invoke(obj.methodTable);
}
class DispatchStub : VirtualStub
{
Type expectedMT;
PCODE implTarget;
PCODE failTarget;
Invoke(MethodTable methodTable)
{
if (expectedMT == methodTable)
{
this->implTarget();
}
else
{
this->failTarget();
}
}
}
However if the LookupStub and ResolveStubs returned 0 as MT then the DispatchStub could be turned into a regular POCO and the call inlined?
function CallInterface()
{
var dispatch = callsite_dispatch;
var expectedMT = dispatch->expectedMT;
if (expectedMT == 0 || expectedMT == obj.methodTable)
{
dispatch->implTarget();
}
else
{
dispatch->failTarget();
}
}
class DispatchStub : VirtualStub
{
Type expectedMT;
PCODE implTarget;
PCODE failTarget;
}
class LookupStub : VirtualStub
{
Type expectedMT = 0;
PCODE implTarget => Invoke();
PCODE failTarget ...;
}
class ResolveStub : VirtualStub
{
Type expectedMT = 0;
PCODE implTarget => Invoke();
PCODE failTarget ...;
}
Comparable examples
Inline caches in Smalltalk, Java, Javascript
Question
Would there be any benefit to this change?