LINUX.ORG.RU

Скорость пинг-понга - 40 тыс запросов/сек


1

3

Есть простейший клиент-сервер (TCP/IP). Попробовал погонять
пинг-понг (8 байт туда - 8 байт обратно, последовательный
блокирующий send/recv). Скорость получилась - порядка 40 тыс
обработанных запросов в секунду. При этом использовался
loopback-интерфейс (что, думаю, не принципиально). Мне эта
скорость (40 тыс запросов/сек) кажется низкой. Скорее всего
косяк - у меня, или такая скорость - нормальна?
Машина - обычный 2-ядерный десктоп 2800 Мгц (полуторалетней давности).

★★★★★

Ответ на: нормально от anonymous

сколько переключений контекста на один пинг-понг?

Как это померять? Я не сталкивался с таким.

pacify ★★★★★
() автор топика
Ответ на: комментарий от pacify

strace

можно не мерять, я посчитать: каждый системный вызов — 2 переключения (в ядро и обратно)

современное процессорное ядро как раз и тянет десятки тысяч переключений в секунду

anonymous
()
Ответ на: нормально от anonymous

Как сделать работу пинг-понга быстрее? Перейти на неблокирующее чтение-запись в сокет?

pacify ★★★★★
() автор топика
Ответ на: комментарий от pacify

Правильно выставить аффинити, затюнить сетевой драйвер (interrupt coalescing), перейти на raw-сокеты. Заодно почитать про основы функционирования ethernet, где расписываются времянки, и посчитать, сколько теоретически пакетов пройти может.

mv ★★★★★
()

на loopback вообще бессмысленно тестировать ИМХО,

и какой протокол используется, TCP или UDP?

Harald ★★★★★
()

40к пинг-понгов это 80к пакетов. AFAIK это где-то в пределах 100мб канала. Когда мы делали железку-измеритель и замыкали ее на себя 100мб кабелем, в секунду успевало пролетать по-моему как раз в районе 80к-100к пакетов длиной в пределах 50 байт со всеми заголовками, максимально «плотно». Только я не знаю, как ограничена пропускная способность лупбэка.

staseg ★★★★★
()
Ответ на: комментарий от anonymous

на лупбэке-то?

От TCP на лупбэке смысла чуть. Если за пределы локалхоста не выходить, то есть более подходящие IPC.

mv ★★★★★
()

блокируюемость выпили и сделай epoll

punya ★★
()
Ответ на: комментарий от Harald

и какой протокол используется, TCP или UDP?

Протокол используется TCP. Но я потом перепишу на UDP.

Просто надо для высоконагруженной системы понять - на какую мощность обмена можно рассчитывать.

P.S. Попробую изучить указанные выше по треду способы оптимизации ...

pacify ★★★★★
() автор топика
Ответ на: комментарий от maxcom

tcp_nodelay стоит?

В параметрах соединения - не указывал.
Используется только опция SO_REUSEADDR.

pacify ★★★★★
() автор топика
Ответ на: комментарий от pacify

Поставь, в зависимости от того как у тебя устроен код записи может или ничего не измениться, или станет быстрее

maxcom ★★★★★
()
Ответ на: комментарий от anonymous

какая разница для передачи одного пакета туда и одного обратно?

большая, «наглый алгоритм» это в первую очередь удар по lattency, при маленьком размере данных его влияние велико

maxcom ★★★★★
()

частота общения между двумя локальными процессами так и небудет выще 70-100тыщ сообщений в секунду
больше уже нельзя - это ограничение на контент свитч (thread непомогут)

а зачем тебе большая частота общения ?
тебе для локальных или удаленных ?

для локальных лучше несделать
а для удаленных достаточно легко - просто сделай чтобы процессы непереключались

для локальных можно сделать по другому - неждать ответа - а накапливать данные на другую сторону- и изредка проверять непоступилили данные от другой стороны

кстати - именно изза этого технология микроядра - и нерулит

ae1234 ★★
()

Маловато. Я делал не пин понг, но десереализировал поток сообщений. Пол миллиона в секунду на loopback. Java

vertexua ★★★★★
()
Ответ на: комментарий от vertexua

пол миллиона - это явная буферизация

#include <stdio.h>                                                                                
#include <stdlib.h>                                                                                

