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

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

Today, I am going to continue the comparison of C++/CLI vs Explict PInvoke performance. It is the second part of the blog, you may want to read the first part if you have not already.

string or String is commonly used in .NET, if you know nothing about C++/CLI, you would expect it to be able to convert or marshal a .NET string to c string such as char* transparently with no data copy at all, but you will be disappointed to see the a different way of how C++/CLI actually bridges string between managed world and the native world. We will be talking about this later.

In order to test the performance of using the C++/CLI managed wrapper and a C# wrapper generated by our .NET PInvoke Interop SDK – A C# Wrapper Generator for C++ DLL, we will need to create a C++ class inside a native C++ DLL. We are going to use a sample C++ DLL from now on. We are going to write a single method IndexOf in a C++ class named StringHelper, the method is used to find the index of a key string in a value string. Here is the implementation of the C++ class.

In the preceding example of IndexOf method, we used strstr to find the pointer of the key string value, we did not use std::string::find method to find the index of the key string because std::string:find is far less efficient than strstr, and then it would undermine the efficiency of the Explicit P/Invoke when we do the performance comparison of C++/CLI vs Explicit P/Invoke.

Once again, let’s write the C++/CLI wrapper class first, it is a simple one because we have only one class with one method we will be using, if there are many of them, it is tedious, time-consuming, hard-to-maintain, because everything you want from the original C++ class, you will need to wrap them in C++/CLI, it has no such intelligence allowing you to automatically find the methods and wrap them for you.

Remember I stated that the string conversion is not transparent, you would have to manually convert the .NET string to C++ const char*, which is being shown in the preceding code. That is where it performs so much worse than Explicit P/Invoke, I will show you the result later. In order to use a string parameter in C++/CLI to call into the native C++ DLL, we are responsible for converting it to const char* by using a class called marshal_context, I appreciate such a type conversion class is provided by C++/CLI, the issue with this kind of type conversion is it slows down the whole process of calling the native C++ classes, which makes me unwilling to use C++/CLI, not mentioning that I still get to maintain the code whenever I change the pubic interfaces of my C++ classes.

It took me only less than 5 seconds to use our PInvoke Interop SDK Tools to generate a C# wrapper for the sample C++ DLL. A new class named StringHelper is ready for me to use in C# code. I will show you the P/Invoke signature and the wrapper code.

Now, let’s write the C# code to test the performance of C++/CLI and Explicit PInvoke. I would also want to know the performance difference of the C# wrapper for the native version of IndexOf and the C# version of String.IndexOf. Here is the complete test class.

I was surprised to see the testing result, i ran the testing program a few times with different iteration numbers, but they all gave me similar result. Just like what you can see the following picture, the wrapper class generated by our PInvoke Interop SDK performs very close to the C# version of String.IndexOf.

The C# wrapper generated by our PInvoke Interop SDK Tools is 3 times faster than C++/CLI wrapper, it is so close to the C# version of String.IndexOf. From my experience of working with so many C++ DLL, I would say there is not much string copy at all when passing a string value to C++ DLL using Explicit P/Invoke when the C++ method accepts char* as parameter, I don’t know the details of .NET implementation though.

C++/CLI wrapper is once again the worst performer when passing string(char* type) from C# to the C++ DLL.

Stay tuned, we will be comparing the performance when using std::string in the C++ class next time. I hope the C++/CLI wrapper for C++ DLL could perform better.


Translate »