Попросил DALL-E нарисовать программиста, выбирающего секундомер. Вот результат )
Попросил DALL-E нарисовать программиста, выбирающего секундомер. Вот результат )

Решил обновить свою старую крохотную библиотечку для тайминга кода. Измерение коротких временнЫх интервалов в ней выполнялось с помощью: инструкции RDTSC при компиляции MSC для x86 и gettimeofday при сборке GCC под Linux. IMHO, на сегодняшний день это не самые правильные методы, и захотелось объективно выбрать что-то более точное и лёгкое. Для этого были измерены несколько параметров наиболее популярных функций измерения времени. Результатами этих измерений и делюсь с вами (вдруг кому тоже интересно).

Для удобства сравнения в таблице ниже приведены результаты тестирования на одной машине с процессором i7-6700K@4.00GHz, но под двумя ОС: Win10 22H2 и Ubuntu 22.04. Подобные тесты проводились и на других машинах (всего 8 штук включая два Raspberry Pi: 1B+ и 4B), но расширять таблицу не стал, результаты аналогичные. Все числа в статье замерены на указанной выше тестовой машине!

В таблице ниже все значения указаны в микросекундах. Discreteness - минимальный прирост показаний функции (очень грубо говоря это точность измерения), Duration - длительность вызова самой функции (overhead), One tick - значение единицы измерения, Type - тип функции (мировое время, время потока или процесса).

Краткие (если получится) замечания по наиболее популярным функциям измерения времени:

  1. clock() [С89] – Описываю исключительно из уважения к этой древнейшей функции, которая появилась ещё в первой редакции ANSI C и предназначалась как раз для точного измерения интервалов времени. По стандарту функция должна возвращать «processor time used by the program so far» но по историческим причинам её реализация у Microsoft не соответствует стандарту и возвращает «the elapsed time since the CRT initialization at the start of the process». Это различие не имело значения в одно-процессных ОС (например, в MS DOS) но принципиально для современных многозадачных ОС. Так что для портируемых приложений эта функция не годится. Кроме того, «правильная» реализация функции в Linux ну оооочень медленная (длительность вызова 960ns(!!!) против 36ns на Windows). Вообще все функции, возвращающие время, связанное с процессом или потоком, очень неспешны, видимо их реализация требует перехода в режим ядра, а это, как известно, операция дорогая. Дискретность функции сегодня тоже не удовлетворительна (1ms в Windows и 1µs в POSIX).

  2. timespec_get() [C11] – Позволяет получать системное время в наносекундах. Но не следует путать единицы измерения и точность – в Windows эта функция имеет дискретность 100ns а в Linux зависит от железа (22ns на тестовой машине и 1,1µs(!!!) на Raspberry Pi 1B+). То, что функция возвращает системное время, делает её не монотонной – система может корректировать время (даже назад).

  3. std::chrono [C++11] - функции now() классов high_resolution_clock и steady_clock достаточно лёгкие и позволяют получать время в наносекундах (см. комментарий к timespec_get()). В стандарте оговаривается, что high_resolution_clock предоставляет наиточнейшее измерение времени, но есть нюанс, эти часы не обязаны быть steady. В Linux high_resolution_clock является синонимом для system_clock а значит зависит от коррекции времени, как и timespec_get, короче говоря, в Linux время измеренное high_resolution_clock может уменьшаться! Зачем так сделано не знаю. В Windows этот класс является синонимом steady_clock и потому является монотонным. На исследованных платформах дискретность и длительность измерения времени с помощью всех классов std::chrono оказались одинаковы и соответствуют timespec_get(). В большинстве случаев можно прямо использовать steady_clock и не связываться с high_resolution_clock.

  4. gettimeofday() [POSIX] – позволяет получать время в микросекундах. На сегодня считается устаревшей - «POSIX.1-2008 marks gettimeofday() as obsolete, recommending the use of clock_gettime() instead». Упоминаю её лишь потому, что она часто используется в примерах из интернета.

  5. clock_gettime(CLOCK_MONOTONIC_RAW) [Linux] – видимо лучший вариант для тайминга в LinuxAPI. Точность и время выполнения около 22ns.

  6. QueryPerformanceCounter() [WinAPI] – хороший и очень популярный вариант из WinAPI. Функция монотонна, дискретность – 100ns (хотелось бы поменьше), время выполнения - 13ns(!).

  7. RDTSC/RDTSCP - для измерения интервалов времени на x86 процессорах часто рекомендуют эти инструкции. На тестовой машине они выполняются за 6,4/9,2ns (при частоте процессора 4,0GHz это примерно 26/37 тактов). TSC приписывают несколько проблем: а) счётчик может обновляться с непостоянной скоростью; б) на разных ядрах они могут быть не синхронизированы; в) процессор может переупорядочить инструкции, возвращающие значение счётчика.

  8. cntvct_el0 - Counter-timer Virtual Count регистр ARM процессора. В таблицу не попал, но был испытан на RPi 4B. Доступен в пользовательском режиме и показывает дискретность равную 18,5ns при длительности вызова 5.0ns (для процессора с частотой 1.8GHz это примерно 33 и 9 тактов). Пример кода для чтения этого регистра:

    inline uint64_t vi_get_cntvct()
    { uint64_t result;
    __asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(result));
    return result;
    }

