Производительность пинг-понга - PullRequest
4 голосов
/ 14 мая 2010

Я написал две простые программы (пробовал на C ++ и C #).

Это псевдокод:

-------- Клиент ---------------

for(int i = 0; i < 200.000; i++)  
{  
    socket_send("ping")  
    socket_receive(buff)  
}  

--------- Сервер -------------

while(1)  
{  
    socket_receive(buff)  
    socket_send("pong")  
}  

Я попробовал это на Windows.

Время выполнения клиента составляет около 45 секунд. Может кто-нибудь объяснить мне, почему это занимает так много времени? Я понимаю, что если бы существовало реальное сетевое соединение между клиентом и сервером, время одного пинг-понга было бы: generate_ping + send_via_network + generate_pong + send_via_network но здесь все делается в «локальном» режиме.

Есть ли способ ускорить пинг-понг между процессами с помощью сетевых сокетов (например, я не спрашиваю об общей памяти :))

Ответы [ 3 ]

2 голосов
/ 16 мая 2010

Может ли это быть проблемой с орлом? Вы отправляете очень маленькие пакеты, а затем сразу же ждете ответа. Стек TCP будет зависать от данных некоторое время, пока он не будет уверен, что вы больше не будете отправлять данные. Установка параметра TCP_NODELAY может сделать его быстрее.

Что меня озадачивает в этой гипотезе, так это то, почему никто больше ее не видит и почему те же эффекты не видны в Linux (о чем я практически ничего не знаю). Я действительно знаю, что стек TCP в Windows почти наверняка не в 9 раз медленнее, чем стек в Linux - кто-то заметил бы.

1 голос
/ 14 мая 2010

Является ли ваша машина однопроцессорной? Затем требуется полное переключение задач после каждой отправки. Это занимает время.

Даже если ваша машина является многопроцессорной, сетевой стек должен использовать блокировки / мьютексы, чтобы гарантировать, что только один процесс попытается обновить определенные внутренние структуры данных одновременно. Другие процессы, пытающиеся установить связь, должны будут ждать, например, в спин-петле для освобождения мьютекса. Это требует времени. (Хорошая реализация сетевого стека вызовет минимальные помехи между запросами на связь в разных процессах, которые не зависят друг от друга - но, очевидно, это не так!)

Также сетевой код почти наверняка находится в ядре ОС, что требует переключения уровней защиты ЦП при входе и выходе. Это занимает время.

(Я предполагаю, что вы имеете в виду «200 000», как в «двухсот тысячах», а не «200 000», как в «двухстах, с 3-мя цифрами избыточной точности после десятичного знака». Я понимаю значения , и . поменялись местами на некоторых языках относительно моего понимания, просто убедившись.)

Наконец, действительно ли латентность (что именно вы здесь измеряете) действительно так важна для вас? Я ожидаю, что вы найдете пропускную способность в порядке - то есть вы могли бы передавать гораздо больше данных (конечно, я ожидал бы до 1 страницы виртуальной памяти, обычно около 4 КБ) за отправку, не затрачивая гораздо больше времени.

0 голосов
/ 14 мая 2010

Вот примерный код производителя в C #:

using System;
using System.Net;
using System.Net.Sockets;


namespace Producer
{    
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length != 2)
            {
                Console.WriteLine("Usage:");
                Console.WriteLine("Producer.exe <server_ip> <port>");
                return;
            }
            string ipAddress = args[0];
            string port = args[1];

            Console.WriteLine("IpAddress: " + ipAddress);
            Console.WriteLine("Port     : " + port);

            Socket s = new Socket(AddressFamily.InterNetwork, 
                           SocketType.Stream, ProtocolType.Tcp);
            s.Connect(new IPEndPoint(IPAddress.Parse(ipAddress), 
                           int.Parse(port)));

            Console.WriteLine("Press key to start pinging");
            Console.ReadKey();

            byte[] buff = new byte[1];
            Console.WriteLine("Pinging started");
            DateTime start = DateTime.Now;

            for (int i = 0; i < 200000; i++)
            {
                s.Send(buff);
                s.Receive(buff, 1, SocketFlags.None);
            }
            DateTime end = DateTime.Now;
            Console.WriteLine("Pinging finished in: " 
                               + (end - start).TotalSeconds);

            Console.ReadKey();
        }
    }
}

А вот сервер в C #:

using System;
using System.Net;
using System.Net.Sockets;

namespace Server
{
    class Program
    {
        static void Main(string[] args)
        {
            if (args.Length < 1)
            {
                Console.WriteLine("Usage:");
                Console.WriteLine("Server.exe <port>");
                return;
            }

            string port = args[0];

            //IPHostEntry ipHostInfo = Dns.Resolve(Dns.GetHostName());
            //IPAddress ipAddress = ipHostInfo.AddressList[0];
            IPAddress ipAddress = IPAddress.Parse("127.0.0.1");
            IPEndPoint m_LocalEndPoint = new IPEndPoint(ipAddress, 
                                                        int.Parse(port));

            try
            {
                // Create a TCP/IP socket.
                Socket s = new Socket(AddressFamily.InterNetwork, 
                               SocketType.Stream, ProtocolType.Tcp);
                s.Bind(m_LocalEndPoint);
                s.Listen(10);

                Console.WriteLine("IpAddress: " + ipAddress.ToString());
                Console.WriteLine("Port     : " + port);

                byte[] buff = new byte[1];

                Socket s2 = s.Accept();
                Console.WriteLine("connected");

                while (true)
                {
                    s2.Receive(buff, 1, SocketFlags.None);
                    s2.Send(buff);
                }
            }
            catch( Exception )
            {
            }
        }
    }
}

А вот вывод от производителя (работает на Windows):

Producer.exe 127.0.0.1 667
IpAddress: 127.0.0.1
Port     : 667
Press key to start pinging
nPinging started
Pinging finished in: 46,617032

У меня очень похожие результаты для программы, написанной на C ++ с использованием winsock2 (код похож на сокеты Linux ниже)

То же самое написано на C и запущено в Linux:

Производитель:

#include <stdio.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <time.h>

int main(int argc, char** argv)
{
  int sockfd, portno, n;
  struct sockaddr_in serv_addr;
  struct hostent *server;
  char buff[1];
  time_t start, end;
  int i;

  if (argc != 3)
  {
  printf("Usage:\n");
  printf("%s <server_ip> <port>\n", argv[0]);
  return;
  }

  char* ipAddress = argv[1];
  char* port = argv[2];

  printf("IpAddress: %s\n", ipAddress);
  printf("Port     : %s\n", port);

  portno = atoi(port);
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) 
  printf("error - socket open\n");

  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = inet_addr(ipAddress);
  serv_addr.sin_port = htons(portno);
  if(0 > connect(sockfd,&serv_addr,sizeof(serv_addr)))
printf("error - connect\n");

  start = time(0);
  printf("ping-pong started\n");
  for(i = 0; i < 200000; i++)
  {
write(sockfd,buff,1);
read(sockfd,buff,1);
  }
  end = time(0);
  printf("finished in %d secs\n", end - start);

  return 0;
}

и сервер: #включают #включают #включают #включают #include

int main(int argc, char** argv)
{
  int sockfd, newsockfd, portno, clilen;
  char buffer[1];
  struct sockaddr_in serv_addr, cli_addr;
  int n;

  if (argc < 2)
  {
printf("Usage:\n");
printf("%s <port>\n", argv[0]);
return;
  }

  portno = atoi(argv[1]);
  sockfd = socket(AF_INET, SOCK_STREAM, 0);
  if (sockfd < 0) 
  printf("error - socket open\n");

  memset(&serv_addr, 0, sizeof(serv_addr));
  serv_addr.sin_family = AF_INET;
  serv_addr.sin_addr.s_addr = INADDR_ANY;
  serv_addr.sin_port = htons(portno);


  if (0 > bind(sockfd, (struct sockaddr *) &serv_addr, sizeof(serv_addr))) 
printf("error - bind\n");

  listen(sockfd,5);
  clilen = sizeof(cli_addr);
  printf("waiting for connection...\n");
  newsockfd = accept(sockfd, (struct sockaddr *) &cli_addr, &clilen);
  if (newsockfd < 0) 
printf("error - accept\n");

  printf("connected\n");

  while(1)
  {
read(newsockfd,buffer,1);
write(newsockfd,buffer,1);
  }  
  return 0;
}

Вывод от производителя (Linux):

$ ./a.out 127.0.0.1 666
IpAddress: 127.0.0.1
Port     : 666
ping-pong started
finished in 5 secs

Моя машина - Intel Core Duo

Что происходит в Windows ??

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...