Насколько мне известно, инструкция RDTSC является чрезвычайно точной.
Я думаю, что если вы ищете точное число циклов, то в случае коротких загрузочных разделов вы можете столкнуться с проблемами одновременности, что Mysticialупомянуто ...
Но если ультра-ультра-ультра-ультра-точность не является препятствием ... то есть, если вы можете выжить, зная, что для определенных сценариев ваш результат отклоняется ...Я не знаю ... скажем, от 9 до 80 циклов ... тогда я почти уверен, что вы все еще можете получить очень точные результаты с помощью RDTSC ... особенно, если учесть, что 9 - 80, деленное на 3,2 миллиарда, - очень крошечное число :)
Числа 9 и 80 были выбраны немного произвольно (и, возможно, вы не используете скорость процессора 3,2 ГГц), так как я не знаю точно, что такое количество ошибок ... но я уверен, что этов этом приблизительном примере:)
Вот выдержка RDTSC из функции таймера, которую я использую:
//High-Rez Setup
__asm
{
push eax
push edx
rdtsc
mov [AbsoluteLow],eax
mov [AbsoluteHigh],edx
pop edx
pop eax
}
на самом деле я пойду дальше и опубликую все это ... этот код предполагает, чтотипа "двойной"является 64-битным числом с плавающей запятой ... которое не может быть универсальным предположением компилятора / архитектуры:
double AbsoluteTime;
double AbsoluteResolution;
ulong AbsoluteLow;
ulong AbsoluteHigh;
void Get_AbsoluteTime (double *time)
{
//Variables
double current, constant;
double lower, upper;
ulong timelow, timehigh;
//Use the Intel RDTSC
__asm
{
push eax
push edx
rdtsc
sub eax, [AbsoluteLow]
sbb edx, [AbsoluteHigh]
mov [timelow], eax
mov [timehigh], edx
pop edx
pop eax
}
//Convert two 32bit registers to a 64-bit floating point
//Multiplying by 4294967296 is similar to left-shifting by 32 bits
constant = 4294967296.0;
lower = (double) timelow;
upper = (double) timehigh;
upper *= constant;
current = lower + upper;
current /= AbsoluteResolution;
current += AbsoluteTime;
*time = current;
}
void Set_AbsoluteTime (double time, double scale)
{
//Variables
double invScale;
//Setup
AbsoluteTime = time;
//High-Rez Setup
__asm
{
push eax
push edx
rdtsc
mov [AbsoluteLow],eax
mov [AbsoluteHigh],edx
pop edx
pop eax
}
//Fetch MHZ
if (1)
{
//Local Variables
int nv;
ulong mhz;
char keyname[2048];
//Default assumption of 3.2ghz if registry functions fail
mhz = 3200;
//Registry Key
sprintf (keyname, "HARDWARE\\DESCRIPTION\\System\\CentralProcessor\\0");
nv = Reg_Get_ValueDW (keyname, "~MHz", (ulong *)&mhz);
//Transform into cycles per second
mhz *= 1000000;
//Calculate Speed Stuff
AbsoluteResolution = (double) mhz;
invScale = 1.0;
invScale /= scale;
AbsoluteResolution *= invScale;
}
}
Вы хотите где-нибудь вызвать Set_AbsoluteTime перед использованием функций Get ... без первого начального вызоваУстановите, Gets будет возвращать ошибочные результаты ... но как только этот разовый вызов сделан, вы можете идти ...
вот пример:
void Function_to_Profile (void)
{
//Variables
double t1, t2, TimeElapsed;
//Profile operations
Get_AbsoluteTime (&t1);
...do stuff here...
Get_AbsoluteTime (&t2);
//Calculate Elapsed Time
TimeElapsed = (t2 - t1);
//Feedback
printf ("This function took %.11f seconds to run\n", TimeElapsed);
}
void main (void)
{
Set_AbsoluteTime (0.000, 1.000);
Function_to_Profile();
}
если по какой-то причине вычтобы измерения времени проходили в обратном направлении на половинной скорости (возможно, удобно для программирования игр), начальный вызов будет следующим: Set_AbsoluteTime (0.000, -0.500);
первый параметр для Set - это базовое время, которое получаетдобавлено ко всем результатам
Я почти уверен, что эти функции более точны, чем большинство общедоступных таймеров Windows API с высоким разрешением, которые существуют в настоящее время ... Я думаю, что на быстрых процессорах ошибка меньше 1 наносекунды, ноЯ не уверен на 100%:)
они достаточно точны для моих целей, но учтите, что стандартная инициализация40 байтов pre-amble (составленных из 'current', 'constant', 'lower', 'upper', 'timelow', 'timehigh'), которые большинство компиляторов C установит в 0xCC или 0xCD, будут использовать некоторые циклы ... какбудет ли математика выполняться в нижней части каждого вызова Get_AbsoluteTime ...
, поэтому для по-настоящему первозданной точности вам лучше всего создать кадры того, что вы хотите профилировать в «встроенных» RDTSC ... Я бы использовалрасширенный регистр x64 для хранения ответа для последующих операций вычитания вместо того, чтобы возиться с более медленным доступом к памяти ...
, как, например, что-то вроде этого ... это в основном концепция, кстати, потому что технически VC2010не позволяет вам генерировать x64-Assembly с помощью ключевого слова __asm :( ... но я думаю, что это даст вам концептуальную дорогу:
typedef unsigned long long ulonglong;
ulonglong Cycles;
__asm
{
push rax
push rdx
rdtsc
mov r9, edx
shl r9, 32
and rax, 0xFFFFFFFF
or r9, rax
pop rdx
pop rax
}
...Perform stuff to profile here
__asm
{
push rax
push rdx
rdtsc
mov r10, edx
shl r10, 32
and rax, 0xFFFFFFFF
or r10, rax
sub r10, r9
mov qword ptr [Cycles], r10
pop rdx
pop rax
}
printf ("The code took %s cycles to execute\n", ULONGLONG_TO_STRING (Cycles));
с этим кодом, я думаю, что окончательный ответчисло прошедших циклов будет в r10, 64-битном регистре ... или в Cycles, 64-битном целом числе без знака ... с горсткой циклов ошибки, вызванной биt операции сдвига и стека ... при условии, что профилируемый код не уничтожает r9 и r10 хе-хе ... Я забыл, что наиболее стабильны регистры расширенного x64 ...
также "и rax, 0xFFFFFFFF "может быть посторонним, потому что я не могу вспомнить, обнуляет ли RDTSC верхние 32 бита RAX или нет ... поэтому я включил эту операцию И на всякий случай:)