Re[3]: Чтение из сети и обработка
От: qaz77  
Дата: 14.02.22 10:38
Оценка:
Здравствуйте, tryAnother, Вы писали:
A>    while(true)
A>    {
A>        const int packet_awailable = get_pending_count(sock);
A>        for (int i = 0; i < packet_awailable; ++i)
A>        {
A>            int nRead = recvfrom(sock, packet, PACKET_LEN, 0, NULL, NULL);
A>            _ASSERT(nRead == PACKET_LEN);
A>            stat.OnPacket(packet);
A>        }
A>        Sleep(20);
A>    }


Идея со Sleep(20) — так себе.
Поток не пробуждается сразу же по приходу хотя бы одного пакета.
Представьте, что пакет приходит в первую мс слипа, а мы все равно спим еще 19 мс.
Лучше для ожидания поступления данных использовать select.

Вторая проблема, если за время слипа придет слишком много пакетов, то возможна их потеря.
Т.к. у сокета внутренний буфер конечного размера.
Re[4]: Чтение из сети и обработка
От: tryAnother  
Дата: 14.02.22 10:49
Оценка:
Здравствуйте, qaz77, Вы писали:

Q>Идея со Sleep(20) — так себе.

Q>Поток не пробуждается сразу же по приходу хотя бы одного пакета.
Q>Представьте, что пакет приходит в первую мс слипа, а мы все равно спим еще 19 мс.
Q>Лучше для ожидания поступления данных использовать select.

такая актуальность и не требуется, обработка всех собранных за "такт" пакетов происходит пачкой, поток не прыгает в сон и не пробуждается без надобности
в случае обработки по приходу поток бы постоянно дергался (пакеты идут 20 К в секунду) полюсов не видно.

Q>Вторая проблема, если за время слипа придет слишком много пакетов, то возможна их потеря.

Q>Т.к. у сокета внутренний буфер конечного размера.

конечно, но с существенным запасом,
при потоке 20К в секунду за 20 мс придет 400 пакетов, а буфер у меня на ~4000
Отредактировано 14.02.2022 13:03 tryAnother . Предыдущая версия .
Re[5]: Чтение из сети и обработка
От: qaz77  
Дата: 14.02.22 15:23
Оценка:
Здравствуйте, tryAnother, Вы писали:
A>такая актуальность и не требуется, обработка всех собранных за "такт" пакетов происходит пачкой, поток не прыгает в сон и не пробуждается без надобности
A>в случае обработки по приходу поток бы постоянно дергался (пакеты идут 20 К в секунду) полюсов не видно.

Если много датаграм приходит в единицу времени, то ожидание по select не приведет к лишним пробуждениям потока.
Когда пробудились кушаем все, пока очередь пакетов не пустая.
На практике надо замерять производительность со Sleep и select.

Все-таки смущает хардкод константы 20 мс.
Допустим софт будет крутится на слабенькой машине и время манипуляции с принятым пакетом будет сильно больше...

Если это не массовый софт, а написанный для конкретной железки, то наверное и так норм.
Я вот пишу коробочный и даже сейчас в 2022 еще пентиум 3 у клиентов бывает.
Re[3]: Чтение из сети и обработка
От: Умака Кумакаки Ниоткуда  
Дата: 20.02.22 03:25
Оценка:
Здравствуйте, tryAnother, Вы писали:

вот это:

boost::bind(&async_server::handle_receive_from, this,
                ba::placeholders::error,
                ba::placeholders::bytes_transferred)
            );

надо сделать один раз, например в конструкторе, хендлер один и тот же, зачем он аллоцируется каждый раз?
нормально делай — нормально будет
Re[3]: Чтение из сети и обработка
От: Умака Кумакаки Ниоткуда  
Дата: 20.02.22 19:15
Оценка:
Здравствуйте, tryAnother, Вы писали:

A> ++seq_;

A> if(seq_ != seq)
A> printf("Sequence broken, recv %d expect %d, dif %d\n", seq, seq_, seq-seq_);


блэт, слона то я и не заметил, это UDP, алло, откуда здесь гарантия последовательности пакетов? и откуда гарантия последовательного вызова хендлеров в азио?
нормально делай — нормально будет
Re[4]: Чтение из сети и обработка
От: tryAnother  
Дата: 22.02.22 09:18
Оценка:
Здравствуйте, Умака Кумакаки, Вы писали:

УК>Здравствуйте, tryAnother, Вы писали:


УК>вот это:


УК>
УК>boost::bind(&async_server::handle_receive_from, this,
УК>                ba::placeholders::error,
УК>                ba::placeholders::bytes_transferred)
УК>            );
УК>

УК>надо сделать один раз, например в конструкторе, хендлер один и тот же, зачем он аллоцируется каждый раз?

допускаю что тут лишние аллокации, которых можно избежать, но 20К аллокаций в секунду не большая нагрузка
и синхронная версия по потерям ведет себя аналогично асинхронной, то есть дело не в этом.

а можно ли посмотреть на код где "асио не напрягаясь может хендлить на одном ядре на порядок больше пакетов" и интересно какой там pps и общая скорость.
Re[4]: Чтение из сети и обработка
От: tryAnother  
Дата: 22.02.22 09:23
Оценка:
Здравствуйте, Умака Кумакаки, Вы писали:

УК>Здравствуйте, tryAnother, Вы писали:


A>> ++seq_;

A>> if(seq_ != seq)
A>> printf("Sequence broken, recv %d expect %d, dif %d\n", seq, seq_, seq-seq_);


УК>блэт, слона то я и не заметил, это UDP, алло, откуда здесь гарантия последовательности пакетов? и откуда гарантия последовательного вызова хендлеров в азио?


гарантий никто и не обещал, это понятно, но сеть там точка точка, никого кроме железки и принимающей машины нет, и она работает хорошо, пакетам там теряться некуда.
вопрос в сравнении подходов к чтению:
— синхронный и асинхроный asio теряют пачки по единицам — десяткам пакетов от единиц до десятков в час в зависимости от загрузки машины,
— подход с чтением через sleep теряет единицы пакетов в сутки
Re[5]: Чтение из сети и обработка
От: Умака Кумакаки Ниоткуда  
Дата: 23.02.22 22:41
Оценка:
Здравствуйте, tryAnother, Вы писали:

A>гарантий никто и не обещал, это понятно, но сеть там точка точка, никого кроме железки и принимающей машины нет, и она работает хорошо, пакетам там теряться некуда.

рация работает на бронепоезде? речь не о потерях, а о порядке пакетов

A>вопрос в сравнении подходов к чтению:

A>- синхронный и асинхроный asio теряют пачки по единицам — десяткам пакетов от единиц до десятков в час в зависимости от загрузки машины,
A>- подход с чтением через sleep теряет единицы пакетов в сутки

ещё раз, текущий тест "потери пакетов" невалиден, инкремент счётчика тут не канает, если надо посчитать количество принятых пакетов — заведи vector<bool> например на миллион элементов, и засовывай ему по индексу, который равен id пришедшего пакета true, после отправки миллиона пакетов выждать паузу и посчитать, сколько в векторе true значений, если меньше миллиона, то есть потери.
нормально делай — нормально будет
Re[6]: Чтение из сети и обработка
От: tryAnother  
Дата: 24.02.22 09:45
Оценка: +1
Здравствуйте, Умака Кумакаки, Вы писали:

УК>Здравствуйте, tryAnother, Вы писали:


A>>гарантий никто и не обещал, это понятно, но сеть там точка точка, никого кроме железки и принимающей машины нет, и она работает хорошо, пакетам там теряться некуда.

УК>рация работает на бронепоезде? речь не о потерях, а о порядке пакетов

рация конечно на танке, но тест результаты теста показывают не перетасовку пакетов, а именно потерю
    Sequence broken, recv 15463 expect 15448, dif 15
    Sequence broken, recv 15490 expect 15472, dif 18
    Sequence broken, recv 15503 expect 15499, dif 4
    Sequence broken, recv 15516 expect 15512, dif 4
    Sequence broken, recv 15528 expect 15525, dif 3
    Sequence broken, recv 15744 expect 15723, dif 21


если бы проблемы была в потере порядка, то сообщения изобиловали как положительными так и отрицательными dif

A>>вопрос в сравнении подходов к чтению:

A>>- синхронный и асинхроный asio теряют пачки по единицам — десяткам пакетов от единиц до десятков в час в зависимости от загрузки машины,
A>>- подход с чтением через sleep теряет единицы пакетов в сутки