Чуть подробнее про упомянутые выше проблемы TSC на x86 процессоре

Первая проблема - уже не проблема: «For Pentium 4 processors, Intel Xeon processors <…>; for Intel Core Solo and Intel Core Duo processors <...>; for the Intel Xeon processor 5100 series and Intel Core 2 Duo processors <...>; for Intel Core 2 and Intel Xeon processors <…>; for Intel Atom processors <…>: the time-stamp counter increments at a constant rate (Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4. Vol. 3B. P.18-42)

Вторую проблему рекомендуют уменьшить двумя способами:

  • Использовать сочетание инструкций RDTSCP и LFANCE: «The RDTSCP instruction is not a serializing instruction, but it does wait until all previous instructions have executed <…> If software requires RDTSCP to be executed prior to execution of any subsequent instruction (including any memory accesses), it can execute LFENCE immediately after RDTSCP» (Intel® 64 and IA-32 Architectures Software Developer’s Manual Combined Volumes: 1, 2A, 2B, 2C, 2D, 3A, 3B, 3C, 3D, and 4. Vol. 2B. P.4-553). Если я правильно понял, то рекомендуется нечто подобное коду ниже (при включении соответствующих заголовочных файлов эти intrinsic функции компилируются и в GCC, и в MSC):

    inline uint64_t vi_get_tsc()
    { uint32_t aux; // Будет удалён оптимизатором
    const uint64_t result = __rdtscp(&aux);
    _mm_lfence();
    return result;
    }

  • Другой популярный в интернете совет сериализации инструкций в ассемблере – использовать CPUID:

    inline uint64_t vi_get_tsc()
    { int32_t cpuInfo[4]; // Будет удалён оптимизатором
    uint32_t aux;
    const uint64_t result = __rdtscp(&aux);
    __cpuid(cpuInfo, 0);
    return result;
    }

Но! При использовании CPUID под Windows я столкнулся с непонятным поведением. Указанные в таблице 32ns для <ASM>::RDTSCP+CPUID были измерены в Safe mode. В обычном режиме Windows вызов инструкции CPUID увеличивал времена до 350ns(!!!). Похоже, что на проверенных мной машинах с Windows инструкция CPUID выполнялась программно. Наверно это из-за какого-то ПО, установленного на всех моих компьютерах. Разбираться пока не стал, надеюсь, кто-нибудь объяснит это в комментариях.

Как проводились измерения

