2

从我的测试来看,我的 10gb 网络似乎遇到了性能障碍。我似乎无法每秒读取超过 180-200k 个数据包。查看 perfmon 或任务管理器,如果不是更多,我每秒最多可以接收一百万个数据包。测试 1 个套接字或 10 个或 100 个,似乎并没有改变每秒 200-300k 个数据包的限制。我一直在摆弄 RSS 之类的东西,但没有成功。单播与多播似乎无关紧要,重叠 i/o 与同步也没有区别。数据包的大小也无关紧要。窗口可以从网卡复制到缓冲区的数据包数量似乎有一个硬性限制。这是戴尔 r410。有任何想法吗?

#include "stdafx.h"

#include <WinSock2.h>
#include <ws2ipdef.h>

static inline void fillAddr(const char* const address, unsigned short port, sockaddr_in &addr)
{
    memset( &addr, 0, sizeof( addr ) );
    addr.sin_family = AF_INET;
    addr.sin_addr.s_addr = inet_addr( address );
    addr.sin_port = htons(port);
}

int _tmain(int argc, _TCHAR* argv[])
{
#ifdef _WIN32
    WORD wVersionRequested;
    WSADATA wsaData;
    int err;

    wVersionRequested = MAKEWORD( 1, 1 );

    err = WSAStartup( wVersionRequested, &wsaData );
#endif
    int error = 0;
    const char* sInterfaceIP = "10.20.16.90";
    int nInterfacePort = 0;

    //Create socket
    SOCKET m_socketID = socket( AF_INET, SOCK_DGRAM, IPPROTO_UDP );

    //Re use address
    struct sockaddr_in addr;
    fillAddr( "10.20.16.90", 12400, addr ); //"233.43.202.1"

    char one = 1;
    //error = setsockopt(m_socketID, SOL_SOCKET, SO_REUSEADDR , &one, sizeof(one));
    if( error != 0 )
    {
        fprintf( stderr, "%s: ERROR setsockopt returned %d.\n", __FUNCTION__, WSAGetLastError() );
    }

    //Bind
    error = bind( m_socketID, reinterpret_cast<SOCKADDR*>( &addr ), sizeof( addr ) );

    if( error == -1 )
    {
        fprintf(stderr, "%s: ERROR %d binding to %s:%d\n",
            __FUNCTION__, WSAGetLastError(), sInterfaceIP, nInterfacePort);
    }

    //Join multicast group
    struct ip_mreq mreq;
    mreq.imr_multiaddr.s_addr = inet_addr("225.2.3.13");//( "233.43.202.1" );
    mreq.imr_interface.s_addr = inet_addr("10.20.16.90");

    //error = setsockopt( m_socketID, IPPROTO_IP, IP_ADD_MEMBERSHIP, reinterpret_cast<char*>( &mreq ), sizeof( mreq ) );

    if (error == -1)
    {
        fprintf(stderr, "%s: ERROR %d trying to join group %s.\n", __FUNCTION__, WSAGetLastError(), "233.43.202.1"  );
    }

    int bufSize = 0, len = sizeof(bufSize), nBufferSize = 10*1024*1024;//8192*1024;

    //Resize the buffer
    getsockopt(m_socketID, SOL_SOCKET, SO_RCVBUF, (char*)&bufSize, &len );
    fprintf(stderr, "getsockopt size before %d\n", bufSize );


    fprintf(stderr, "setting buffer size %d\n", nBufferSize );

    error =  setsockopt(m_socketID, SOL_SOCKET, SO_RCVBUF,
        reinterpret_cast<const char*>( &nBufferSize ), sizeof( nBufferSize ) );
    if( error != 0 )
    {
        fprintf(stderr, "%s: ERROR %d setting the receive buffer size to %d.\n",
            __FUNCTION__, WSAGetLastError(), nBufferSize );
    }

    bufSize = 1234, len = sizeof(bufSize);
    getsockopt(m_socketID, SOL_SOCKET, SO_RCVBUF, (char*)&bufSize, &len );
    fprintf(stderr, "getsockopt size after %d\n", bufSize );

    //Non-blocking
    u_long op = 1;
    ioctlsocket( m_socketID, FIONBIO, &op );

    //Create IOCP
    HANDLE iocp = CreateIoCompletionPort( INVALID_HANDLE_VALUE, NULL, NULL, 1 );
    HANDLE iocp2 = CreateIoCompletionPort( (HANDLE)m_socketID, iocp, 5, 1 );

    char buffer[2*1024]={0};

    int r = 0;

    OVERLAPPED overlapped; 
    memset(&overlapped, 0, sizeof(overlapped));

    DWORD bytes = 0, flags = 0;
//  WSABUF buffers[1];
//
//  buffers[0].buf = buffer;
//  buffers[0].len = sizeof(buffer);
//
//  while( (r = WSARecv( m_socketID, buffers, 1, &bytes, &flags, &overlapped, NULL )) != -121 )
    //sleep(100000);
    while( (r = ReadFile( (HANDLE)m_socketID, buffer, sizeof(buffer), NULL, &overlapped )) != -121 )
    {
        bytes = 0;
        ULONG_PTR key = 0;
        LPOVERLAPPED pOverlapped;

        if( GetQueuedCompletionStatus( iocp, &bytes, &key, &pOverlapped, INFINITE ) )
        {
            static unsigned __int64 total = 0, printed = 0;

            total += bytes;

            if( total - printed > (1024*1024) )
            {
                printf( "%I64dmb\r", printed/ (1024*1024) );
                printed = total;
            }
        }

    }

    while( r = recv(m_socketID,buffer,sizeof(buffer),0) )
    {
        static unsigned int total = 0, printed = 0;

        if( r > 0 )
        {
            total += r;

            if( total - printed > (1024*1024) )
            {
                printf( "%dmb\r", printed/ (1024*1024) );
                printed = total;
            }
        }
    }

    return 0;
}

