Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Need some help in getting the CPU Frequency

Tags:

c++

c#

visual-c++

I'm trying to make a C# software that reads information about the CPU and displays them to the user (just like CPU-Z). My current problem is that I've failed to find a way to display the CPU frequency.

At first I tried the easy way using the Win32_Processor class. It proved very efficient, except if the CPU is overclocked (or underclocked).

Then, I discovered that my Registry contain at HKLM\HARDWARE\DESCRIPTION\System\CentralProcessor\0 the "standard" clock of the CPU (even if overclocked). The problem is that in modern CPUs, the Core Multiplier is decreasing when the CPU does not need it's full power, so the CPU Frequency is also changing, but the Value in the Registry remains the same.

My next step was trying to use the RdTSC to actually calculate the CPU Frequency. I used C++ for this because I can embed it in a C# project if the method is working. I found the next code at http://www.codeproject.com/Articles/7340/Get-the-Processor-Speed-in-two-simple-ways but the problem was the same: the program gives me only the maximum frequency (like in the registry value, 1-2 Mhz difference) and it also looks like it loads the CPU more than it should (I even had CPU load spikes).

#include "stdafx.h"
#include <windows.h>
#include <cstdlib>
#include "intrin.h"
#include <WinError.h>
#include <winnt.h>

float ProcSpeedCalc() {

#define RdTSC __asm _emit 0x0f __asm _emit 0x31

    // variables for the clock-cycles:
    __int64 cyclesStart = 0, cyclesStop = 0;
    // variables for the High-Res Preformance Counter:
    unsigned __int64 nCtr = 0, nFreq = 0, nCtrStop = 0;

    // retrieve performance-counter frequency per second:
    if(!QueryPerformanceFrequency((LARGE_INTEGER *) &nFreq))
        return 0;

    // retrieve the current value of the performance counter:
    QueryPerformanceCounter((LARGE_INTEGER *) &nCtrStop);

    // add the frequency to the counter-value:
    nCtrStop += nFreq;


    _asm
    {// retrieve the clock-cycles for the start value:
        RdTSC
        mov DWORD PTR cyclesStart, eax
        mov DWORD PTR [cyclesStart + 4], edx
    }

    do{
    // retrieve the value of the performance counter
    // until 1 sec has gone by:
         QueryPerformanceCounter((LARGE_INTEGER *) &nCtr);
      }while (nCtr < nCtrStop);

    _asm
    {// retrieve again the clock-cycles after 1 sec. has gone by:
        RdTSC
        mov DWORD PTR cyclesStop, eax
        mov DWORD PTR [cyclesStop + 4], edx
    }

    // stop-start is speed in Hz divided by 1,000,000 is speed in MHz
    return    ((float)cyclesStop-(float)cyclesStart) / 1000000;
}


int _tmain(int argc, _TCHAR* argv[])
{

    while(true)
    {
        printf("CPU frequency = %f\n",ProcSpeedCalc());
        Sleep(1000);
    }

    return 0;
}

I should also mention that I've tested the last method on an AMD CPU. I've also tried some other codes for the RdTSC method, but none working correctly.

Finally, I've tried to understand the code used to make this program https://code.google.com/p/open-hardware-monitor/source/browse/ , but it was much too complex for me.

So, my question is: how to determine the CPU Frequency in real-time (even when the CPU is overclocked) using C++ or C# ? I know that this question was asked a lot of times, but none actually answers my question.

like image 955
TheQuestioner Avatar asked Jul 21 '13 15:07

TheQuestioner


2 Answers

Yes, that code sits and busy-waits for an entire second, which has causes that core to be 100% busy for a second. One second is more than enough time for dynamic clocking algorithms to detect load and kick the CPU frequency up out of power-saving states. I wouldn't be surprised if processors with boost actually show you a frequency above the labelled frequency.

The concept isn't bad, however. What you have to do is sleep for an interval of about one second. Then, instead of assuming the RDTSC invocations were exactly one second apart, divide by the actual time indicated by QueryPerformanceCounter.

