使用托管内存进行已注册I/O RIOSend函数调用

本文关键字:注册 函数调用 RIOSend 内存 | 更新日期: 2023-09-27 17:57:52

我正在开发一个需要高速、低延迟/抖动通信的系统,它是用C#编写的。我们看到有一种新的机制可以通过名为Windows注册IO的套接字获得更好的性能http://www.serverframework.com/asynchronousevents/2011/10/windows-8-registered-io-networking-extensions.html

但目前C#不可用。。。并且基于C++中的测试,它肯定显示出更好的性能。

无论如何,我已经编写了Managed C++/CLI部分来创建DLL,这似乎可以很好地使用C#和大大改进的数字。

但我想知道我是否可以在发送时保存一个缓冲副本。。。

目前的C++包装器是这样的:

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 );
}

其中,_RIObs是实现Registered I/O功能的非托管C++对象。在该函数中,它会将数据复制到启动时用RIORegisterBuffer()注册的缓冲区中,然后调用RIOSend()通知操作系统有数据要发送。

因此,我的问题是,我是否可以让这个托管C++对象的用户在启动时传入一个托管字节数组,并在其上调用GCHandle::Alloc()以将其修复到位并防止垃圾收集,在托管应用程序中对其调用RIORegisterBuffer(),然后在托管C++对象处于活动状态时使用它?我会注册托管缓冲区内存,而不是非托管内存,当用户想要发送数据时,他们会填充他们已经有引用的缓冲区,并通知我他们想要发送多少字节。这是一个阻塞发送并等待完成事件,所以在操作系统完成数据处理之前,他们不会再次使用缓冲区。

似乎只要非托管应用程序可以交替使用托管内存或非托管内存,只要托管内存被锁定,直到不再需要它,它就可以工作。

使用托管内存进行已注册I/O RIOSend函数调用

您可以直接从C#调用RIO,而不是通过C++/CLI;分配一个大于85k的大缓冲区,这将把它移到LoH中,而不是干扰常规GC,然后你可以使用类似于你建议的:

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

我已经制作了一个示例http echo服务器,它使用到已注册IO的直接互操作,并对其性能进行了一些分析(非常好)。

这听起来是个不错的策略。只需确保在创建GCHandle时使用GCHandle::Pined即可确保内存不会移动。