main(){
    int i,j;                                                                         
    int fd1[2],fd2[2];                                                           

    pipe(fd1,0);pipe(fd2,0);
    if(!fork())
        for(j=0;j<1000000;j++){
            read (fd1[0],&i,sizeof(i));
            write(fd2[1],&i,sizeof(i));
    else
        for(j=0;i<1000000;j++){
            write(fd1[1],&i,sizeof(i));
            read (fd2[0],&i,sizeof(i));
            }                                                                      
    }

проверте простенькой прогой сколько у вас макс колво контент свитчей это прога далет около 1 (или 2 для двух потоков) миллиона контент свитчей

ae1234 ★★
()
Ответ на: комментарий от ae1234

Правого кудрявого кронштейна

anonymous
()
Ответ на: комментарий от ae1234
if(!fork())
        for(j=0;j<1000000;j++){
            read (fd1[0],&i,sizeof(i));
            write(fd2[1],&i,sizeof(i));
    else

У меня тут недавно товарищ форк-бомбу похожей ошибкой устроил.

Kosyak ★★★★
()
Ответ на: комментарий от ae1234

закрывающего кудрявого кронштейна в конструкторе for

bhfq ★★★★★
()
Ответ на: комментарий от Kosyak

У меня тут недавно товарищ форк-бомбу похожей ошибкой устроил

А вот ae1234 не допускает подобных опасных ляпов и поэтому попросту реализовал бесконечный цикл в ветке «else».

anonymous
()
Ответ на: комментарий от ae1234
#include <stdio.h>                                                                                
#include <stdlib.h>                                                                                

main(){
    int i,j;                                                                         
    int fd1[2],fd2[2];                                                           

    pipe(fd1,0);pipe(fd2,0);
    if(!fork())
        for(j=0;j<1000000;j++){
            read (fd1[0],&i,sizeof(i));
            write(fd2[1],&i,sizeof(i));
            }
    else
        for(j=0;i<1000000;j++){
            write(fd1[1],&i,sizeof(i));
            read (fd2[0],&i,sizeof(i));
            }                                                                      
    }

эт при копировании потерялась скобка - вот этот правильный вариант

ae1234 ★★
()
Ответ на: комментарий от anonymous

:) нашел нашел :) таки анонимусы иногда бывают правы

#include <stdio.h>                                                                           
#include <stdlib.h>                                                                          
                                                                                             
main(){                                                                                      
    int i,j;                                                                                 
    int fd1[2],fd2[2];                                                                       
                                                                                             
    pipe(fd1,0);pipe(fd2,0);                                                                 
    if(!fork())                                                                              
        for(j=0;j<1000000;j++){                                                              
            read (fd1[0],&i,sizeof(i));                                                      
            write(fd2[1],&i,sizeof(i));                                                      
            }                                                                                
    else                                                                                     
        for(j=0;j<1000000;j++){                                                              
            write(fd1[1],&i,sizeof(i));                                                      
            read (fd2[0],&i,sizeof(i));                                                      
            }                                                                                
    }                                                                                        
ae1234 ★★
()
Ответ на: комментарий от anonymous

/usr/bin/time --verbose ./a.out
Command exited with non-zero status 4
Command being timed: "./a.out"
User time (seconds): 0.21
System time (seconds): 5.34
Percent of CPU this job got: 64%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:08.65
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 1392
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 121
Voluntary context switches: 999664
Involuntary context switches: 65
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Exit status: 4

ae1234 ★★
()
Ответ на: комментарий от ae1234

Теперь поменяй команду создания пайпов на то, что внизу и померь снова:

pipe2(fd1,O_NONBLOCK);pipe2(fd2,O_NONBLOCK);

anonymous
()
Ответ на: комментарий от anonymous

/usr/bin/time --verbose ./a.out
Command exited with non-zero status 255
Command being timed: "./a.out"
User time (seconds): 0.04
System time (seconds): 0.36
Percent of CPU this job got: 99%
Elapsed (wall clock) time (h:mm:ss or m:ss): 0:00.40
Average shared text size (kbytes): 0
Average unshared data size (kbytes): 0
Average stack size (kbytes): 0
Average total size (kbytes): 0
Maximum resident set size (kbytes): 1392
Average resident set size (kbytes): 0
Major (requiring I/O) page faults: 0
Minor (reclaiming a frame) page faults: 121
Voluntary context switches: 1
Involuntary context switches: 1
Swaps: 0
File system inputs: 0
File system outputs: 0
Socket messages sent: 0
Socket messages received: 0
Signals delivered: 0
Page size (bytes): 4096
Exit status: 255

ae1234 ★★
()
Ответ на: комментарий от ae1234

Никаких переключений контекста, в 13 раз быстрее, раз в восемь меньше интегральное потребление CPU.

anonymous
()
Ответ на: комментарий от anonymous

ну ты вкурсе что там сплошные ошибки идет и 0 полезного действия ? чтоб было полезное действие - нужно проверку на ошибку делать - и тока правильные отрабатывать

#include <stdio.h>                                                              
#include <stdlib.h>                                                             
#include <fcntl.h>                                                              
                                                                                
main(){                                                                         
    int i,j;                                                                    
    int fd1[2],fd2[2];                                                          
                                                                                
    pipe2(fd1,O_NONBLOCK);pipe2(fd2,O_NONBLOCK);                                
    if(!fork())                                                                 
        for(j=0;j<1000000;j++){                                                 
            while(read (fd1[0],&i,sizeof(i))!=4);                               
            while(write(fd2[1],&i,sizeof(i))!=4);                               
            }                                                                   
    else                                                                        
        for(j=0;j<1000000;j++){                                                 
            while(write(fd1[1],&i,sizeof(i))!=4);                               
            while(read (fd2[0],&i,sizeof(i))!=4);                               
            }                                                                   
    }                                                                           

