Assembly CPU frequency measuring algorithm

后端 未结 10 1977
时光取名叫无心
时光取名叫无心 2020-12-01 14:29

What are the common algorithms being used to measure the processor frequency?

相关标签:
10条回答
  • 2020-12-01 14:50

    On Intel CPUs, a common method to get the current (average) CPU frequency is to calculate it from a few CPU counters:

    CPU_freq = tsc_freq * (aperf_t1 - aperf_t0) / (mperf_t1 - mperf_t0)
    

    The TSC (Time Stamp Counter) can be read from userspace with dedicated x86 instructions, but its frequency has to be determined by calibration against a clock. The best approach is to get the TSC frequency from the kernel (which already has done the calibration).

    The aperf and mperf counters are model specific registers MSRs that require root privileges for access. Again, there are dedicated x86 instructions for accessing the MSRs.

    Since the mperf counter rate is directly proportional to the TSC rate and the aperf rate is directly proportional to the CPU frequency you get the CPU frequency with the above equation.

    Of course, if the CPU frequency changes in your t0 - t1 time delta (e.g. due due frequency scaling) you get the average CPU frequency with this method.

    I wrote a small utility cpufreq which can be used to test this method.

    See also:

    • [PATCH] x86: Calculate MHz using APERF/MPERF for cpuinfo and scaling_cur_freq. 2016-04-01, LKML
    • Frequency-invariant utilization tracking for x86. 2020-04-02, LWN.net
    0 讨论(0)
  • "lmbench" provides a cpu frequency algorithm portable for different architecture.

    It runs some different loops and the processor's clock speed is the greatest common divisor of the execution frequencies of the various loops.

    this method should always work when we are able to get loops with cycle counts that are relatively prime.

    http://www.bitmover.com/lmbench/

    0 讨论(0)
  • 2020-12-01 14:52

    A quick google on AMD and Intel shows that CPUID should give you access to the CPU`s max frequency.

    0 讨论(0)
  • 2020-12-01 14:55

    I'm gonna date myself with various details in this answer, but what the heck...

    I had to tackle this problem years ago on Windows-based PCs, so I was dealing with Intel x86 series processors like 486, Pentium and so on. The standard algorithm in that situation was to do a long series of DIVide instructions, because those are typically the most CPU-bound single instructions in the Intel set. So memory prefetch and other architectural issues do not materially affect the instruction execution time -- the prefetch queue is always full and the instruction itself does not touch any other memory.

    You would time it using the highest resolution clock you could get access to in the environment you are running in. (In my case I was running near boot time on a PC compatible, so I was directly programming the timer chips on the motherboard. Not recommended in a real OS, usually there's some appropriate API to call these days).

    The main problem you have to deal with is different CPU types. At that time there was Intel, AMD and some smaller vendors like Cyrix making x86 processors. Each model had its own performance characteristics vis-a-vis that DIV instruction. My assembly timing function would just return a number of clock cycles taken by a certain fixed number of DIV instructions done in a tight loop.

    So what I did was to gather some timings (raw return values from that function) from actual PCs running each processor model I wanted to time, and record those in a spreadsheet against the known processor speed and processor type. I actually had a command-line tool that was just a thin shell around my timing function, and I would take a disk into computer stores and get the timings off of display models! (I worked for a very small company at the time).

    Using those raw timings, I could plot a theoretical graph of what timings I should get for any known speed of that particular CPU.

    Here was the trick: I always hated when you would run a utility and it would announce that your CPU was 99.8 Mhz or whatever. Clearly it was 100 Mhz and there was just a small round-off error in the measurement. In my spreadsheet I recorded the actual speeds that were sold by each processor vendor. Then I would use the plot of actual timings to estimate projected timings for any known speed. But I would build a table of points along the line where the timings should round to the next speed.

    In other words, if 100 ticks to do all that repeating dividing meant 500 Mhz, and 200 ticks meant 250 Mhz, then I would build a table that said that anything below 150 was 500 Mhz, and anything above that was 250 Mhz. (Assuming those were the only two speeds available from that chip vendor). It was nice because even if some odd piece of software on the PC was throwing off my timings, the end result would often still be dead on.

    Of course now, in these days of overclocking, dynamic clock speeds for power management, and other such trickery, such a scheme would be much less practical. At the very least you'd need to do something to make sure the CPU was in its highest dynamically chosen speed first before running your timing function.

    OK, I'll go back to shooing kids off my lawn now.

    0 讨论(0)
  • 2020-12-01 15:00

    Intel CPUs after Core Duo support two Model-Specific registers called IA32_MPERF and IA32_APERF.
    MPERF counts at the maximum frequency the CPU supports, while APERF counts at the actual current frequency.

    The actual frequency is given by:

    freq = max_frequency * APERF / MPERF

    You can read them with this flow

    ; read MPERF
    mov ecx, 0xe7
    rdmsr
    mov mperf_var_lo, eax
    mov mperf_var_hi, edx
    
    ; read APERF
    mov ecx, 0xe8
    rdmsr
    mov aperf_var_lo, eax
    mov aperf_var_hi, edx
    

    but note that rdmsr is a privileged instruction and can run only in ring 0.

    I don't know if the OS provides an interface to read these, though their main usage is for power management, so it might not provide such an interface.

    0 讨论(0)
  • 2020-12-01 15:01

    I'm not sure why you need assembly for this. If you're on a machine that has the /proc filesystem, then running:

    > cat /proc/cpuinfo
    

    might give you what you need.

    0 讨论(0)
提交回复
热议问题