УК>ещё раз, текущий тест "потери пакетов" невалиден, инкремент счётчика тут не канает, если надо посчитать количество принятых пакетов — заведи vector<bool> например на миллион элементов, и засовывай ему по индексу, который равен id пришедшего пакета true, после отправки миллиона пакетов выждать паузу и посчитать, сколько в векторе true значений, если меньше миллиона, то есть потери.


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

еще какие предположения по невалидности теста? и почему он проходит в режиме с задержкой
Re[7]: Чтение из сети и обработка
От: Умака Кумакаки Ниоткуда  
Дата: 24.02.22 11:10
Оценка:
Здравствуйте, tryAnother, Вы писали:

A>еще какие предположения по невалидности теста? и почему он проходит в режиме с задержкой


я бы всё таки убрал аллокацию, возможно после тысяч аллокаций аллокатор решает компактифицировать кучу, или ещё какую оптимизацию провести, и на этот момент попадает передача пакета. Убери вообще класс calcstat, сделай просто свободную функцию OnPacket(const boost::system::error_code& error, std::size_t bytes_transferre), внутри неё статическую переменную int seq_ и передавай этот хендлер просто как есть, без оборачивания в функтор через bind
нормально делай — нормально будет
Re[3]: Чтение из сети и обработка
От: tryAnother  
Дата: 13.06.24 10:44
Оценка:
Прошло время и пришлось вернуться к этой задаче.

Дополнительные тесты показали. что дело было не в бобине asio, а в размере буфера чтения сокета, если для примеров с asio установить буфер как для read_periodic, то потерь пакетов не наблюдается, однако есть разница в загрузке ЦП, так в примерах с asio загрузка примерно раза в 2-3 выше по сравнению с read_periodic.

Однако теперь потребовалось реализовать чтение и на Linux.
Написал программку, которая разными способами вычитывает поток UDP пакетов.
Способы такие:

  1. ReadSync, простое синхронное чтение (receive_from) в цикле
  2. NoBlock (NoBlock with Sleep), сокет в неблокирующем режиме, читает пока есть данные, если данных нет, то или опять читает, или засыпает на 10 мс.
  3. ASync (ASync with Sleep) асинхронное чтение через async_receive_from, но в обработчике запускается синхронное чтение в неблокирующем режиме, пока не кончатся данные, потом опять async_receive_from, (или через задержку 10 мс)
  4. RecvMsg, на Linux реализован дополнительный способ чтения через recvmmsg.


Данные генерировались программой, там socket.send_to в цикле.

Тестировалось на Win10 и wsl (Ubuntu 22).
Для запуска на Linux нужно увеличить максимальный размер входного буфера командой
sudo sysctl -w net.core.rmem_max=4194304

Иначе идут постоянные потери.

Результаты сомнительной правдивости, тк при запуске чтения в разных режимах менялась и скорость отдачи пакетов, поэтому в результатах программы показывают разные скорости приема, а ошибок счетчика при этом нет. Но можно сравнить загрузку ЦП.


Так результаты:
                              |  Linux p/sec      || Win p/sec
----------------------------------------------------------------------              
                      ReadSync|  102603  cpu= 86% ||   66338  cpu= 42% 
                     Non Block|  257363  cpu= 99% ||   99270  cpu= 97% 
          Non Block with Sleep|  254976  cpu= 26% ||   87445  cpu=  1% 
                         ASync|  143197  cpu= 84% ||   74373  cpu= 59% 
              ASync with Sleep|  251303  cpu= 98% ||   81930  cpu= 74% 
                       RecvMsg|   87232  cpu= 84% |



Что видно:

В целом поток получается не большой, на localhost с пакетом 1024 байта получается 650 Mbits/sec на Windows, поэтому попробовал утилиту iperf3:
— сервер: iperf3 -s
— клиент: iperf3 -c 127.0.0.1 -u -b 0 -l 1024
Показывает схожие скорости на таком размере буфера.
Если размер пакета поставить по умолчанию (128 k) то скорость 25 Gbits/sec, при 50000 pps.
То есть видно что ограничение не в ширине канала, а в количестве системных вызовов.
По идее recvmmsg как раз и сделан чтобы оптимизировать этот вопрос, он читает много пакетов за раз, но почему то это не помогло.
Re[4]: Чтение из сети и обработка
От: reversecode google
Дата: 13.06.24 11:10
Оценка:

в asio уже есть все возможные примеры
не
надо накатать +105000 своих кривых