вот тут да - работа без контент свитча - лиш только куча сисколов и ожидание в работе поступления

но изначально то речь шла о скорости именно контент свитча а то что две нити могут проверять ячейку в памяти на фулл скорости работы памяти/кеша - это само собой понятно

ae1234 ★★
()
Ответ на: комментарий от ae1234

ну ты вкурсе что там сплошные ошибки идет и 0 полезного действия ? чтоб было полезное действие - нужно проверку на ошибку делать - и тока правильные отрабатывать

Значение, возвращаемое этими функциями нужно в любом случае обрабатывать, без разницы блокирующий дескриптор или не очень. Я, например, просто вычитываю дескриптор в цикле пока sizeof(int) не вычитается или ошибка не произойдёт, EAGAIN игнорирую.

но изначально то речь шла о скорости именно контент свитча

Ну так что не так? Эти два примера и показывают разницу между блокирующим дескриптором со свитчами и неблокирующим. Возможно так vertexua и получил указанную им производительность.

а то что две нити могут проверять ячейку в памяти на фулл скорости работы памяти/кеша - это само собой понятно

Не понял про нити и ячейку памяти. У тебя же процессы в примере использованы.

Кстати, не понял ещё одну вещь - твой Феном 3.7 ГГц выполняет оба примера в 3 (три!) раза быстрее чем мой Феном 3 ГГц... Как ты этого добился? (Если чо, у меня Fedora 64 bit, камень «AMD Phenom(tm) II X4 945 Processor»)

anonymous
()

http://search.cpan.org/perldoc?AnyEvent::FastPing

Performance: On my 2 GHz Opteron system with a pretty average nvidia gigabit network card I can ping around 60k to 200k addresses per second, depending on routing decisions.

Тестировал сам и получил, что пингануть 60+ тыс. хостов (маршутизаторы, коммутаторы) в реальной сети известного провайдера можно за 1 секунду.

gh0stwizard ★★★★★
()
Ответ на: комментарий от anonymous

имхо ты както нетак понимаеш эти примеры в моем примере - идут именно контент свитчи - один процесс пишет в пайп - а потом говорит ядру - ЖДИ данных от другого процесса - и засыпает

а в изменненом примере от тебя - один процесс пишет другому а потом начинает пинать ядро на предмет проверки не поступили ли данные - при этом этот процесс не засыпает и постоянно далет чертово колво сисколов

а ядро то просто по запросу - проверяет внутреннию структуру на предмет поступили ли данные в пайп - такчто

измененный пример в принципе равносилен такому

// gcc -lpthread -O2
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
#include <pthread.h>
#include <sys/time.h>

volatile int tester;

void* w2(void* p){
	int i;
	for(i=0;i<1000000;i++){
	    while(tester%2) ;
	    tester++;
	    }
	}
void* w1(void* p){
	int i;
	for(i=0;i<1000000;i++){
	    while(!(tester%2));
	    tester++;
	    }
	}

main(){
	long long int i;
	pthread_t p1,p2;

	tester=0;

	pthread_create(&p1,NULL,w1, (void*) i);
	pthread_create(&p2,NULL,w2, (void*) i);

	pthread_join(p1,NULL);

	}

time ./a.out

real 0m0.197s

user 0m0.393s

sys 0m0.000s

ae1234 ★★
()
Ответ на: комментарий от gh0stwizard

тестировали прогу на Си - насчет маскимального забивания гигабитного канала мелкими пакетами
получилсоь что на один пакет уходит гдето 6микросекунд - даже с хитрыми ухищрениями типа массовый вызов сисколов прямо из ядра

и как другие подсказывают - есть проект - суть которого руление напрямую сетевой из юзерлевелного пространства - и там эта величина уменьшаеться в несколько раз

ae1234 ★★
()
Ответ на: комментарий от ae1234

Можно вообще без задержек/ожиданий, но тогда ловите ошибки типа ENOBUFS:

The output queue for a network interface was full. This generally indicates that the interface has stopped sending, but may be caused by transient congestion. (Normally, this does not occur in Linux. Packets are just silently dropped when a device queue overflows.)

http://www.kernel.org/doc/man-pages/online/pages/man2/sendto.2.html

gh0stwizard ★★★★★
()
Ответ на: комментарий от gh0stwizard

неа - в том и дело
в один поток - 60 байтными пакетами гигабит загрузить получаеться лиш на 10% - 10 мегобайт в секунду - 166тон пакетов в секунду
тут небывает ENOBUFS - в каждом сисколе - пакет сразу отправляеться в железо сетевки

ae1234 ★★
()
Вы не можете добавлять комментарии в эту тему. Тема перемещена в архив.