Утилита запускалась с приоритетом ABOVENORMAL (nice -n -5). Перед всеми измерениями процессор прогревался полсекунды при 100% загрузке рабочего потока. Потом вызвалась функция std::this_thread::yield() это наивная попытка предотвратить переключение контекста если измерение укладывается в один квант времени (к сожалению, это не защищает от системных прерываний). Далее 5 раз вызывались задействованные в тесте функции и данные, чтобы исключить из измерений время подгрузки в кэш процессора. Замеры проводились дважды с разным числом повторов для учёта накладных расходов. Наконец все измерения производились по 20 раз и за результат бралось медианное значение (обычно оно очень близко к минимальному).

Итоги

Никаких открытий сделано не было (и не ожидалось). Для себя сделал выбор функции примерно таким образом (надеюсь на критику профи):

#include <stdint.h>

#ifdef _MSC_VER
#	include <intrin.h>
#	pragma intrinsic(__rdtscp, _mm_lfence)
#elif defined(__GNUC__)
#   include <x86intrin.h>
#endif

#ifdef __cplusplus
extern "C" {
#endif

#	if defined(_M_X64) || defined(_M_AMD64) || defined(__x86_64__) || defined(__amd64__)
		// MSC or GCC on Intel
		static inline uint64_t vi_tmGetTicks(void)
		{	uint32_t _; // будет удалён оптимизатором
			const uint64_t result = __rdtscp(&_);
			_mm_lfence();
			return result;
		}
#	elif __ARM_ARCH >= 8 // ARMv8 (RaspberryPi4)
		static inline uint64_t vi_tmGetTicks(void)
		{	uint64_t result;
			__asm__ __volatile__("mrs %0, cntvct_el0" : "=r"(result));
			return result;
		}
#	elif defined(_WIN32) // Windows on other platforms.
		static inline uint64_t vi_tmGetTicks(void)
		{	LARGE_INTEGER cnt;
			QueryPerformanceCounter(&cnt);
			return cnt.QuadPart;
		}
#	elif defined(__linux__) // Linux on other platforms
		static inline uint64_t vi_tmGetTicks(void)
		{	struct timespec ts;
			clock_gettime(CLOCK_MONOTONIC_RAW, &ts);
			return 1000000000ULL * ts.tv_sec + ts.tv_nsec;
		}
#	else
#		error "You need to define function(s) for your OS and CPU"
#	endif

#ifdef cplusplus
} // extern "C" {
#endif

Буду благодарен за конструктивные замечания.

Спасибо, что дочитали )

P.S.
Ещё пара логов с других машин:

i3-4005U CPU @ 1.70GHz
Start: 2024.05.05 20:11:01
Processor: Intel(R) Core(TM) i3-4005U CPU @ 1.70GHz

Collecting function properties... Done (872.0s )

