• Advertisement
Sign in to follow this  

Interpreting the CPU clock.

This topic is 4307 days old which is more than the 365 day threshold we allow for new replies. Please post a new topic.

If you intended to correct an error in the post then please contact us.

Recommended Posts

This was raised in another thread (somewhere) but I cannot track it down. Anyway, I'll get straight to the point... I've been using QueryPerformanceFrequency and QueryPerformanceCounter for some time now in obtaining 'accurate' timings of code. However, in response to the original thread, someone suggested using the following asm code:
int time;
__asm
{
   cpuid
   rdtsc
   mov time, eax
}

I've been skimming through the Intel processor manuals, which indicates that the integer 'time' would contain the low-order 32 bits of the MSR (Model Specific Register), which in turn contains the number of elapsed clock cycles since the processor started. However, I'm trying to convert the number of clock cycles, between two snap-shots of the MSR, into microseconds and I was using QueryPerformanceFrequency to do it. But I seem to be getting some strange results when I time a null statement in Release builds: 0.18 microseconds using the rdtsc instruction (which is way too slow for a 3GHz PC like mine) 0.0019 microseconds using QueryPerformanceCounter (which seems nominal) Am I right in assuming rdtsc clock cycles and QueryPerformanceFrequency counts per second are actually related??? It's no big deal at the end of the day as I can always just stick to QueryPerformanceCounter. It just seemed (reading from the Intel manuals) that rdtsc would provide even more accurate timings, considering the cpuid instruction forces the CPU to finish all pending operations before placing the time-stamp in the MSR.

Share this post


Link to post
Share on other sites
Advertisement
Quote:
Original post by TerrorFLOP
Am I right in assuming rdtsc clock cycles and QueryPerformanceFrequency counts per second are actually related???


No, not necessarily.

Share this post


Link to post
Share on other sites
It's okay. I got it sorted now :-)

Seemed I had to take into account the high-order bits of the MSR as follows:


inline LONGLONG CPU_Counter ()
{
LARGE_INTEGER counter;
__asm
{
cpuid
rdtsc
mov counter.LowPart, eax
mov counter.HighPart, edx
}
return counter.QuadPart;
}



