Оценка загрузки ЦП планировщика задач

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

У меня есть простой переносимый планировщик задач (он зависит только от millis() и работает на разных системах с поддержкой Arduino), который вызывается с помощью функции в цикле() и выбирает, а затем запускает указатель функции ( "задача") из списка задач с информацией о статусе, режиме ожидания и приоритете.

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

Проблема в том, что из loop() могут вызываться и другие функции, а не только мой планировщик. Таким образом, было бы плохой идеей отслеживать общее время по сравнению со временем, затраченным на задачу. Если бы я это сделал, использование ЦП всегда было бы низким, поскольку фактическое время задачи не может использовать все время ЦП, поскольку другие задачи в цикле() незаметно использовали бы его часть.

Однако, если я специально отслеживаю время в планировщике по сравнению со временем, затраченным на задачу, я сталкиваюсь с новой проблемой. Я бы использовал больше памяти (еще несколько байтов, но я уже использую много...) и больше ЦП*, так как мне придется отслеживать общее количество и также текущее время выполнения для этой итерации планировщика. Это поверх аналогичной конструкции, определяющей время выполнения задачи. В основном, однако, проблема заключается в том, что функция loop() и планировщик работают достаточно быстро, поэтому таймер millis() может тикать медленнее, чем переключение между моим планировщиком и функцией loop(). Это приводит к тому, что время также отключается, так как оно будет засчитано, если оно сработает в планировщике, даже если в основном это было ошибкой loop().

Существует ли общая методика расчета процессорного времени? Было бы сложно разделить планировщик по времени, и он в любом случае будет работать медленнее. Я мог бы подсчитать долю вызовов планировщика, которые запускают/не запускают задачи, но это не сработает, потому что задачи занимают больше времени, чем бездействие, которое происходит, если задача не выполняется, и не учитывает длину задачи. .

*В системе AVR 8-разрядный ЦП использует удивительное совокупное время для всех этих 32-разрядных операций, между расчетами времени ожидания (честно говоря, только для задач ожидания) и расчетами истекшего времени, которые вводятся в моя предыдущая попытка использования процессора. Это была не единственная причина, но я заметил небольшое смещение времени при одной попытке кодирования планировщика из-за похожей проблемы.

, 👍1

Обсуждение

Чтобы решить проблему слишком низкого разрешения mills(), вы также можете использовать micros() для подсчета микросекунд, хотя на некоторых Arduino оно имеет разрешение 4 мкс или 8 мкс, а не настоящую микросекунду., @romkey

Я рассматриваю это, но та же проблема все еще может возникнуть, просто не так сильно. Я надеюсь, что есть какой-то известный или популярный подход для получения нагрузки на ЦП, который минимизирует это или, по крайней мере, чтобы мой не был хуже. Это также не дает ответа на вопрос, какой подход здесь лучше для того, что менее неправильно — игнорировать ли слишком низкое чтение из-за непреднамеренного включения loop(), или я беру на себя накладные расходы и сложность отслеживания только времени выполнения планировщика со слишком низким чтением для разные причины?, @RDragonrydr


2 ответа


Лучший ответ:

0

Единственный показатель, который у вас есть, — это время. Единственное, что вы можете сделать, это сравнить это время с итерациями/вызовами задач. Типичный подход состоит в том, чтобы записывать время в начале задачи «тик» и записывать его в конце. Затем добавьте разницу к общей сумме для этой задачи.

Если вы хотите затем посмотреть процент использования, вы можете сравнить эти итоги с общим временем работы системы.

Общее время выполнения уже сделано за вас в виде millis(), поэтому вам не нужно об этом беспокоиться.

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

Если вы относитесь к своему "бездействующему" задача такая же, как и любая другая задача (что является нормальным способом ведения дел - задача бездействия - это просто задача, которая ничего не делает), тогда сумма всех отсчетов времени для всех задач = (в пределах определенной детализации) общая сумма времени работы вашего планировщика задач. Разница между этим и временем выполнения системы (millis()) заключается в доле времени работы вашего планировщика по сравнению с другими вещами, происходящими в loop(). р>

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

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

В надлежащей многозадачной системе с переключением контекста вы обычно оперируете количеством "галочек" системы достаточно вовремя. То есть просто считайте 1 каждый раз, когда задача переключается переключателем контекста. Поскольку каждая задача имеет фиксированное время выполнения, контролируемое детализацией планировщика, то легко узнать, что задача X выполнялась в течение X/TOTAL_TICKS % времени. Любые расчеты будут выполняться в задаче — может быть, в задаче бездействия или в задаче системного учета.

,

Я думаю, что смогу заставить это работать. Проблема, конечно, в том, что у меня нет праздной задачи; в случае отсутствия задачи он просто терпит неудачу и проверяет снова. Дело все в самом планировщике, а не в задаче. Я не могу посчитать нулевое время, и это искажает любые измерения, которые я пытаюсь провести. Но если я смогу понять, как заставить незанятую задачу ждать, пока задача не будет (почти?) готова, возможно, я смогу заставить ее работать. Или сложите время, проведенное в самом планировщике, но это все равно слишком мало для измерения..., @RDragonrydr


-1

Самый дешевый (во всех смыслах) известный мне способ показать "праздность" заключается в том, чтобы установить выходной контакт при входе в планировщик и снова очистить его при отправке задачи (кроме задачи бездействия, если она есть). Или наоборот, если вы хотите вместо этого отображать нагрузку. Это может быть выполнено с помощью одной инструкции. Низкое влияние на вашу память, время выполнения, время сборки и кошелек.

Как именно вы хотите сообщить об этом?

  • Светодиод на выходе будет тускнеть и становиться ярче в зависимости от нагрузки. Если вам нужно демпфирование (возможно, нагрузка кратковременная и «пиковая»), добавьте RC-фильтр в цепь светодиода.

  • Подключите DVM или, что еще лучше, механический VOM, если сможете его найти, к выходу и считывайте нагрузку со счетчика. Механический счетчик сделает для вас некоторое демпфирование. DVM, вероятно, понадобится фильтр из предыдущего предложения.

  • Подключите отфильтрованный вывод обратно к аналоговому входу и считывайте нагрузку с точностью ~ 0,1 %! Это намного важнее, чем это оправдано, как вы считаете нужным.

,

Я не могу использовать это здесь, но ваша идея довольно гениальна. Мне это нравится!, @RDragonrydr