Measured properties of time functions (median of 5 measurements):
Name:............................................ Discreteness:    +/- Duration:    +/- One tick:    +/-   Type:
vvvvvv                                                                                                          
<WIN>::timeGetTime():............................        15.6ms   0.5%    25.0ns   1.7%     1.0ms   0.1%    Wall
<WIN>::timeGetSystemTime():......................        15.6ms   0.5%    29.0ns   0.8%     1.0ms   0.1%    Wall
<WIN>::_time64():................................         1.0s    0.0%    18.0ns  21.0%     1.0s    0.0%    Wall
<WIN>::_time32():................................         1.0s    0.0%    18.0ns   4.6%     1.0s    0.0%    Wall
<WIN>::QueryUnbiasedInterruptTimePrecise():......       100.0ns   0.0%    42.0ns   0.0%   100.0ns   0.0%    Wall
<WIN>::QueryUnbiasedInterruptTime():.............        15.6ms   0.0%    15.0ns  38.0%   100.0ns   0.0%    Wall
<WIN>::QueryThreadCycleTime():...................       880.0ns   0.2%   910.0ns   0.3%   590.0ps   0.0%  Thread
<WIN>::QueryProcessCycleTime():..................         8.5us  10.0%     8.2us   4.6%   590.0ps   0.0% Process
<WIN>::QueryPerformanceCounter():................       100.0ns   0.0%    28.0ns   0.0%   100.0ns   0.0%    Wall
<WIN>::QueryInterruptTimePrecise():..............       100.0ns   0.2%    41.0ns   3.6%   100.0ns   0.0%    Wall
<WIN>::QueryInterruptTime():.....................        15.6ms   0.0%     2.1ns  86.0%   100.0ns   0.0%    Wall
<WIN>::NtQuerySystemTime():......................        15.6ms   0.0%     4.4ns  23.0%   100.0ns   0.0%    Wall
<WIN>::GetTickCount64():.........................        15.6ms   0.0%    14.0ns  57.0%     1.0ms   0.1%    Wall
<WIN>::GetTickCount():...........................        15.6ms   0.5%    13.0ns  42.0%     1.0ms   0.1%    Wall
<WIN>::GetThreadTimes():.........................        15.6ms   0.0%   890.0ns   0.3%   100.0ns   1.2%  Thread
<WIN>::GetSystemTimes() K+U:.....................         8.6ms   7.5%     3.2us   1.4%    25.0ns   0.9%    Wall
<WIN>::GetSystemTimePreciseAsFileTime():.........       100.0ns   0.0%    48.0ns   0.0%   100.0ns   0.0%    Wall
<WIN>::GetSystemTimeAsFileTime():................        15.6ms   0.0%    14.0ns  14.0%   100.0ns   0.0%    Wall
<WIN>::GetSystemTime():..........................        15.6ms   0.0%   130.0ns   0.3%   100.0ns   0.1%    Wall
<WIN>::GetProcessTimes():........................        15.6ms   0.0%     1.0us   1.8%   100.0ns   0.0% Process
<WIN>::GetLocalTime():...........................        15.6ms   0.0%   190.0ns   0.3%   100.0ns   0.1%    Wall
<C>  ::timespec_get(TIME_UTC):...................       100.0ns   0.0%    73.0ns   3.8%     1.0ns   0.0%    Wall
<C>  ::time():...................................         1.0s    0.0%    18.0ns   4.3%     1.0s    0.0%    Wall
<C>  ::clock():..................................         1.0ms   0.0%    85.0ns   4.1%     1.0ms   0.0%    Wall
<C++>::system_clock::now():......................       100.0ns   0.0%    45.0ns   0.0%   100.0ns   0.0%    Wall
<C++>::steady_clock::now():......................       100.0ns   0.0%    36.0ns   0.0%     1.0ns   0.0%    Wall
<C++>::high_resolution_clock::now():.............       100.0ns   0.0%    36.0ns   0.0%     1.0ns   0.0%    Wall
<BST>::cpu_timer.elapsed() wall:.................         1.1us   0.1%     1.1us   3.0%     1.0ns   0.0%    Wall
<BST>::cpu_timer.elapsed() user + system:........        15.6ms   0.0%     1.1us   0.1%     1.0ns   0.0% Process
<ASM>::RDTSC_INTRINSIC:..........................        14.2ns   0.0%    14.0ns   0.0%   590.0ps   0.0%    Wall
<ASM>::RDTSC_ASM:................................        14.2ns   1.0%    14.0ns   0.0%   590.0ps   0.0%    Wall
<ASM>::RDTSCP_INTRINSIC:.........................        19.5ns   0.0%    18.0ns   0.0%   590.0ps   0.0%    Wall
<ASM>::RDTSCP_ASM:...............................        21.2ns   1.1%    19.0ns   0.0%   590.0ps   0.0%    Wall
<ASM>::RDTSCP+LFENCE_INTRINSIC:..................        32.6ns   0.0%    31.0ns   0.0%   590.0ps   0.0%    Wall
<ASM>::RDTSCP+LFENCE_ASM:........................        33.6ns   0.0%    33.0ns   0.0%   590.0ps   0.0%    Wall
<ASM>::RDTSCP+CPUID_INTRINSIC:...................        79.0ns   0.0%    79.0ns   1.2%   590.0ps   0.0%    Wall
<ASM>::RDTSCP+CPUID_ASM:.........................        80.1ns   0.0%    79.0ns   0.1%   590.0ps   0.0%    Wall
<ASM>::MFENCE+LFENCE+RDTSC_INTRINSIC:............        36.8ns   0.0%    39.0ns   0.0%   590.0ps   0.0%    Wall
<ASM>::MFENCE+LFENCE+RDTSC_ASM:..................        40.7ns   0.0%    40.0ns   0.0%   590.0ps   0.0%    Wall
<ASM>::LFENCE+RDTSC_INTRINSIC:...................        21.1ns   2.2%    19.0ns   5.4%   590.0ps   0.0%    Wall
<ASM>::LFENCE+RDTSC_ASM:.........................        22.4ns   0.0%    21.0ns   0.0%   590.0ps   0.0%    Wall
<ASM>::CPUID+RDTSC_INTRINSIC:....................        64.9ns   0.7%    67.0ns   0.0%   590.0ps   0.0%    Wall
<ASM>::CPUID+RDTSC_ASM:..........................        68.4ns   0.0%    67.0ns   0.0%   590.0ps   0.0%    Wall


