Я хочу рассчитать время, прошедшее во время вызова функции в C, с точностью до 1 наносекунды.
Есть ли функция таймера, доступная в C, чтобы сделать это?
Если да, укажите примерный fragment кода.
Псевдокод
Timer.Start() foo(); Timer.Stop() Display time elapsed in execution of foo()
Сведения об окружающей среде: – с использованием компилятора gcc 3.4 на машине RHEL
Могу ли я спросить, какой процессор вы используете? Если вы используете процессор x86, вы можете посмотреть счетчик tsc
времени ( tsc
). Этот fragment кода:
#define rdtsc(low,high) \ __asm__ __volatile__("rdtsc" : "=a" (low), "=d" (high))
поставит количество циклов, которые CPU выполнил на low
и high
соответственно (он ожидает 2 long
s, вы можете сохранить результат в long long int
) следующим образом:
inline void getcycles (long long int * cycles) { unsigned long low; long high; rdtsc(low,high); *cycles = high; *cycles <<= 32; *cycles |= low; }
Обратите внимание, что это возвращает количество циклов, выполненных вашим процессором. Вам нужно будет получить скорость вашего процессора, а затем выяснить, сколько циклов за ns, чтобы получить количество ns, прошедших.
Чтобы сделать это, я проанализировал строку «cpu MHz» из /proc/cpuinfo
и преобразовал ее в десятичную. После этого это всего лишь немного математики, и помните, что 1 МГц = 1 000 000 циклов в секунду и что 1 миллиард нс / сек.
На Intel и совместимых процессорах вы можете использовать команду rdtsc, которая может быть легко перенесена в блок asm () кода C. Он возвращает значение встроенного счетчика циклов процессора, который увеличивается на каждый цикл. Вы получаете высокое разрешение, и такое время очень быстро.
Чтобы узнать, как быстро это приращение вам потребуется, выполните калибровку – дважды вызовите эту инструкцию за фиксированный период времени, например пять секунд. Если вы сделаете это на процессоре, который сдвигает частоту, чтобы снизить потребление энергии, у вас могут быть проблемы с калибровкой.
Используйте clock_gettime(3)
. Для получения дополнительной информации введите man 3 clock_gettime
. При этом точность наносекунд редко необходима.
Любая функция таймера должна быть специфичной для платформы, особенно с учетом этой точности.
Стандартное решение в системах POSIX – gettimeofday()
, но оно имеет только микросекундную точность.
Если это относится к бенчмаркингу производительности, стандартный способ заключается в том, чтобы сделать тест кода достаточным для того, чтобы сделать точность требований менее серьезной. Другими словами, запустите тестовый код в течение целой секунды (или более).
В таймере нет таймера с гарантированной точностью в 1 наносекунду. Вы можете посмотреть в clock()
или еще лучше. POSIX gettimeofday()
Я не знаю, найдете ли вы таймеры, обеспечивающие разрешение одной наносекунды – это будет зависеть от разрешения системных часов – но вы можете посмотреть http://code.google.com/ p / high-resolution-timer / . Они указывают, что они могут обеспечить разрешение микросекундного уровня на большинстве систем Linux и в наносекундах на системах Sun.
Создание контрольных показателей в этой шкале не является хорошей идеей. У вас есть накладные расходы, чтобы получить время, по крайней мере, что может сделать ваши результаты ненадежными, если вы работаете над наносекундами. Вы можете использовать системные вызовы своих платформ или boost :: Date_Time в более крупном масштабе [предпочтительнее].
Можете ли вы просто запустить его 10 ^ 9 раз и секундомер?
Вы можете использовать стандартные системные вызовы, такие как gettimeofday, если вы уверены, что ваш процесс получает 100%, если процессорное время. Я могу придумать много ситуаций, в которых, пока вы выполняете foo (), другие streamи и процессы могут украсть процессорное время.
Вы просите что-то, что не возможно таким образом. Вам понадобится поддержка уровня HW, чтобы достичь такого уровня точности и даже тогда тщательно контролировать переменные. Что произойдет, если вы получите прерывание при запуске кода? Что делать, если ОС решает запустить какой-либо другой код?
И что делает ваш код? Использует ли он оперативную память? Что делать, если ваш код и / или данные находятся или не находятся в кеше?
В некоторых средах вы можете использовать счетчики уровня HW для этого задания, если вы контролируете эти переменные. Но как вы предотвращаете контекстные переключатели в Linux?
Например, в инструментах DSP Texas Instruments (Code Composer Studio) вы можете точно профилировать код, потому что вся среда отладки настроена так, что эмулятор (например, Blackhawk) получает информацию о каждом запуске операции. Вы также можете установить точки наблюдения, которые кодируются непосредственно в блок HW внутри чипа в некоторых процессорах. Это работает, потому что полосы памяти также направляются в этот блок отладки.
Они предлагают функции в своей библиотеке CSL (Chip Support Library), которую вы запрашиваете, с накладными затратами времени, которые состоят из нескольких циклов. Но это доступно только для их процессоров и полностью зависит от считывания значений таймера из регистров HW.
Мы все тратим наше время на воссоздание этого тестового образца. Почему бы не написать что-нибудь компилируемое? Во всяком случае, вот мои результаты.
CLOCK_PROCESS_CPUTIME_ID resolution: 0 sec 1 nano clock_gettime 4194304 iterations : 459.427311 msec 0.110 microsec / call CLOCK_MONOTONIC resolution: 0 sec 1 nano clock_gettime 4194304 iterations : 64.498347 msec 0.015 microsec / call CLOCK_REALTIME resolution: 0 sec 1 nano clock_gettime 4194304 iterations : 65.494828 msec 0.016 microsec / call CLOCK_THREAD_CPUTIME_ID resolution: 0 sec 1 nano clock_gettime 4194304 iterations : 427.133157 msec 0.102 microsec / call rdtsc 4194304 iterations : 115.427895 msec 0.028 microsec / call Dummy 16110479703957395943 rdtsc in milliseconds 4194304 iterations : 197.259866 msec 0.047 microsec / call Dummy 4.84682e+08 UltraHRTimerMs 197 HRTimerMs 197.26 #include #include #include #include #include #include enum { TESTRUNS = 1024*1024*4 }; class HRCounter { private: timespec start, tmp; public: HRCounter(bool init = true) { if(init) SetStart(); } void SetStart() { clock_gettime(CLOCK_MONOTONIC, &start); } double GetElapsedMs() { clock_gettime(CLOCK_MONOTONIC, &tmp); return (double)(tmp.tv_nsec - start.tv_nsec) / 1000000 + (tmp.tv_sec - start.tv_sec) * 1000; } }; __inline__ uint64_t rdtsc(void) { uint32_t lo, hi; __asm__ __volatile__ ( // serialize "xorl %%eax,%%eax \n cpuid" ::: "%rax", "%rbx", "%rcx", "%rdx"); /* We cannot use "=A", since this would use %rax on x86_64 and return only the lower 32bits of the TSC */ __asm__ __volatile__ ("rdtsc" : "=a" (lo), "=d" (hi)); return (uint64_t)hi << 32 | lo; } inline uint64_t GetCyclesPerMillisecondImpl() { uint64_t start_cyles = rdtsc(); HRCounter counter; std::this_thread::sleep_for (std::chrono::seconds(3)); uint64_t end_cyles = rdtsc(); double elapsed_ms = counter.GetElapsedMs(); return (end_cyles - start_cyles) / elapsed_ms; } inline uint64_t GetCyclesPerMillisecond() { static uint64_t cycles_in_millisecond = GetCyclesPerMillisecondImpl(); return cycles_in_millisecond; } class UltraHRCounter { private: uint64_t start_cyles; public: UltraHRCounter(bool init = true) { GetCyclesPerMillisecond(); if(init) SetStart(); } void SetStart() { start_cyles = rdtsc(); } double GetElapsedMs() { uint64_t end_cyles = rdtsc(); return (end_cyles - start_cyles) / GetCyclesPerMillisecond(); } }; int main() { auto Run = [](std::string const& clock_name, clockid_t clock_id) { HRCounter counter(false); timespec spec; clock_getres( clock_id, &spec ); printf("%s resolution: %ld sec %ld nano\n", clock_name.c_str(), spec.tv_sec, spec.tv_nsec ); counter.SetStart(); for ( int i = 0 ; i < TESTRUNS ; ++ i ) { clock_gettime( clock_id, &spec ); } double fb = counter.GetElapsedMs(); printf( "clock_gettime %d iterations : %.6f msec %.3f microsec / call\n", TESTRUNS, ( fb ), (( fb ) * 1000) / TESTRUNS ); }; Run("CLOCK_PROCESS_CPUTIME_ID",CLOCK_PROCESS_CPUTIME_ID); Run("CLOCK_MONOTONIC",CLOCK_MONOTONIC); Run("CLOCK_REALTIME",CLOCK_REALTIME); Run("CLOCK_THREAD_CPUTIME_ID",CLOCK_THREAD_CPUTIME_ID); { HRCounter counter(false); uint64_t dummy; counter.SetStart(); for ( int i = 0 ; i < TESTRUNS ; ++ i ) { dummy += rdtsc(); } double fb = counter.GetElapsedMs(); printf( "rdtsc %d iterations : %.6f msec %.3f microsec / call\n", TESTRUNS, ( fb ), (( fb ) * 1000) / TESTRUNS ); std::cout << "Dummy " << dummy << std::endl; } { double dummy; UltraHRCounter ultra_hr_counter; HRCounter counter; for ( int i = 0 ; i < TESTRUNS ; ++ i ) { dummy += ultra_hr_counter.GetElapsedMs(); } double fb = counter.GetElapsedMs(); double final = ultra_hr_counter.GetElapsedMs(); printf( "rdtsc in milliseconds %d iterations : %.6f msec %.3f microsec / call\n", TESTRUNS, ( fb ), (( fb ) * 1000) / TESTRUNS ); std::cout << "Dummy " << dummy << " UltraHRTimerMs " << final << " HRTimerMs " << fb << std::endl; } return 0; }