изучите уже asio до конца, что бы не заниматься велосипедо строением в улучшении
Re[5]: Чтение из сети и обработка
От: tryAnother  
Дата: 14.06.24 06:40
Оценка:
3 способа из примеров asio,
еще 2 эти же примеры с засыпанием, они работают лучше,
и еще 1 специфичный posix.

или есть еще какие то особенные примеры asio?
Re[6]: Чтение из сети и обработка
От: reversecode google
Дата: 14.06.24 10:21
Оценка:
бегло

1 тюнить буферы системе нет смысла если есть возможность из задать через настройки сокета
2 тюнить надо и исходящий буфер на сокете а не только входящий
3 recvmmsg не нужен, он есть в asio для линукса и для винды, надо всего лишь брать массив строк векторов итд, лимит 64
4 io_context run надо крутить не в одном потоке, а на максимум воркеров по доступным для системе реальных ядрах
5 под линуксом есть еще uring, если апи находится при компиляции
6 в asio есть еще и как минимум три вида сокетов на корутинах, где они в тестах
Re[7]: Чтение из сети и обработка
От: tryAnother  
Дата: 14.06.24 11:10
Оценка:
Здравствуйте, reversecode, Вы писали:

R>1 тюнить буферы системе нет смысла если есть возможность из задать через настройки сокета


настройки сокета позволяют увеличивать буфер только до некоторой границы установленной в OS,
для win дает установить хоть ГБ, а в linux по умолчанию верхняя граница 208 КБ, можно посмотреть командой
sysctl net.core.rmem_max
те на 50000 п/с заполняется за 4 мс, и идут потери, я в примере ставлю 4 МБ, хватает на 80 мс (больше гранулярности переключения потоков?)

R>2 тюнить надо и исходящий буфер на сокете а не только входящий


в целом можно, но у меня отдающая утилита только для тестов, пока в доступе нет реальной железки,
но проверил, при установке буфера в 4 МБ на первый взгляд ничего не поменялось.

R>3 recvmmsg не нужен, он есть в asio для линукса и для винды, надо всего лишь брать массив строк векторов итд, лимит 64


поискал в 85 версии буста, не нашел вызова recvmmsg, гугл говорит что есть сторонние патчи, но их не проверял
да и получается что recvmmsg дает не лучшие результаты

R>4 io_context run надо крутить не в одном потоке, а на максимум воркеров по доступным для системе реальных ядрах


да, это я понимаю.
но у меня задача скорее не получить максимальный поток, а лучшим образом читать заданный.
предполагается что передача будет в пределах 30000-60000 пакетов/с, и неблокирующее чтение со слипом показывает минимальное потребление ЦП

R>5 под линуксом есть еще uring, если апи находится при компиляции


спасибо, поробую посмотреть

R>6 в asio есть еще и как минимум три вида сокетов на корутинах, где они в тестах


корутины это не лишний слой абстракции над асинхронными вызовами?
они реально могут дать выигрыш производительности?
Re[8]: Чтение из сети и обработка
От: reversecode google
Дата: 14.06.24 11:52
Оценка:
Здравствуйте, tryAnother, Вы писали:

A>Здравствуйте, reversecode, Вы писали:


R>>1 тюнить буферы системе нет смысла если есть возможность из задать через настройки сокета


A>настройки сокета позволяют увеличивать буфер только до некоторой границы установленной в OS,

A>для win дает установить хоть ГБ, а в linux по умолчанию верхняя граница 208 КБ, можно посмотреть командой
A> sysctl net.core.rmem_max
A>те на 50000 п/с заполняется за 4 мс, и идут потери, я в примере ставлю 4 МБ, хватает на 80 мс (больше гранулярности переключения потоков?)

да, забыл что в линуксе оно еще и лимитом

R>>2 тюнить надо и исходящий буфер на сокете а не только входящий


A>в целом можно, но у меня отдающая утилита только для тестов, пока в доступе нет реальной железки,

A>но проверил, при установке буфера в 4 МБ на первый взгляд ничего не поменялось.

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

R>>3 recvmmsg не нужен, он есть в asio для линукса и для винды, надо всего лишь брать массив строк векторов итд, лимит 64


A>поискал в 85 версии буста, не нашел вызова recvmmsg, гугл говорит что есть сторонние патчи, но их не проверял

A>да и получается что recvmmsg дает не лучшие результаты

да спутал одну m, только патчи есть