Notes:
	The std::chrono::system_clock::is_steady:          false;
	The std::chrono::steady_clock::is_steady:          true;
	The std::chrono::high_resolution_clock::is_steady: true;
	The std::chrono::system_clock::duration:          100.0ns;
	The std::chrono::steady_clock::duration:            1.0ns;
	The std::chrono::high_resolution_clock::duration:   1.0ns;
	The number of clock vi_tmGetTicks per second 'CLOCKS_PER_SEC': 1'000 (what is equivalent to 1.0ms)
	Frequency of the performance counter 'QueryPerformanceFrequency()': 10'000'000 (what is equivalent to 100.0ns);
	The timer resolution 'NtQueryTimerResolution()': 15.6ms. (from 0.5 to 15.6ms);
	The timer device to determine its resolution 'timeGetDevCaps()': from 1 to 1'000'000ms.;
	Periodic time adjustments 'GetSystemTimeAdjustment()':
		TimeAdjustment = 15.6ms;
		TimeIncrement = 15.6ms;
		TimeAdjustmentDisabled = true;

Time expend: 00:14:33

Cortex-A72 1.8GHz (Raspberry Pi 4B)
Start: 2024.05.29 16:08:46
Model name:                           Cortex-A72
CPU max MHz:                          1800.0000
CPU min MHz:                          600.0000
Computer: 'RaspberryPi4'

Collecting function properties... Done (1.5ks)

