C# vs C++/CLI vs PInvoke Performance – Part I

C++/CLI vs Explicit PInvoke Performance – Part I

When I have a C++ DLL with a bunch of C++ classes and I would want to pinvoke those C++ classes from C#, I have two choices, either using C++/CLI to write a managed wrapper or using our C# wrapper generator to generate a managed C# wrapper of the native C++ DLL. I will create both of the wrappers today.

There is actually a false assumption all over the web which assumes that calling into native C++ DLL from C# via C++/CLI wrapper is faster than simply explicitly pinvoking the C++ DLL. That assumption is wrong and explicit pinvoke actually performs better than C++/CLI when C++/CLI is used to write a managed wrapper of C++ DLL, which is used from .NET. I am going to write a few blogs to talk about the performance difference of C++/CLI wapper and the C# wrapper via explicit PInvoke generated by our PInvoke Interop SDK tools.

This is the first part of my blog comparing C++/CLI to the C# wrapper generated by our C# wrapper generator for C++ DLL.

Let’s write a C++ class with 2 methods we will call from C# via different wrappers. We are going to use a sqrt method in C++ DLL, in this example, the parameter is simple and the type is blittable type and no marshal is needed from C# to the native dll.

The implementation of a managed C++/CLI wrapper for native C++ DLL is very simple for our example although it will become very tedious and time-consuming if you want to wrap a large scale of exported C++ classes in a C++ DLL, such as type marshaling and maintaining all the method signatures, but that is a different story, we may would want to talk about this in the future.

Now, I will present the C# class for testing the wrapper, I also wrote a managed C# sqrt for comparison.

Here is the result for calling float sqrt(float) from C# via all different kinds of wrappers.

The C# version did not perform much faster than the explicit Pinvoke C# wrapper whose method call is made directly on the PInvoke method without going through the C# wrapper class of Calculator. That is because the C# version of Math.Sqrt accepts a double parameter while we passed float type, type conversion happens when we call Sqrt, type conversion happens again when we get back the return value of double.

Calling sqrt directly via the pinvoke method is faster than calling sqrt method of the C# wrapper class of Calculator because there is less stack push and pop.

The C++/CLI wrapper presents the worst performance. I was not surprised because C++/CLI wrapper adds additional layer to make the call and the underlying mechanism of calling into the C++ DLL is still the same, P/Invoke, it is implicit though.

Here is testing class for double version of sqrt and its result for the method of double sqrt(double x).

The result of C++/CLI, PInvoke Performance

This time, the C# managed sqrt performs better than the float version because it does not need to convert the input parameter and convert back the return value any more,  and conversion back and forth does take time.

And, unfortunately, the C++/CLI wrapper is again the worst performer.

To be fair to C++/CLI, I would mention that the

was added to the PInvoke declaration when building the C# Wrapper for the native C++ DLL.

Stay tuned for the second part of the blog of C++/CLI vs PInvoke Performance.

2 thoughts on “C# vs C++/CLI vs PInvoke Performance – Part I

  1. Since you do not show the generated C# code, I am left wondering what part of the work done by the C++/CLI wrapper is avoided in the generated code.

    From the P/Invoke signature, it looks like a Calculator class object is being allocated on the native heap and its address is passed using the IntPtr argument. The C# wrapper class would then call this method. How is this different from the C++/CLI wrapper which does the same thing with an implicit P/Invoke?

    Could you please post the code or at least explain how the performance improvement is achieved? Without this information, it is difficult to judge the relevance of this micro-benchmark.

    • Hi, Koustubh Moharir,

      Thank you for commenting.

      To answer your question, the C# Wrapper via Explicit P/Invoke generator by NGeneator is a pure C# wrapper library. It does not use C++/CLI at all.

      The performance of Explicit P/Invoke over C++/CLI is by the nature of their design, I did not have to do anything to improve the performance, as long as you don’t use C++/CLI to aggregate calls between C++ and managed code which you would want to do same in the C++ code anyway, C++/CLI always performs worth than Explicit P/Invoke.

      All the P/Invoke signatures with their wrapper code are generated by NGenerator which is part of our C# .NET P/Invoke Interop SDK, you would be able to purchase license once it gets release. Along with the P/Invoke signature, the testing C# code was published.

      The Calculator class was generated by NGenerator as well. It depends on part of the SDK which I can not publish due to the company’s policy.

Comments are closed.

Translate »