Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Using managed memory for Registered I/O RIOSend function call

I'm working on a system that needs high speed, low latency/jitter communication and it's written in C#. We saw there is a new mechanism to get better performance with sockets called Windows Registered IO http://www.serverframework.com/asynchronousevents/2011/10/windows-8-registered-io-networking-extensions.html

...But not available to C# at the moment ...and it definitely seems to show better performance based on testing in C++.

Anyway, I've written the Managed C++/CLI part already to create a DLL, and that seems to work fine with C# and much improved numbers.

But I'm wondering if I can save a buffer copy with the sending...

Currently the C++ wrapper looks like this:

bool ManagedSendData( array<byte> ^ buf, ULONG bufLen )
{
// must pin array memory until we're done with sending it, 
// at which point it'll be copied into the unmanaged memory

   pin_ptr<Byte> p = &buf[0];   // pinning one element of an array pins the whole array
   unsigned char * cp = p;

   return _RIObs->SendData( cp, bufLen );
}

Where _RIObs is the unmanaged C++ object that implements the Registered I/O functionality. In that function, it will copy the data into the buffer that was registered at the startup with RIORegisterBuffer(), and then call RIOSend() to notify the OS that there is data to send.

So, my question is can I have the user of this Managed C++ object pass in a managed byte array on startup and call GCHandle::Alloc() on it to fix it in place and prevent garbage collection, call RIORegisterBuffer() on it in the managed application, and then use it while the managed C++ object is alive? I will register that managed buffer memory instead of unmanaged memory, and when the user wants to send data, they will fill up the buffer they already have a reference to, and just notify me how many bytes they want to send. It's a blocking send and waits for a completion event, so they wouldn't be using the buffer again until the OS is done it's thing with the data.

It seems it would work as long as unmanaged applications can use managed or unmanaged memory interchangeably as long as the managed memory is locked down until it's not longer needed.

like image 650
Peter Avatar asked Oct 06 '22 03:10

Peter


1 Answers

You can call RIO directly from the C# rather than going via C++/CLI; allocate a large buffer > 85k which will move it into the LoH rather than interfering with regular GC and then you can use similar to what you are suggesting:

_underlyingBuffer = new byte[BufferLength];
var pinnedBuffer = GCHandle.Alloc(_underlyingBuffer, GCHandleType.Pinned);
var address = Marshal.UnsafeAddrOfPinnedArrayElement(_underlyingBuffer, 0);
var bufferId = _rio.RegisterBuffer(address, BufferLength);

I've put together a sample http echo server using direct interop to Registered IO and some analysis of how it performs (very well).

like image 122
Ben Adams Avatar answered Oct 10 '22 03:10

Ben Adams