Measured properties of time functions (median of 20 measurements):
Name:............................................ Discreteness:    +/- Duration:    +/- One tick:    +/-   Type:
vvvvvv                                                                                                          
<LNX>::times(tms):...............................        10.0ms   0.0%   870.0ns   0.5%    10.0ms   0.8% Process
<LNX>::times(nullptr):...........................        10.0ms   0.0%   400.0ns   0.0%    10.0ms   0.0%    Wall
<LNX>::gettimeofday():...........................         1.0us   0.0%    44.0ns   0.3%     1.0us   0.0%    Wall
<LNX>::getrusage(RUSAGE_THREAD):.................         4.0ms   0.0%   680.0ns   0.3%     1.0us   0.0%  Thread
<LNX>::getrusage(RUSAGE_SELF):...................         1.1us   0.4%     1.1us   0.2%     1.0us   0.0% Process
<LNX>::clock_gettime(CLOCK_THREAD_CPUTIME_ID):...       747.0ns   0.9%   750.0ns   0.3%     1.0ns   0.0%  Thread
<LNX>::clock_gettime(CLOCK_TAI):.................        48.3ns   0.1%    45.0ns   0.1%     1.0ns   0.0%    Wall
<LNX>::clock_gettime(CLOCK_REALTIME_COARSE):.....         4.0ms   0.0%    18.0ns   0.2%     1.0ns   0.0%    Wall
<LNX>::clock_gettime(CLOCK_REALTIME):............        48.3ns   0.1%    45.0ns   0.3%     1.0ns   0.0%    Wall
<LNX>::clock_gettime(CLOCK_PROCESS_CPUTIME_ID):..       773.0ns   0.9%   790.0ns   0.5%     1.0ns   0.0% Process
<LNX>::clock_gettime(CLOCK_MONOTONIC_RAW):.......        48.5ns   7.0%    46.0ns  12.0%     1.0ns   0.0%    Wall
<LNX>::clock_gettime(CLOCK_MONOTONIC_COARSE):....         4.0ms   0.0%    18.0ns   0.2%     1.0ns   0.0%    Wall
<LNX>::clock_gettime(CLOCK_MONOTONIC):...........        48.3ns   0.2%    45.0ns   0.2%     1.0ns   0.0%    Wall
<LNX>::clock_gettime(CLOCK_BOOTTIME):............        48.3ns   0.2%    45.0ns   2.8%     1.0ns   0.0%    Wall
<C>  ::timespec_get(TIME_UTC):...................        47.3ns   9.5%    44.0ns  12.0%     1.0ns   0.0%    Wall
<C>  ::time():...................................         1.0s    0.0%    22.0ns   2.0%     1.0s    0.0%    Wall
<C>  ::clock():..................................         1.0us   0.0%   780.0ns   0.3%     1.0us   0.0% Process
<C++>::system_clock::now():......................        46.7ns  12.0%    44.0ns   7.6%     1.0ns   0.0%    Wall
<C++>::steady_clock::now():......................        46.7ns  13.0%    43.0ns   0.6%     1.0ns   0.0%    Wall
<C++>::high_resolution_clock::now():.............        46.7ns  12.0%    43.0ns   1.3%     1.0ns   0.0%    Wall
<ASM>::MRS:......................................        18.9ns   0.2%     5.0ns   0.7%    19.0ns   0.0%    Wall


Notes:
	The std::chrono::system_clock::is_steady:          false;
	The std::chrono::steady_clock::is_steady:          true;
	The std::chrono::high_resolution_clock::is_steady: false;
	The std::chrono::system_clock::duration:            1.0ns;
	The std::chrono::steady_clock::duration:            1.0ns;
	The std::chrono::high_resolution_clock::duration:   1.0ns;
	The number of clock vi_tmGetTicks per second 'CLOCKS_PER_SEC': 1'000'000 (what is equivalent to 1.0us)
	The number of clock vi_tmGetTicks per second 'sysconf(_SC_CLK_TCK)': 100 (10ms);
	Resolution (precision) 'clock_getres(CLOCK_REALTIME)': 1.0ns;
	Resolution (precision) 'clock_getres(CLOCK_MONOTONIC)': 1.0ns;
	Resolution (precision) 'clock_getres(CLOCK_PROCESS_CPUTIME_ID)': 1.0ns;
	Resolution (precision) 'clock_getres(CLOCK_THREAD_CPUTIME_ID)': 1.0ns;

Time expend: 00:25:14