https://lists.boost.org/Archives/boost/2023/01/253873.php
https://blog.cloudflare.com/how-to-receive-a-million-packets

R>>4 io_context run надо крутить не в одном потоке, а на максимум воркеров по доступным для системе реальных ядрах


A>да, это я понимаю.

A>но у меня задача скорее не получить максимальный поток, а лучшим образом читать заданный.
A>предполагается что передача будет в пределах 30000-60000 пакетов/с, и неблокирующее чтение со слипом показывает минимальное потребление ЦП

я совсем не понял что это значит

R>>5 под линуксом есть еще uring, если апи находится при компиляции


A>спасибо, поробую посмотреть


если нет смысла получить максимальный, то нет смысла и смотреть

R>>6 в asio есть еще и как минимум три вида сокетов на корутинах, где они в тестах


A>корутины это не лишний слой абстракции над асинхронными вызовами?

A>они реально могут дать выигрыш производительности?

только в случае когда логика размазывается на потоки в случаях воркеров
Re[9]: Чтение из сети и обработка
От: tryAnother  
Дата: 14.06.24 12:17
Оценка:
R>тюнить входящий не тюня исходящий
R>на отправку пакеты тоже могут дропнуться если буфер будет переполнен

отправлять данные будет специализированная железка, там передающая часть реализована на ПЛИС.
а пока ее нет использую эту программу имитатор.

R>да спутал одну m, только патчи есть


R>https://lists.boost.org/Archives/boost/2023/01/253873.php

R>https://blog.cloudflare.com/how-to-receive-a-million-packets

спасибо, посмотрю

R>я совсем не понял что это значит


синхронное чтение утилизирует CPU на 40% при 66 kpps, а не блокирующее чтение со сном 1% на 87 kpps
второй вариант лучше, так по потоку проходит, и потребляет меньше ЦП.

тут тестируется только прием, обработка будет тоже затратная и лучше оставить ей больше ресурсов.
Re[10]: Чтение из сети и обработка
От: reversecode google
Дата: 14.06.24 12:28
Оценка:
в случае когда есть затратная обработка
то это пальцем в небо пытаться просчитывать ее сейчас методом анализа простоя cpu%

в целом вы ерундой занимаетесь
потому что надо воркеры
раскидывать по треидам
и каждый воркер будет заниматься разгребанием затратной обработкой
это asio и умеет делать
достаточно run запустить на потоках больше одного, плюс синхронизации если там нет разных сессий а просто один поток от одного девайса
и не важно как и сколько там будет оставаться cpu

и вот для лучшей оптимизации воркеров и можно будет поиграться с тремя видами корутин
ну как минимум двумя которые поддерживаются c++20
где одна из них чего то там безстековая
https://hackernoon.com/temporary-lifetime-extension-mistakes-and-solutions
https://old.reddit.com/r/cpp/comments/1d9ha7d/make_it_async_building_shared_async_resources/
но для начала до воркеров дойдите
Re[11]: Чтение из сети и обработка
От: tryAnother  
Дата: 14.06.24 12:56
Оценка:
Здравствуйте, reversecode, Вы писали:

в общем задача такая:
— есть железка, которая снимает показания датчиков (вроде линейной камеры) упаковывает в поток UDP и отправляет,
— приложение этот поток собирает вычищает весь мусор, остатки обрабатывает и передает дальше подписчикам уже намного меньше информации.

специфика фильтрации и обработки в том, что используется предыдущее состояние, те "кадры" нужно обрабатывать последовательно, или хотя бы группами.

поэтому и вычитывание в один поток, чтобы потом из разных очередей не собирать.
понятно что UDP может пакеты перемешать, и для этого есть небольшой буфер на 10 пакетов где они сортируются,
но в процессе эксплуатации предыдущей версии железки и ПО такое если и встречалось, то очень очень редко. может единицы раз на годы работы.

сейчас поток вычитывает пакеты, собирает в блоки, и отдает другому потоку на обработку, обработка занимает примерно 50-80% ядра.
но данных хотят гнать больше, и соответственно загрузка вырастет.
а обработка происходит на обычном ПК, позиционируется что чуть ли не на ноутбуке этим можно будет заниматься, от того и интерес использовать поменьше ЦП если получится.
Re[12]: Чтение из сети и обработка
От: reversecode google
Дата: 14.06.24 13:50
Оценка:
железка одна и поток с нее идет один ?
сорм что ли
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.