- PVSM.RU - https://www.pvsm.ru -
Привет! В этой статье я хочу рассказать о решении одной из типичных проблем, с которой Embox справляется лучше GNU/Linux. Речь идет о времени реакции на пакет, переданный по протоколу Ethernet. Как известно, основной характеристикой передачи данных по сети является пропускная способность, и с ней у GNU/Linux все хорошо. Но когда речь заходит об уменьшении времени на прием/передачу единичного сетевого пакета, могут возникнуть проблемы. В частности, у заказчика была плата DE0-Nano-SoC с Linux, и с помощью этой платы хотелось управлять неким объектом по сети. Топология сети — точка-точка, никаких роутеров и хабов нет. По модели управления время реакции должно быть меньше 100 мкс, а на базе Linux удавалось добиться только 500 мкс.
Для оценки времени передачи создаем стенд, состоящий из двух хостов.
В качестве первого хоста выступает компьютер общего назначения с GNU/Linux, в качестве второго хоста — отладочная плата DE0-Nano-SoC Kit с Embox. Эта плата содержит FPGA и HPS (Hard Processing System, т.е. обычный ARM), и именно на ней нужно уменьшать время отклика. Напишем тестовое приложение, которое просто будет отвечать UDP-пакетом с идентичным содержимым:
while (1) {
char buf[BUFLEN];
recvfrom(s, buf, BUFLEN);
sendto(s, buf, BUFLEN);
}
Его будем запускать на втором хосте, то есть на DE0-Nano-SoC.
На первом хосте программа будет посылать пакеты, ждать ответа и измерять время между отправкой и приемом:
for (int i = 0; i < N; i++) {
char buf_tx[BUFLEN], buf_rx[BUFLEN];
sprintf(buf_tx, "This is packet %dn", i);
time_t time_begin = time_now();
sendto(s, buf_tx, BUFLEN);
recvfrom(s, buf_rx, BUFLEN);
time_t time_end = time_now();
if (memcmp(buf_tx, buf_rx, sizeof(buf))) {
printf("%d: Buffer mismatchn", i);
}
if (time_end - time_begin > TIME_LIMIT) {
printf("Slow answer #%d: %dn", i, time_end - time_begin);
}
}
При этом вычислим среднее, максимальное и минимальное время отклика.
Код есть на Github [1].
Сделав пробный запуск и удостоверившись, что пакеты приходят и уходят, на стороне отладочной платы сразу применим очевидные оптимизации:
-O2
При отправке 500 000 пакетов (столько же отправлялось и в следующих примерах) получились такие значения:
Avg: 4.52ms
Min: 3.12ms
Max: 12.24ms
Это в несколько раз медленнее, чем ориентировочные значения, предоставленные со стороны заказчика — среднее значение должно быть хотя бы на порядок меньше. Заказчик говорил, что у него на Linux получались даже лучшие характеристики. Мы начали думать, что может быть не так.
Возможно, дело в других процессах? Но нет, кроме данной программы на плате ничего не запускается.
Возможно, много времени уходит на обработку каких-то прерываний, например, таймера? Тоже нет — снижение частоты его срабатываний никак не влияет на результат.
Как оказалось, дело было в скорости самого ethernet-соединения — использовался USB-адаптер, поддерживающий максимум 100Мбит/с, да и в драйвере не было поддержки гигабитной скорости.
После замены сетевой карты и добавления поддержки 1Гбит в драйвере получаем ещё один значительный скачок в производительности:
Avg: 0.08ms
Min: 0.07ms
Max: 4.31ms
Достаточно естественно сравнить это время с Linux. То же самое приложение очень просто кросс-компилировать: arm-linux-gnueabihf-gcc server.c -O2
. Получившийся ELF заливаем на плату и запускаем:
Avg: 0.77ms
Min: 0.74ms
Max: 5.31ms
Таким образом, Embox "отвечает" примерно в 9 раз быстрее, что не может не радовать!
Среднее время отклика вполне хорошее, но есть "выбросы", которые не только имеют слишком большую задержку, но и вносят значительную непредсказуемость.
Для того, чтобы определить источник задержек, было решено замерить аппаратным таймером время, которой занимает обработка UDP-пакета от его приёма до отправки соответствующего ответа.
Можно накапливать статистику на отладочной плате, но проще сразу же передать на хост данные прямо в UDP-пакете.
В итоге мы решили, что прямо в пакет UDP будем ставить метки времени прихода и отправления. Причем время приходя получается еще до получения пакета из сетевой карты, прямо в обработчике прерывания. Время отправления будет получать перед самой отправкой пакета в сеть. Ну а дальше приблизительно такой код:
int net_tx(...) {
if (is_udp_packet()) {
timestamp2 = timer_get();
memcpy(packet[UDP_OFFT],
×tamp1,
sizeof(timestamp1));
memcpy(packet[UDP_OFFT + sizeof(timestamp2)],
×tamp2,
sizeof(timestamp2));
...
}
}
В данном случае не важно, с какой частотой работает таймер, достаточно убедиться, что выбросы по времени совпадают с более долгой обработкой пакета внутри Embox.
Получили такие результаты
Avg: 8673
Min: 6191
Max: 11950
При изучении полученных данных оказалось, что разброс времени обработки пакета составляет (между средним и максимальным) где то 25%, что конечно никак не может объяснить разброс на хосте (Avg: 0.08ms Max: 4.31ms). То есть либо задержки происходят вне проверяемого интервала (после получения пакета, но до входа в соответствующий обработчик прерывания, либо после того, как начинается отправка), либо задержки возникают на другом конце провода. В любом случае, ситуацию программно уже не удастся улучшить, точнее её можно улучшить только на 25%.
Может, проблема на другой стороне?
Остаётся один вариант — задержки возникают на стороне приложения Linux, ведь мы для измерений использовали обычный хост.
Как это проверить?
Первое, что приходит в голову — запустить процесс с высоким приоритетом:
nice -n -20 ./client
Ощутимых изменений это не дало — казалось, небольшой прирост есть, но разброс от раза к разу всё равно значительно его перекрывал.
Ещё один способ — запустить процесс с алгоритмом планирования round robin и с высоким приоритетом, это можно сделать с помощью chrt
:
chrt --rr 99 ./client
И на этот раз нужный эффект действительно был достигнут — количество длительных задержек уменьшилось на порядок. Приведу гистрограмму распределения задержек для разных стратегий планировщика (масштаб оси ординат имеет логарифмический масштаб, т.к. при линейном столбцы после первого не различимы).
Используя Embox [2] удалось достаточно просто решить поставленную задачу, уменьшить время отклика на один пакет почти в 10 раз. При этом прикладное ПО у заказчика остается фактически без изменений, следовательно, его не нужно переписывать и отлаживать. Может кто нибудь подскажет, можно ли добиться оптимизации данного параметра средствами Linux, например используя какой-нибудь bpfilter.
Если есть какие-то вопросы — пишите в рассылку embox-devel@googlegroups.com, или в наш телеграм-чат [3], или в комментарии здесь.
Автор: 0xdde
Источник [4]
Сайт-источник PVSM.RU: https://www.pvsm.ru
Путь до страницы источника: https://www.pvsm.ru/benchmark/347720
Ссылки в тексте:
[1] на Github: https://github.com/Deryugin/udp_test
[2] Embox: http://embox.github.io
[3] телеграм-чат: https://t.me/embox_chat
[4] Источник: https://habr.com/ru/post/489882/?utm_campaign=489882&utm_source=habrahabr&utm_medium=rss
Нажмите здесь для печати.