Использование таймеров с программным обеспечением, критичным к производительности (Qt)

Я занимаюсь разработкой приложения, которое отвечает за перемещение и управление роботами через соединение UDP.

Приложение должно:

  • Прочитать джойстик / пользовательский ввод, используя SDL.
  • Генерация и отправка управляющего пакета роботу каждые 20 миллисекунд (UDP)
  • Получать и декодировать ответные пакеты от робота (~ 20 мсек). Это было реализовано с помощью механизма сигнал / слот и не требует таймера.
  • Получать и обрабатывать сообщения робота по причинам отладки. Это не регламентировано временем.
  • Регулярно обновляйте пользовательский интерфейс, чтобы держать пользователя в курсе о состоянии робота (например, напряжение батареи). В большинстве случаев я также использовал механизм сигнала / слота Qt.
  • Используйте сторожевой таймер, который отключает робота, если ответ не получен через 1 секунду. Сторожевой таймер сбрасывается, когда приложение получает пакет робота (~ 20 мсек)

На данный момент я реализовал все вышеперечисленное. Тем не менее, приложение не может отправлять пакеты регулярно, когда сторожевой таймер активирован или когда два или более QTimer объекты используются. Приложение, как правило, будет работать, но я не буду считать его «готовым к производству». Я пытался использовать точные флаги таймеров (Qt::Precise, Qt::Coarse а также Qt::VeryCoarse), но у меня все еще были проблемы.

Заметки:

  • Код в целом хорошо организован, в базе кода нет «объектов бога» (большинство исходных файлов имеют длину менее 150 строк и создают только необходимые зависимости).
  • В большинстве случаев я использую QTimer::singleShot() (например, я отправлю следующий пакет только после отправки текущего пакета).

Где мы используем таймеры:

  • Для чтения ввода джойстика (~ 50 мсек, точный таймер)
  • Для отправки пакетов робота (~ 20 мсек, точный таймер)
  • Чтобы обновить некоторые аспекты пользовательского интерфейса (~ 500 мсек, грубый таймер)
  • Обновление прошедшего времени с момента включения робота (~ 100 мсек, точный таймер)
  • Для реализации сторожевого таймера (переведите приложение и робот в безопасное состояние, если 1000 мсек прошли без ответа робота)
  • Замечания: сторожевой таймер подается, когда мы получаем ответный пакет от робота (~ 20 мсек)

Есть ли у вас какие-либо рекомендации по использованию объектов QTimer с критичным к производительности кодом (любая идея приветствуется). Обратите внимание, что я также пытался использовать разные потоки, но это вызвало у меня больше проблем, так как приложение не было бы синхронизировано, что не позволяло эффективно контролировать протестированных нами роботов.

2

Решение

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

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

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

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

И последнее, но не менее важное: если вы хотите выполнять задачу X каждые N единиц времени, это будет возможно только в том случае, если задача X занимает в вашей системе N единиц времени или меньше. Вы должны учитывать это для каждой задачи и для всех задач, запущенных одновременно. И чтобы получить точное планирование, вы должны измерить, сколько времени заняло задание X, и, если его частота меньше, запланировать следующее выполнение за оставшееся время, иначе выполнить немедленно.

2

Другие решения

Других решений пока нет …

По вопросам рекламы [email protected]