clock_gettime требует больше времени для выполнения при запуске программы с терминала - PullRequest
2 голосов
/ 03 августа 2020

Я пытался измерить время для фрагмента кода и заметил, что время было примерно на 50 нс быстрее, когда я запускал программу из моего редактора QtCreator, по сравнению с тем, когда я запускал ее из оболочки bash, запущенной в гном-терминал. Я использую Ubuntu 20.04 в качестве ОС.

Небольшая программа для воспроизведения моей проблемы:

#include <stdio.h>
#include <time.h>

struct timespec now() {
  struct timespec now;
  clock_gettime(CLOCK_MONOTONIC, &now);
  return now;
}

long interval_ns(struct timespec tick, struct timespec tock) {
  return (tock.tv_sec - tick.tv_sec) * 1000000000L
      + (tock.tv_nsec - tick.tv_nsec);
}

int main() {
    // sleep(1);
    for (size_t i = 0; i < 10; i++) {
        struct timespec tick = now();
        struct timespec tock = now();
        long elapsed = interval_ns(tick, tock);
        printf("It took %lu ns\n", elapsed);
    }
    return 0;
}

Вывод при запуске из QtCreator

It took 84 ns
It took 20 ns
It took 20 ns
It took 21 ns
It took 21 ns
It took 21 ns
It took 22 ns
It took 21 ns
It took 20 ns
It took 21 ns

И при запуске из моей оболочки внутри терминала:

$ ./foo 
It took 407 ns
It took 136 ns
It took 74 ns
It took 73 ns
It took 77 ns
It took 79 ns
It took 74 ns
It took 81 ns
It took 74 ns
It took 78 ns

Вещи, которые я пробовал, но не имело значения

  • Разрешение QtCreator запускать программу в терминале
  • Использование вызовов rdts c и rdtscp вместо clock_gettime (те же относительные различия во времени выполнения)
  • очистка среды из терминала, запустив его под env -i
  • Запуск программы с использованием sh вместо bash

Я проверил, что во всех случаях вызывается один и тот же двоичный файл. Я проверил, что значение nice для программы во всех случаях равно 0.

Вопрос

Почему запуск программы из моей оболочки имеет значение? Есть предложения, что попробовать?

Обновление

  • Если я добавлю вызов sleep (1) в начало main, как QtCreator, так и gnome-terminal / * Вызов 1046 * сообщает о более длительном времени выполнения.

  • Если я добавлю системный вызов ("ps -H") в начало main, но отброшу ранее упомянутый sleep (1): оба вызова сообщают о коротком времени выполнения (~ 20 нс).

1 Ответ

2 голосов
/ 03 августа 2020

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

Очевидно, QtCreator использует достаточно процессорного времени, чтобы это произошло до запуска вашей программы, иначе вы компилируете + работаете, и процесс компиляции служит для разогрева. (по сравнению с вилкой / execve bash, имеющей меньший вес.)

См. Idiomati c способ оценки производительности? для получения дополнительной информации о выполнении прогонов при тестировании, и также Почему эта delay-l oop начинает работать быстрее после нескольких итераций без сна?

На моем i7-6700k (Skylake) работает Linux, увеличивая l oop счетчик итераций до 1000 достаточен для того, чтобы последние итерации работали на полной тактовой частоте, даже после того, как первая пара итераций обрабатывает ошибки страниц, разогревает iTLB, кэш uop, кеши данных и т. Д.

$ ./a.out      
It took 244 ns
It took 150 ns
It took 73 ns
It took 76 ns
It took 75 ns
It took 71 ns
It took 72 ns
It took 72 ns
It took 69 ns
It took 75 ns
...
It took 74 ns
It took 68 ns
It took 69 ns
It took 72 ns
It took 72 ns        # 382 "slow" iterations in this test run (copy/paste into wc to check)
It took 15 ns
It took 15 ns
It took 15 ns
It took 15 ns
It took 16 ns
It took 16 ns
It took 15 ns
It took 15 ns
It took 15 ns
It took 15 ns
It took 14 ns
It took 16 ns
...

В моей системе для параметра energy_performance_preference установлено значение balance_performance, поэтому аппаратный регулятор P-состояния не такой агрессивный, как с performance. Используйте grep . /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference, чтобы проверить, используйте sudo, чтобы изменить его:

sudo sh -c 'for i in /sys/devices/system/cpu/cpufreq/policy[0-9]*/energy_performance_preference;do echo balance_performance > "$i";done'

Однако даже запуска под perf stat ./a.out достаточно, чтобы очень быстро увеличить тактовую частоту до максимальной; это действительно не займет много времени. Но синтаксический анализ команды bash после того, как вы нажмете return, - это очень дешево, не так много работы процессора, прежде чем он вызовет execve и достигнет main в вашем новом процессе.

printf с выводом с линейной буферизацией - это то, что занимает большую часть времени процессора в вашей программе, BTW. Вот почему требуется так мало итераций, чтобы набрать скорость. например, если вы запустите perf stat --all-user -r10 ./a.out, вы увидите, что такты ядра пользовательского пространства в секунду равны всего лишь 0,4 ГГц, остальное время, проведенное в ядре в системных вызовах write.

...