Skip to content

[Question] Can Virtual Stub Dispatch be "inlined"? #7198

Closed
@benaadams

Description

@benaadams

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
Figure 1

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?

Metadata

Metadata

Assignees

No one assigned

    Labels

    area-VM-coreclrbacklog-cleanup-candidateAn inactive issue that has been marked for automated closure.questionAnswer questions and provide assistance, not an issue with source code or documentation.

    Type

    No type

    Projects

    No projects

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions