views:

56

answers:

2

What can be done to speed up calling native methods from managed code?

I'm writing a program which needs to be able to manage arbitrarily-sized lists of objects and retrieve information from them at high speed, which it feeds into scripts. Scripts are bits of compiled C# code. I'm writing a basic interface layer from the C++ (native) DLL/SO/etc to the C# (.Net or Mono) management layer.

Now, I've been doing some testing, and I've found that on average, PInvoking a native method from managed code is something like 100 times slower than doing it all in managed (all native and all managed are identically fast, for reference).

The syntax I was using is:

[DllImport("test.dll")]
extern static public String test_method(String value);

String returnedValue = test_method("hello world");

Is there a way to cache a pointer to the function, some code of fast invoker, that would increase speed after loading the native library? That would solve the problem quite neatly, so I doubt it exists. :P

Edit: I didn't specify, but this needs to work on Windows, Linux (Ubuntu at least) and Mac OS X, all for both x86 and x64. Otherwise I would've gone with a C++/CLI interface and been done with it, but unless that works for all 3 platforms, I can't use it.

+2  A: 

Perhaps the string marshalling is what is causing a slowdown. For comparison sake, try to profile a function that takes and returns elementary C++ types like int.

You can also try to experiment with C++/CLI. That way you can take explicit control over the marshalling and maybe see an improvement.

In C++/CLI assembly:

System::String ^ test_method(System::String ^ args)
{
    pin_ptr<const wchar_t> pp = PtrToStringChars(args);
    //This might leak, probably needs a smart pointer to wrap it
    wchar_t* ret = native_method(pp);
    return gcnew String^(ret);
}
Igor Zevaka
I should have specified, but unless that'll work for Linux and Macs, it's unfortunately out of the question. My first choice would've been a C++/CLI interface, so your answer is right as I stated the question but I forgot the specify cross-platform and haven't seen any way to do that for other OSes than Windows. :\
peachykeen
Bugger, that definitely rules out C++/CLI.
Igor Zevaka
+2  A: 

Further to my question comment, we've established that it was a debug build with the debugger attached. This has a massive impact on runtime performance of .Net code. Easy mistake to make. :)

I'm guessing with a release build and no debugger attached, the performance difference is now much more reasonable.

If you have a very chatty API, and the native methods being called are cheap, then method call overhead can be a performance issue. Try and design a less chatty API. This is a typical technique used to increase the performance of boundary\systems communications.

If performance is acceptable after sorting the debugger issue, there is a simple technique that I have used to easily get a substantial performance increase in chatty APIs, by just adding a single attribute.

In the classes where you have your imported functions (i.e. the DllImport functions), place the SuppressUnmanagedCodeSecurity attribute on the classes. This will remove some expensive security checking from each P/Invoke call. Please see the documentation on SuppressUnmanagedCodeSecurity to understand the ramifications of this. I tend to keep my imported functions grouped together in internal classes (that only contain imported functions) with this attribute applied.

chibacity
Oddly, a release build (on all sides) with no debugger has the same 1:100 performance. I was trying to test the interop overhead, so I designed a function that compiles down to 4/5 asm instructions (one cmp, one jmp, two mov and one ret).As for creating a less chatty API, it looks like that's what I'll be doing. Something akin to a driver's minimal state changes, buffering some information, possibly writing to a shared buffer in unmanaged memory and calling a sync function occasionally. Thanks for the security tip.
peachykeen