我使用 Iperf 作为发送方,并将接收到的数据量与发送的数据量进行比较: iperf.exe -c 10.20.16.90 -u -P 10 -B 10.20.16.51 -b 1000000000 -p 12400 -l 1000

编辑:对 iperf 执行 iperf 性能接近 180k 左右而不会下降(8mb 客户端缓冲区)。如果我在做 tcp,我可以做大约 200k 个数据包/秒。不过有趣的是 - 我可以使用多个 tcp 连接完成超过 200k 的操作,但是多个 udp 连接不会增加总数(我使用多个 iperfs 测试 udp 性能,因为具有多个线程的单个 iperf 似乎不起作用)。所有硬件加速都在驱动程序中进行了调整。似乎 udp 性能只是低于标准?

4

1 回答 1

2

我一直在使用类似的硬件进行一些 UDP 测试,因为我研究了在 Windows 8 Server 中使用Winsock 注册 I/O 网络扩展 RIO可以获得的性能提升。为此,我一直在 Windows Server 2008 R2 和 Windows Server 8 上运行测试。

我还没有开始使用我们的 10Gb 卡进行测试(它们才刚到货),但我之前的测试结果和用于运行它们的示例程序可以在我的博客上找到。

我可能会建议的一件事是,通过一个简单的测试,如您展示的对每个数据报所做的工作很少的测试,您可能会发现老式的同步 I/O 比 IOCP 设计更快。随着每个数据报工作负载的增加,IOCP 设计向前迈进了一步,您可以充分利用多个线程。

此外,您的测试机器是背靠背接线(即没有开关)还是通过开关运行?如果是这样,问题可能出在您的交换机性能而不是您的测试机器上吗?如果您使用的是交换机,或者服务器中有多个网卡,您可以针对服务器运行多个客户端,问题可能出在客户端而不是服务器上吗?

您在发送和接收机器上看到的 CPU 使用率是多少?您是否使用 Process Explorer 查看过机器的 cpu 使用情况?这比任务管理器更准确。哪个 CPU 正在处理 nic 中断,您可以通过将它们绑定到另一个 cpu 来改进吗?或更改您的测试程序的亲和力以在另一个 cpu 上运行?您的 IOCP 示例是将其线程分布在多个 NUMA 节点上,还是将它们全部锁定到一个节点?

我希望下周能够进行更多测试,并在完成后更新我的答案。

编辑:对我来说,问题是由于 NIC 驱动程序启用了“流控制”,这导致发送方以接收方的速度运行。这有一些不受欢迎的“非分页池”使用特性,关闭流量控制可以让您查看发送方的速度(发送方和接收方之间的网络利用率差异清楚地显示了丢失了多少数据)。有关更多详细信息,请参阅我的博客帖子。

于 2012-05-12T08:15:47.213 回答