Speed up mixed mode apps with __clrcall

When you compile with /clr and you have a native class in your code (one that’s neither a ref class nor a value class), there are two entry points generated for its methods – a managed entry point and a native entry point. This allows both managed and native callers to call these methods, and the native entry point acts as a thunk to the managed entry point. Virtual functions are always called through the native entry point. Now, this results in a performance issue; when a managed caller calls a virtual function in a /clr compiled native class, there are two managed-unmanaged transitions that are required – the first managed-to-native transition to invoke the native entry point and the second native-to-managed transition to invoke the managed entry point – this is referred to as double thunking.

class N
{
public:
    N()
    {
        Console::WriteLine(__FUNCSIG__);
    }
    N(const N&)
    {
        Console::WriteLine(__FUNCSIG__);
    }
    ~N()
    {
        Console::WriteLine(__FUNCSIG__);
    }
};

class X
{
public:
    virtual void UseNVirt(N)
    {
        Console::WriteLine(__FUNCSIG__);
    }

Now, if you call UseNVirt as follows, you can see the effect of double thunking :-

    X* x = new X();
    Console::WriteLine();

    x->UseNVirt(n);
    Console::WriteLine();

/*** Output

__thiscall N::N(const class N &)
__thiscall N::N(const class N &)
__thiscall N::~N(void)
void __thiscall X::UseNVirt(class N)
__thiscall N::~N(void)

***/

The solution to this is to use __clrcall appropriately :-

class X
{
public:
    virtual void UseNVirt(N)
    {
        Console::WriteLine(__FUNCSIG__);
    }
    virtual void __clrcall UseNVirtClrCall(N)
    {
        Console::WriteLine(__FUNCSIG__);
    }

Now, if you call UseNVirtClrCall as follows, there’s no double thunking involved :-

    X* x = new X();
    Console::WriteLine();

    x->UseNVirtClrCall(n);
    Console::WriteLine();

/*** Output

__thiscall N::N(const class N &)
void __clrcall X::UseNVirtClrCall(class N)
__thiscall N::~N(void)

***/

So, my suggestion to those who are doing mixed mode programming would be to profusely use __clrcall except when you know for certain that the method could be invoked by a native caller. Note that VC++ 2005 does some optimizations on its own and implicitly adds __clrcall to any method with a managed type in its signature (including the return type).

Advertisements

3 thoughts on “Speed up mixed mode apps with __clrcall

  1. I’ve found the same performance issues as Jeff.

    In fact – I’ve abandoned all c++/clr development due to the the debug performance issues
    and the failure to ship stl.net. A language _is_ it’s libraries and the failure
    to produce a version of the standard library that can work with managed classes
    renders c++/clr useless.

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s