Click here to Skip to main content
Licence CPOL
First Posted 12 Sep 2011
Views 9,973
Downloads 429
Bookmarked 24 times

PInvoke Performance

By | 13 Sep 2011 | Article
Performance comparison of P/Invoke versus a C++/CLI wrapper.

Introduction

Skip to the results

There is a performance penalty when using P/Invoke to cross the managed/unmanaged boundary. But how serious is this penalty? Can this penalty be reduced by not using P/Invoke, but writing a C++/CLI library that exposes functions from a traditional API?

In this article, we will look into the performance of P/Invoke compared to a C++/CLI wrapper.

Screenshot1.png

Background

I am currently working on the new version of my OpenGL wrapper and Scene Graph called SharpGL (http://www.codeproject.com/KB/openGL/sharpgl.aspx). OpenGL is a very 'talkative' API - the functions are called many thousands of times per second. Whilst working on this library I wondered, would it be faster to write a C++/CLI class library to expose OpenGL functions or would it be faster to P/Invoke them directly? A brief Google suggested a C++/CLI wrapper but I wanted to look into this further.

I have written a tiny API called 'TraditionalAPI' which exposes three functions - this project invokes the functions a number of times using different methods.

Part 1: The Traditional API

The traditional API exposes three basic functions:

Test Function 1: IncrementCounter

This is the most basic function I could come up with, testing this function should be a good way of testing the overhead of a P/Invoke call:

//    A global counter.
unsigned int g_uCounter = 0;

TRADITIONALAPI_API void __stdcall TA_IncrementCounter()
{
    g_uCounter++;
}

Test Function 2: Square Root

The second function calculates the square root of a double. No complicated marshalling should be required:

//    A slightly more complex function, find the square root of a double.
TRADITIONALAPI_API double __stdcall TA_CalculateSquareRoot(double dValue)
{
    return ::sqrt(dValue);
}

Test Function 3: Dot Product

The next function calculates the dot product of two three-tuples. This function takes two arrays - so in the managed world we will have to pin memory to marshal this:

TRADITIONALAPI_API double __stdcall TA_DotProduct(
               double arThreeTuple1[], double arThreeTuple2[])
{
    return arThreeTuple1[0] * arThreeTuple2[0] + arThreeTuple1[1] * 
           arThreeTuple2[1] + arThreeTuple1[2] * arThreeTuple2[2];
}

Part 2: The C++/CLI Wrapper

The second part of the solution is a C++/CLI wrapper that wraps each function:

Test Function 1 Wrapper

void IncrementCounter()
{
    //    Call the unmanaged function.
    ::TA_IncrementCounter();
}

Nothing special here - this is a C++/CLI class so we will be able to call IncrementCounter from another .NET application.

Test Function 2 Wrapper

double CalculateSquareRoot(double value)
{
    //    Call the unmanaged function.
    return ::TA_CalculateSquareRoot(value);
}

Again, nothing complicated is required for this function.

Test Function 3 Wrapper

double DotProduct(array<double>^ threeTuple1, array<double>^ threeTuple2)
{
    //    Pin the arrays.
    pin_ptr<double> p1(&threeTuple1[0]);
    pin_ptr<double> p2(&threeTuple2[0]);
            
    //    Call the unmanaged function.
    return TA_DotProduct(p1, p2);
}

Now in this case, we actually have to do some work - pinning the managed arrays so that we can access them directly in the unmanaged API.

Part 3: The C# Test Application

The final part of the solution is a C# WPF application that runs the tests. The TraditionalAPI DLL can run each test function individually or run each test a number of times. Because of this, we can compare the following:

  • The time taken to run x tests directly in TraditionalAPI
  • The time taken to run x tests via the C++/CLI interface
  • The time taken to run x tests via P/Invoke

The Results

Below we have the results of running each test 10000 times:

Graph1.png

And the results of running each test 100000 times:

Graph2.png

Conclusion

Certainly not what I would have expected. According to my research, I was expecting to see the C++/CLI interface be at least an order of magnitude faster - as it does less error checking than a P/Invoke call. Even in the case of a million function calls, the C++/CLI interface is barely faster than using P/Invoke.

As we would expect, the cost of calling any native function from a CLI application is very high if we are calling it many times - in the case of using a very talkative API, it may even be worth writing a second C++ API that takes an aggregated set of parameters and calls the functions many times - the managed to unmanaged boundary is expensive.

Further Research

Has anyone found a case where a C++/CLI wrapper really does give a solid performance boost? Is there another way to do this that I have overlooked? Please provide any suggestions and I will update the article as necessary.

License

This article, along with any associated source code and files, is licensed under The Code Project Open License (CPOL)

About the Author

Dave Kerr

Software Developer
7Layer Solutions
United Kingdom United Kingdom

Member

Follow on Twitter Follow on Twitter
Follow my blog at www.dwmkerr.com and find out about my charity at www.childrenshomesnepal.org.

Sign Up to vote   Poor Excellent
Add a reason or comment to your vote: x
Votes of 3 or less require a comment

Comments and Discussions

 
You must Sign In to use this message board. (secure sign-in)
 
Search this forum  
 FAQ
    Noise  Layout  Per page   
  Refresh
GeneralMy vote of 5 PinmemberSteppenwolfe4:34 22 Jan '12  
GeneralMy vote of 5 Pinmembermatthias Weiser5:39 19 Jan '12  
GeneralP/Invoke vs. C++/CLI interop PinmemberNov0x3:55 30 Sep '11  
SuggestionSecurity check is significantly affecting performance, and doubles are not blittable. PinPopularmemberAndreasSk11:29 19 Sep '11  
GeneralRe: Security check is significantly affecting performance, and doubles are not blittable. PinmemberDave Kerr11:44 19 Sep '11  
GeneralRe: Security check is significantly affecting performance, and doubles are not blittable. Pinmemberhfrmobile19:00 19 Sep '11  
GeneralRe: Security check is significantly affecting performance, and doubles are not blittable. PinmemberIsh790:28 20 Sep '11  
GeneralRe: Security check is significantly affecting performance, and doubles are not blittable. PinmemberDave Kerr0:35 20 Sep '11  
SuggestionRe: Security check is significantly affecting performance, and doubles are not blittable. PinmemberAndreasSk11:10 20 Sep '11  
GeneralRe: Security check is significantly affecting performance, and doubles are not blittable. PinmemberDave Kerr21:40 20 Sep '11  
GeneralRe: Security check is significantly affecting performance, and doubles are not blittable. PinmemberMember 17592240:56 3 Dec '11  
SuggestionWhat about the managed "equivalents"? [modified] Pinmembercwienands9:45 19 Sep '11  
GeneralRe: What about the managed "equivalents"? PinmemberDave Kerr11:05 19 Sep '11  
QuestionMany thanks for sharing PinmemberRoberto Guerzoni21:01 13 Sep '11  
AnswerRe: Many thanks for sharing PinmemberDave Kerr21:26 13 Sep '11  
GeneralMy vote of 5 PinmvpAspDotNetDev7:48 13 Sep '11  
Questionnice PinmemberCIDev3:51 13 Sep '11  
AnswerRe: nice PinmemberDave Kerr3:53 13 Sep '11  
QuestionCan't download source files... PinmemberGPUToaster™23:48 12 Sep '11  
AnswerRe: Can't download source files... PinmemberDave Kerr0:30 13 Sep '11  
GeneralMy vote of 5 PinmemberHaBiX22:48 12 Sep '11  
GeneralRe: My vote of 5 PinmemberDave Kerr23:09 12 Sep '11  
GeneralMy vote of 5 PinmemberPablo Aliskevicius22:14 12 Sep '11  
GeneralRe: My vote of 5 PinmemberDave Kerr22:22 12 Sep '11  
GeneralMy vote of 5 PinmemberToni Petrina19:38 12 Sep '11  

General General    News News    Suggestion Suggestion    Question Question    Bug Bug    Answer Answer    Joke Joke    Rant Rant    Admin Admin   

Use Ctrl+Left/Right to switch messages, Ctrl+Up/Down to switch threads, Ctrl+Shift+Left/Right to switch pages.

Permalink | Advertise | Privacy | Mobile
Web02 | 2.5.120517.1 | Last Updated 13 Sep 2011
Article Copyright 2011 by Dave Kerr
Everything else Copyright © CodeProject, 1999-2012
Terms of Use
Layout: fixed | fluid