Я пишу программу, которая требует от меня знания времени выполнения функции при любом количестве циклов. После тонкой настройки я обнаружил, что время выполнения нелинейно, и мне хотелось бы знать, почему. Я переписал код во что-то, чем мог бы поделиться здесь, провел его моделирование и получил практически те же результаты. Вот код:
void DummyLoop(long y)
{
long counter = 0;
long DummyArray[3];
char loopArray;
while(counter < y)
{
for(loopArray = 0; loopArray < 3; loopArray++)
{
DummyArray[x] = 10000/50; // Some int division operation
}
counter++;
}
}
Я измерил результаты с помощью внутреннего таймера MCU и вызвал цикл следующим образом:
T0CON0bits.EN = 1; // Start timer
DummyLoop(10000);
T0CON0bits.EN = 0; // Stop timer
Результаты затем распечатываются через UART после остановки таймера. Это время, которое я замерил при выполнении одной и той же функции с растущим числом циклов. Все эти результаты были измерены индивидуально для разных входов числа циклов.
Я добавил туда столбец, умножающий время, необходимое для одного цикла, на количество циклов для этого теста, просто чтобы показать, как меняются результаты. Примерно через 1000 циклов он сужается, но почему результат так сильно меняется? Я бы подумал, что время, необходимое для выполнения одного цикла, можно умножить на любое число? Для справки я использую 8-битный микроконтроллер PIC18F47Q43, скомпилированный с помощью XC8.
Все выглядит довольно линейно, поэтому, вероятно, это накладные расходы на вызов функции и любой другой код, находящийся за пределами цикла, искажает время для результата «1», и вы не учитываете это при умножении.
Для чего вы собираетесь использовать это средство?
10000/50 — постоянное выражение. Он будет оценен во время компиляции и заменен на 200.
Если ваш компилятор посредственный или лучше (и вы, очевидно, включили оптимизацию), то время выполнения будет примерно 0 секунд, или, скорее, время переключения вашего контакта GPIO. Есть ли хорошие компиляторы для этой цели или вам придется использовать шутку Microchip о компиляторе? Рассмотрите возможность портирования на настоящий.





Это 8-битный MCU, поэтому, если у вас код без планировщика, то примерно без каких-либо прерываний вы можете получить почти одинаковое время выполнения для каждого цикла. Однако, если вмешивается планировщик и какой-либо источник прерываний, то выполнение одного цикла будет занимать разное время каждый раз, когда он выполняется. если t1 — это время, необходимое для выполнения 10 000 циклов, вы можете получить лучший результат со средним значением t1/10 000 для одного цикла. В соответствии с результатом, время одного цикла умножается на количество циклов. Если вы выполнили его, оно может быть неточным, или если вы рассчитывали с использованием таймингов инструкций, тогда источники планировщика/прерывания могут мешать выполнению цикла. Пожалуйста, укажите, откуда взята ссылка на синхронизацию одного цикла.
Вы не можете просто умножить количество циклов на какую-то константу, так как не учитываете накладные расходы остального кода.
Вместо этого правильная формула: время(петли) = петли * К1 + К2.
У нас есть время для 1 и для 2 цикла. Это дает нам константу K1.
K1 = время(2) – время(1) = 0,017625 мс – 0,0100625 мс = 0,0075625 мс
Имея этот результат, мы можем вычислить константу K2.
K2 = время(1) - 1 * K1 = 0,0100625 мс - 1 * 0,0075625 мс = 0,0025 мс
Давайте еще раз посмотрим на результаты.
Выглядит хорошо, не так ли? Различия в последних двух значениях, безусловно, являются ошибками округления используемой процедуры вывода. Вычисления с float обычно ограничиваются примерно 6 ненулевыми цифрами.
Примечание 1:
К1 – время одного цикла:
for внутри внешнего циклаК2 — время единовременных накладных расходов:
K1 K2
x T0CON0bits.EN = 1; // Start timer
x DummyLoop(10000);
x T0CON0bits.EN = 0; // Stop timer
x void DummyLoop(long y)
x {
x long counter = 0;
x long DummyArray[3];
x char loopArray;
x x while(counter < y)
x {
x for(loopArray = 0; loopArray < 3; loopArray++)
x {
x DummyArray[x] = 10000/50; // Some int division operation
x }
x counter++;
x }
x }
Заметка 2:
Вы можете суммировать эти времена из сгенерированного машинного кода.
Или, если это слишком сложно, используйте вывод порта для переключения и осциллограф для измерения интервала на этом выводе.
Это именно то, что я искал, спасибо! Что именно означают K1 и K2?
Если вы выполняете сборку с включенной оптимизацией (что и следует делать, иначе тестирование бесполезно), то вся функция, скорее всего, будет пустой (или, возможно, вообще не будет вызвана), поскольку за пределами функции нет видимых побочных эффектов.