In one of my last post, I asked the question, how does the callstack look like.
It was is simple unmanaged class with a virtual function. If you had executed the sample program, and set a breakpoint into the “Foo” method, you can see in the callstack-window of VS, the following callstack:
As you can see, there are two transitions which we had not expected. One transition to native code, and one transition back to managed code.
And if you put a “printf” statement into the copy-constructor of the V-struct, you will see that the copy-constructor is called tiwce! This can have very strong performance effects…
So what might be the course of this “unnecessary” transitions?
The problem is: We have written an unmanaged virtual function as a managed function (the whole file is compiled with /clr). This means, that all unmanaged virtual functions will have two entry points. One entry point is for the “native” v-table call (thiscall); and the other is for the managed entry (clrcall).
If you call a unmanaged virtual function in a managed world, the compiler does not know which function must be callled. This is only examined at runtime through the v-table. Therefor the compiler needs to call the native function which uses the v-table. Then this “proxy”-function is calling the managed entry point. And because there are two functiions involved, the copy-constructor the the V-struct is called twice.
Conclusion:
Be aware that virtual functions in unmanaged classes which are compiled with /clr might lead to performance decrease.
If you have a big codebase, you should only enable /clr for specific files which implements the “wrapper”.
Be aware that every managed/unmanaged transition has additional execution-costs on your application. So prevent this transitions as much as possible.