Having run some tests, this approach to reading the CPU clock seems to be slightly more accurate than QueryPerformanceCounter, by an extra decimal place of accuracy (CPU_Counter seems accurate to within a tenth of a nano-second compared to QueryPerformanceCounter's accuracy of about one nano-second).

So I guess rdtsc clock cycles and QueryPerformanceCounter counts are indeed the same thing (which makes sense!).

Share this post


Link to post
Share on other sites
Doesn't query performance count (and all this stuff) break down in the face of speed step / power now technology?

I used this same syste in all my previous games, but when I got my Turion 64 laptop suddenly I would get unpredictable behavior (It can run as slow as 800 MHz or as fast as 1.6GHz). I never tracked this down to be 100% sure it was the timing system's fault, but I highly suspect it is.

Let me know if you find anything about this - especially if there is a newer prefered method that works in all of these enviroments.

Share this post


Link to post
Share on other sites
Quote:
Original post by LorenzoGatti
What is the initial cpuid instruction for?

It ensures that no other instructions are still being processed.

Share this post


Link to post
Share on other sites
Quote:
Original post by Xai
Doesn't query performance count (and all this stuff) break down in the face of speed step / power now technology?


AFAIK, QueryPerformanceCounter/Frequency should compensate as long as the proper cpu HAL drivers are installed. However, directly reading the MSR in your own inline assembly does not compensate. Most newer processors now have a high performance programmable timer that runs (mostly) independently of the cpu clock. QueryPerformanceCounter could utilize that to compensate if it is available.

Share this post


Link to post
Share on other sites
Quote:
Original post by Xai
Doesn't query performance count (and all this stuff) break down in the face of speed step / power now technology?

I used this same syste in all my previous games, but when I got my Turion 64 laptop suddenly I would get unpredictable behavior (It can run as slow as 800 MHz or as fast as 1.6GHz). I never tracked this down to be 100% sure it was the timing system's fault, but I highly suspect it is.

Let me know if you find anything about this - especially if there is a newer prefered method that works in all of these enviroments.


I'm pretty sure the Intel processor manuals would shed some light on this, as it's pretty in-depth and highly technical (collectively, they're almost 3000 pages!!!)

But since you're using an AMD, I doubt that these manuals could help.

Check out the AMD processor manuals if they're available.

Share this post


Link to post
Share on other sites
Quote:
Original post by TerrorFLOP
Quote:
Original post by Xai
Doesn't query performance count (and all this stuff) break down in the face of speed step / power now technology?

I used this same syste in all my previous games, but when I got my Turion 64 laptop suddenly I would get unpredictable behavior (It can run as slow as 800 MHz or as fast as 1.6GHz). I never tracked this down to be 100% sure it was the timing system's fault, but I highly suspect it is.

Let me know if you find anything about this - especially if there is a newer prefered method that works in all of these enviroments.


I'm pretty sure the Intel processor manuals would shed some light on this, as it's pretty in-depth and highly technical (collectively, they're almost 3000 pages!!!)

But since you're using an AMD, I doubt that these manuals could help.

Check out the AMD processor manuals if they're available.


This is a "normal" behavior - no need ti check the intel or AMD manuals. When the labtop wants to reduce its energy , it may reduces the CPU frequency. I believe you can change this behavior using the Windows control pannel. Now, if you know the correct frequency at a given time, the problem vanishes.

Regards,

Share this post


Link to post
Share on other sites
??? exactly how would you know the correct frequency?

No offense people, but I'm not looking for hints at things that might work when I find them ... that might be appropriate if I was 80% done with some task and just needed to find a way to get it working ... but that's not the way I work.

I'm looking for "the right answer" .. or any of the set of "guaranteed to work correctly in all cases" answers.

Surely someone here has some experience in this area and can either point to some links on how to do this type of thing, answer definitively if it works a certain way (officially), or express the pain they had to go through to solve the problem themselves and therefore why no simple - normal - dependable solution exists as far as they know.

I have no interests in learning and repeating half solutions to solvable problems. But if the problem hasn't been solved (cleanly) - then maybe I'll look into finding a correct solution myself.

Share this post


Link to post
Share on other sites
QueryPerformanceCounter / Frequency will work correctly a winxp patch applied (without patch it's not dualcore safe) - check the microsoft site.
Will give problems with speedstep.

rdtsc is not dualcore safe (but can be made dualcore safe, check the intel
site for apic) - most people will advise against using it - but you don't get
more precision than with rdtsc :)
Obviously rdtsc will also suffer from problems with speedstep
If you know the cpu-speed (intel site has an article on how to do that),
you can convert rdtsc values to seconds.


timeGetTime only has millisecond precision but seems to always work (although it's not good for profiling code).

some more threads on the topic:
http://www.gamedev.net/community/forums/topic.asp?topic_id=370281
http://www.gamedev.net/community/forums/topic.asp?topic_id=368417
http://www.gamedev.net/community/forums/topic.asp?topic_id=369437

[Edited by - Kitt3n on April 5, 2006 4:29:24 PM]

Share this post


Link to post
Share on other sites
Greetings.

Quote:
??? exactly how would you know the correct frequency?

AFAICS this is not possible without OS support. As soon as the OS decides to throttle the CPU to a different frequency, it needs to latch the current cycle counter and convert that to seconds. This serves as the current timebase, to which cycles elapsed from then on are added (divided by current CPU freq).
A user program would not know exactly when the throttling took place; some cycles would then be incorrectly billed under the new or old frequency, thus throwing off results more than any timer can afford.

Quote:
I'm looking for "the right answer" .. or any of the set of "guaranteed to work correctly in all cases" answers.

Unfortunately this is (not yet) to be had in the field of timing.
See article for making the best of things.

Here's my crack at seeing if SpeedStep is active; if so, RDTSC cannot safely be used.
Comments welcome.

static void win_check_speedstep()
{
WIN_SAVE_LAST_ERROR;

// CallNtPowerInformation
// (manual import because it's not supported on Win95)
NTSTATUS (WINAPI *pCNPI)(POWER_INFORMATION_LEVEL, PVOID, ULONG, PVOID, ULONG) = 0;
HMODULE hPowrprofDll = LoadLibrary("powrprof.dll");
*(void**)&pCNPI = GetProcAddress(hPowrprofDll, "CallNtPowerInformation");
if(pCNPI)
{
// most likely not speedstep-capable if these aren't supported
SYSTEM_POWER_CAPABILITIES spc;
if(pCNPI(SystemPowerCapabilities, 0,0, &spc,sizeof(spc)) == STATUS_SUCCESS)
if(!spc.ProcessorThrottle || !spc.ThermalControl)
cpu_speedstep = 0;

// probably speedstep if cooling mode active.
// the documentation of PO_TZ_* is unclear, so we can't be sure.
SYSTEM_POWER_INFORMATION spi;
if(pCNPI(SystemPowerInformation, 0,0, &spi,sizeof(spi)) == STATUS_SUCCESS)
if(spi.CoolingMode != PO_TZ_INVALID_MODE)
cpu_speedstep = 1;

// definitely speedstep if any throttle is less than 100%.
PROCESSOR_POWER_INFORMATION ppi[MAX_CPUS];
if(pCNPI(ProcessorInformation, 0,0, ppi,sizeof(ppi)) == STATUS_SUCCESS)
{
const PROCESSOR_POWER_INFORMATION* p = ppi;
for(int i = 0; i < MIN(cpus, MAX_CPUS); i++, p++)
if(p->MhzLimit != p->MaxMhz || p->CurrentMhz != p->MaxMhz)
{
cpu_speedstep = 1;
break;
}
}
}
FreeLibrary(hPowrprofDll);
// this is most likely the only reference,
// so don't free it (=> unload) until done with the DLL.

// CallNtPowerInformation not available, or none of the above apply:
// don't know yet (for certain, at least).
if(cpu_speedstep == -1)
{
// check if running on a laptop
HW_PROFILE_INFO hi;
GetCurrentHwProfile(&hi);
bool is_laptop = !(hi.dwDockInfo & DOCKINFO_DOCKED) ^ !(hi.dwDockInfo & DOCKINFO_UNDOCKED);
// both flags set <==> this is a desktop machine.
// both clear is unspecified; we assume it's not a laptop.
// NOTE: ! is necessary (converts expression to bool)

// we'll guess SpeedStep is active if on a laptop.
// ia32 code will get a second crack at it.
cpu_speedstep = (is_laptop)? 1 : 0;
}

WIN_RESTORE_LAST_ERROR;
}


static void ia32_check_speedstep()
{
if(vendor == INTEL)
{
if(ia32_cap(EST))
cpu_speedstep = 1;
}
else if(vendor == AMD)
{
u32 regs[4];
if(ia32_cpuid(0x80000007, regs))
if(regs[EDX] & POWERNOW_FREQ_ID_CTRL)
cpu_speedstep = 1;
}
}



HTH+HAND

[Edited by - Jan Wassenberg on April 5, 2006 8:31:26 PM]

Share this post


Link to post
Share on other sites
Sign in to follow this  

  • Advertisement