Выполнение мер в рамках выполнения кода c каждые t миллисекунд

#c #events #visual-c #thread-sleep

#c #c

Вопрос:

Задан цикл while и порядок функций следующим образом:

 int k=0;
int total=100;
while(k<total){
   doSomething();
   if(approx. t milliseconds elapsed) { measure(); }
     k;
}
  

Я хочу выполнять «измерение» каждые t-ю миллисекунду. Однако, поскольку время ‘doSomething’ может быть близко к t-й миллисекунде с момента последнего выполнения, допустимо выполнить измерение примерно через t миллисекунд, прошедших с момента последнего измерения.
Мой вопрос: как этого можно достичь?

Одним из решений было бы установить таймер на ноль и измерять его после каждого ‘doSomething’. Когда оно выходит за пределы допустимого диапазона, я выполняю измерения и выполняю сброс. Однако я не знаю, какую функцию c мне следует использовать для такой задачи. Как я вижу, существуют определенные функции, но дискуссия о том, какая из них является наиболее подходящей, находится за пределами моего понимания. Обратите внимание, что некоторые функции фактически учитывают время, затраченное некоторыми другими процессами, но я хочу, чтобы мой таймер измерял только время выполнения моего кода на c (надеюсь, это понятно). Другое дело — разрешение измерений, как указано ниже. Предположим, что средний вариант из предложенных.

Комментарии:

1. Получите время до вызова doSomething(), получите время после, определите разницу и, если прошло t миллисекунд, измерьте ее. Что такое doSomething() занимает больше t?

2. для подсчета истекших миллисекунд может использоваться таймер высокого разрешения, зависящий от платформы. Сколько времени doSomething() занимает? Какое разрешение в миллисекундах вам нужно, 1-2? 5-10? 50-100?

3. в названии написано «прерывание». вы хотите прервать doSomething() для выполнения измерения? это нечто совершенно другое.

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

5. Как я указывал в правке: чтобы устранить необходимость фактического прерывания, меры могут выполняться после каждого вызова функции. Другими словами, необходимо разрешить ‘doSomething’ выполнять свою задачу.

Ответ №1:

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

 #define SAMPLE_PERIOD_MS 100
#define SAMPLE_PERIOD_TICKS ((CLOCKS_PER_SEC * SAMPLE_PERIOD_MS) / 1000)

int k=0;
int total=100;
clock_t measure_time = clock()   SAMPLE_PERIOD_TICKS  ;
while(k<total)
{
   doSomething();
   if( clock() - measure_time > 0 )
   { 
        measure(); 
        measure_time  = SAMPLE_PERIOD_TICKS ;
          k;
   }
}
  

При необходимости вы можете заменить clock () каким-либо другим источником синхронизации с высоким разрешением.

Однако обратите внимание на пару проблем. Этот метод является «циклом занятости»; если ни doSomething(), ни measure() не выдадут ЦП, процесс займет все возможные циклы ЦП. Если это единственный код, выполняемый на целевом объекте, это может не иметь значения. С другой стороны, это выполняется в ОС общего назначения, такой как Windows или Linux, которые не работают в режиме реального времени, процесс может быть опережен другими процессами, и это может повлиять на точность периодичности выборки. Если вам нужно точное время, было бы лучше использовать RTOS и выполнять doSomething() и measure() в отдельных потоках. Даже в объектах групповой политики это было бы лучше. Например, общий шаблон (с использованием выдуманного API в отсутствие какой-либо спецификации) был бы:

 int main()
{
    StartThread( measure_thread, HIGH_PRIORITY ) ;
    for(;;)
    {
        doSomething() ;
    }
}

void measure_thread()
{
    for(;;)
    {
        measure() ;
        sleep( SAMPLE_PERIOD_MS ) ;
    }
}
  

Код для measure_thread() является точным только в том случае, measure() если для выполнения требуется незначительное время. Если это занимает значительное время, вам может потребоваться учесть это. Если он недетерминирован, возможно, вам даже придется измерить время его выполнения, чтобы вычесть из него период ожидания.

Комментарии:

1. Ответ объясняет большинство возможных проблем, с которыми я мог бы столкнуться. Обратите внимание, что я уже сталкивался с некоторыми противоречивыми результатами, когда значительно асимптотически более быстрый алгоритм на самом деле занимает больше времени, как измерено. Это случается время от времени, но неуместно в экспериментальных условиях с целью продемонстрировать превосходство одного метода. В любом случае, код будет подвергнут сотням выборок с различными параметрами., поэтому конечный результат должен быть «достаточно справедливым».

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