Вопрос по udp, winsock, multicast, performance – Верхний предел производительности UDP на Windows Server 2008

2

Похоже, что из моего тестирования я попал в стену производительности в моей сети 10 ГБ. Кажется, я не могу читать более 180-200k пакетов в секунду. Глядя на perfmon или диспетчер задач, я могу получать до миллиона пакетов в секунду, если не больше. Тестирование 1 сокета или 10 или 100, похоже, не изменяет этот предел в 200-300 тыс. Пакетов в секунду. Я безуспешно возился с RSS и тому подобным. Unicast против многоадресной рассылки, кажется, не имеет значения, перекрытие ввода-вывода против синхронного также не имеет значения. Размер пакета также не имеет значения. Кажется, существует жесткое ограничение на количество пакетов, которые окна могут копировать из буфера в ник. Это dell r410. Есть идеи?

<code>#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;
}
</code>

Я использую 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 или около того, не сбрасывая (8 МБ буфер на стороне клиента). Если я делаю tcp, я могу делать около 200 тыс. Пакетов в секунду. Вот что интересно, хотя - я могу сделать намного больше, чем 200 КБ с несколькими соединениями TCP, но несколько соединений UDP не увеличивают общее количество (я тестирую производительность UDP с несколькими iperfs, так как один iperf с несколькими потоками, кажется, не работает ). Все аппаратное ускорение настроено в драйверах. Кажется, производительность УДП просто ниже нормы?

Я бы не назвал "200-300k" «жесткий лимит»; совсем. Кажется очень "мягким". Andrew Barber
Что именно вы имеете в виду под «множественными UDP-соединениями»? Несколько процессов отправляют на один IP-адрес или что-то еще? jhonkola
Да, несколько исходящих потоков на одном хосте. Вот кое-что, что я нашел с многоадресной рассылкой, что любопытно. Когда я отправляю на один и тот же IP-адрес назначения, но на разные порты, подписывающая машина использует только один процессор (на процессор выводится 90% + процессор, потраченный на ядро). Если я переключаюсь на несколько IP-адресов многоадресной рассылки, то подписывающая машина начинает использовать несколько процессоров для обработки пакетов. Мои очереди rss установлены на 4, так что ясно, что функция хеширования nic каким-то образом нарушена. Если я делаю одноадресную передачу UDP на хост, у меня такое же поведение (использует только один процессор). TCP работает нормально, и используются несколько процессоров. Yves Lauren

Ваш Ответ

1   ответ
2

поскольку я исследую прирост производительности, который можно получить при использованииWinsock Зарегистрированные расширения сети ввода-вывода, RIOв Windows 8 Server. Для этого я проводил тесты на Windows Server 2008 R2 и Windows Server 8.

Я еще не дошел до того, что начал тестирование с нашими картами 10 Гбит (они только что прибыли), но результаты моих предыдущих тестов и примеры программ, использованных для их запуска, можно найтиздесь, в моем блоге.

Одна вещь, которую я мог бы предложить, состоит в том, что с помощью простого теста, подобного тому, который вы показываете, когда очень мало работы выполняется с каждой дейтаграммой, вы можете обнаружить, что старомодный синхронный ввод-вывод быстрее, чем проект IOCP. В то время как проект IOCP идет вперед как рабочая нагрузка на дейтаграмму возрастает, и вы можете полностью использовать несколько потоков.

Кроме того, ваши тестовые машины подключены друг к другу (т.е. без коммутатора) или они работают через коммутатор; Если да, может ли проблема быть связана с производительностью вашего коммутатора, а не тестовых компьютеров? Если вы используете коммутатор или у вас несколько серверов на сервере, можете ли вы запустить несколько клиентов на сервере, может ли проблема быть на клиенте, а не на сервере?

Какое использование процессора вы видите на отправляющей и принимающей машинах? Вы смотрели на использование процессора в Process Explorer? Это точнее, чем диспетчер задач. Какой процессор обрабатывает нормальные прерывания, можете ли вы улучшить ситуацию, связав их с другим процессором? или измените сродство вашей тестовой программы для запуска на другом процессоре? Ваш пример IOCP распространяет свои потоки по нескольким узлам NUMA или вы все их привязываете к одному узлу?

Я надеюсь провести еще несколько тестов на следующей неделе и обновлю свой ответ, когда я это сделаю.

Edit: Для меня проблема была связана с тем, что драйверы NIC имели «управление потоком». включен, и это заставило отправителя работать со скоростью получателя. Это имело некоторый нежелательный «пейджинг без пейджинга»; характеристики использования и отключение управления потоком позволяют увидеть, насколько быстро может отправитель отправиться (и разница в использовании сети между отправителем и получателем ясно показывает, сколько данных теряется). Смотрите мой блогВот Больше подробностей.

Попробуйте отключить генерацию контрольной суммы udp на сетевых адаптерах send и recv.
Отправитель использует несколько потоков, а perfmon показывает количество пакетов в секунду, приближающееся к 1 миллиону в секунду. Я сравниваю количество отправленных пакетов с количеством полученных, поэтому отправитель имеет большую емкость, чем получатель. У меня есть другой тест, который также включает порядковый номер, который подтверждает, что клиент не может обрабатывать все пакеты. Yves Lauren
Я нахожу ограничение, аналогичное генерации дейтаграмм UDP. Интересно, что, кажется, будет меньше, если что-то находится на другом конце, обрабатывающем дейтаграммы, чем если ничего не слушает, хотя дейтаграммы все еще, кажется, попадают в провод в обоих случаях. При использовании ProcExplorer похоже, что ограничение связано с генерируемыми прерываниями, насыщающими один процессор, но я могу ошибаться ...
Можете ли вы использовать несколько отправителей? Может ли узкое место быть у отправителя?
Привет, Лен, спасибо за ваш ответ. Я не заметил никакой разницы в производительности между IOCP и синхронным. Машины проходят высокопроизводительный коммутатор Cisco, и я был бы удивлен, если бы проблема была в коммутаторе. Я не изменил настройки NUMA на моих сетевых адаптерах Intel, настройку «Предпочитаемый узел NUMA» is & quot; System Default & quot ;. Я не вижу никаких других настроек. В зависимости от теста, CPU на отправителе составляет 10-15%, на получателе обычно 10-30% при выполнении многоадресных тестов. Yves Lauren

Похожие вопросы