Комментарии (13)


  1. jaha33
    03.06.2024 05:46
    +1

    А на винде точность измерения разве не зависит от материнской платы и самой версии windows? Несколько лет назад сталкивался необходимостью замера интервалов в мкс на windows, точно замера плавала на десятки мкс, как выяснилось, для более точных замеров нужна win8 с какой версией обновления и какие то определённые мат платы

    Пробовал измерять сначала функциями c#, потом win api


    1. tixo Автор
      03.06.2024 05:46

      А на винде точность измерения разве не зависит от материнской платы и самой версии windows?

      Это зависит от функции. Windows "загрубляет" дискретность ряда функций до 100ns, но если не может обеспечить и такую точность то, например, на старом 32-х битном Intel Atom Z2760@1.80GHz дискретность всех измеренных мной функций оказалась не лучше 570ns. Т.е. QueryPerformanceCounter при документированных единицах измерения 100ns смогла измерить интервал времени лишь в 5,7 раза длиннее. Такую же дискретность на этой машине выдали и все современные стандартные функции С и С++.

      Многие ф-ии WinAPI (например GetSystemTime) завязаны на частоту прерывания системных часов которая по умолчанию равна 64 тикам в секунду независимо от железа, но может изменяться программно с помощью ExSetTimerResolution.

      Кстати, частота прерывания системных часов может изменяться, например, музыкальным или видео плеером, что иногда приводит к неприятным сюрпризам.


  1. unreal_undead2
    03.06.2024 05:46
    +1

    Похоже, что на проверенных мной машинах с Windows инструкция CPUID выполнялась программно.

    Включенный Hyper-V ?


    1. tixo Автор
      03.06.2024 05:46

      надеюсь, кто-нибудь объяснит это в комментариях

      Не зря надеялся )

      Наверно, на всех машинах установлен как минимум MS Sandbox. Но для меня неожиданно, что включение Hyper-V влияет на процессы запущенные прямо на хосте, а не в VM.

      IMHO с такими задержками CPUID для профилирования не годится. Но если погуглить RDTSC, то чаще всего для сериализации инструкций предлагают именно его.


      1. unreal_undead2
        03.06.2024 05:46
        +2

        Hyper V - гипервизор первого типа, так что при включении "хостовая" Windows реально бежит под ним.

        А так особого смысла в сериализации не вижу. Если делать отсечки до и после более-менее тяжёлого цикла - особой разницы (в процентах от полученного значения) в измерениях не будет, если же вставлять измерение внутрь цикла - даже сама по себе rdtsc будет влиять на скорость работы и результат измерений, с сериализацией будет ещё хуже - так что получить осмысленный результат, по которому можно делать выводы о работе кода без измеряющей обвзяки, достаточно сложно.


  1. SpiderEkb
    03.06.2024 05:46

    Со временем - тут у кого на что фантазии хватит...

    Под DOS, помнится, еще в 90-х года был RTC - real-time clock который "тикал" 1024 раза в секунду и мог дергать соответствующее прерывание (номер уже не вспомню за давностью лет, помню, что оно было практически не документировано и разбираться сними приходилось перерыв кучу информации).

    Не так давно писал тут про Standard Time на платформе IBM i - тоже достаточно нестандартное решение... Точность 1мкс (плюс есть еще 12 "бит уникальности").


    1. unreal_undead2
      03.06.2024 05:46

      Всё таки максимальная частота таймера на IBM PC около мегагерца - https://en.wikibooks.org/wiki/X86_Assembly/Programmable_Interval_Timer


      1. SpiderEkb
        03.06.2024 05:46

        Я несколько отошел от x86 архитектуры в последние годы.

        Помнится, под виндой еще со времен 3.11 был multimedia timer который как минимум миллисекунды достаточно точно считал.

        Но сильно глубоко в это не вникал - просто не попадалось задач где нужно было бы точно измерять абсолютное время (или временные интервалы)


  1. e-zig
    03.06.2024 05:46

    Discreteness - минимальный прирост показаний функции (очень грубо говоря это точность измерения)

    Похоже на "разрешение".


    1. tixo Автор
      03.06.2024 05:46

      Согласен, наверно слово resolution подошло бы лучше.


  1. Gumanoid
    03.06.2024 05:46

    clock_gettime(CLOCK_MONOTONIC_RAW) [Linux] – видимо лучший вариант для тайминга в LinuxAPI. Точность и время выполнения около 22ns.

    Только если запускать нативно. В клауде, в зависимости от настроек гипервизора, ситуация может быть другой. Например на моей системе (под KVM) CLOCK_MONOTONIC выполняется за 30 нс, а вот CLOCK_MONOTONIC_RAW за 400 нс (делает системный вызов).


    1. tixo Автор
      03.06.2024 05:46

      Интересно!
      Прогнал утилиту на VPS под KVM:

      Частота процессора в два раза ниже, и время в два раза больше.


  1. randomfoto
    03.06.2024 05:46

    Попросил DALL-E нарисовать программиста, выбирающего секундомер. Вот результат )

    Программист думает: И как я этой кучей безменов буду измерять время?