Also, I recommend checking RDTSC both before and after the QueryPerformanceCounter call, to detect whether there was a context switch between RDTSC and QueryPerformanceCounter which would mess up your results.


Unfortunately, RDTSC on new processors doesn't actually count CPU clock cycles. So this doesn't reflect the dynamically changing CPU clock rate (it does measure the nominal rate without busy-waiting, though, so it is a big improvement over the code provided in the question).

  • Bruce Dawson explained this in a blog post

So it looks like you'd need to access model-specific registers after all. Which can't be done from user-mode. The OpenHardwareMonitor project has both a driver that can be used and code for the frequency calculations


float ProcSpeedCalc()
{
    /*
        RdTSC:
          It's the Pentium instruction "ReaD Time Stamp Counter". It measures the
          number of clock cycles that have passed since the processor was reset, as a
          64-bit number. That's what the <CODE>_emit</CODE> lines do.
    */
    // Microsoft inline assembler knows the rdtsc instruction.  No need for emit.

    // variables for the CPU cycle counter (unknown rate):
    __int64 tscBefore, tscAfter, tscCheck;
    // variables for the Performance Counter 9steady known rate):
    LARGE_INTEGER hpetFreq, hpetBefore, hpetAfter;


    // retrieve performance-counter frequency per second:
    if (!QueryPerformanceFrequency(&hpetFreq)) return 0;

    int retryLimit = 10;    
    do {
        // read CPU cycle count
        _asm
        {
            rdtsc
            mov DWORD PTR tscBefore, eax
            mov DWORD PTR [tscBefore + 4], edx
        }

        // retrieve the current value of the performance counter:
        QueryPerformanceCounter(&hpetBefore);

        // read CPU cycle count again, to detect context switch
        _asm
        {
            rdtsc
            mov DWORD PTR tscCheck, eax
            mov DWORD PTR [tscCheck + 4], edx
        }
    } while ((tscCheck - tscBefore) > 800 && (--retryLimit) > 0);

    Sleep(1000);

    do {
        // read CPU cycle count
        _asm
        {
            rdtsc
            mov DWORD PTR tscAfter, eax
            mov DWORD PTR [tscAfter + 4], edx
        }

        // retrieve the current value of the performance counter:
        QueryPerformanceCounter(&hpetAfter);

        // read CPU cycle count again, to detect context switch
        _asm
        {
            rdtsc
            mov DWORD PTR tscCheck, eax
            mov DWORD PTR [tscCheck + 4], edx
        }
    } while ((tscCheck - tscAfter) > 800 && (--retryLimit) > 0);

    // stop-start is speed in Hz divided by 1,000,000 is speed in MHz
    return (double)(tscAfter - tscBefore) / (double)(hpetAfter.QuadPart - hpetBefore.QuadPart) * (double)hpetFreq.QuadPart / 1.0e6;
}

Most compilers provide an __rdtsc() intrinsic, in which case you could use tscBefore = __rdtsc(); instead of the __asm block. Both methods are platform- and compiler-specific, unfortunately.

like image 138
Ben Voigt Avatar answered Oct 19 '22 23:10

Ben Voigt


The answer depends on what you really want to know.

If your goal is to find the operating frequency of some particular application that you are currently running then this is a hard problem which requires administrator/root privileges to access model specific registers and maybe even access to the BIOS. You can do this with CPU-Z on Windows or powertop on Linux.

However, if you just want to know the operating frequency of your processor for one or many threads under load so that you could for example calculate the peak flops (which is why I care about this) then this can be done with more or less general code which does not need administrator privileges.

I got the idea from the code by Bruce Dawson at http://randomascii.wordpress.com/2013/08/06/defective-heat-sinks-causing-garbage-gaming/. I mostly extended his code to work with multiple threads using OpenMP.

I have tested this on Linux and Windows on Intel processors including, Nahalem, Ivy Bridge, and Haswell with one socket up to four sockets (40 threads). The results all deviate less than 0.5% from the correct answer.

I described how to determine the frequency here how-can-i-programmatically-find-the-cpu-frequency-with-c so I won't repeat all the details.

like image 21
Z boson Avatar answered Oct 19 '22 23:10

Z boson