1
\$\begingroup\$

I have this small function for reading the TSC (time-stamp counter) in *nix. Using it seems to report my CPU frequency more or less correctly.

#include <stdint.h>
#include <stdio.h>
#include <unistd.h>

uint64_t read_time_stamp_counter() 
{   
    uint32_t my_edx;
    uint32_t my_eax;

    asm ("cpuid\n\t" /* Discourage out-of-order execution. */
         "rdtsc\n\t"
         "movl %%edx, %0\n\t"
         "movl %%eax, %1"
         :"=r"(my_edx), "=r"(my_eax) 
         :
         : /* List of registers clobbered by both RDTSC and CPUID. */
           "%eax", "%ebx", "%ecx", "%edx" 
         );

    return (((uint64_t) my_edx) << 32) | my_eax;
}

int main(int argc, char** argv) {
    uint64_t tmp;
    uint64_t cycles;
    uint64_t sleep_overhead;

    tmp = read_time_stamp_counter();
    sleep(1);
    cycles = read_time_stamp_counter() - tmp;

    tmp = read_time_stamp_counter();
    sleep(0);
    sleep_overhead = read_time_stamp_counter() - tmp;

    printf("Sleep overhead: %llu cycles.\n", sleep_overhead);

    cycles -= sleep_overhead;

    printf("Cycles: %llu\n", cycles);
    printf("Estimated frequency: %.3f GHz\n", cycles / 1e9);

    return 0;
}

The current implementation does not look good: there should be a better way. Also, is it possible to make it more precise?

\$\endgroup\$
3
  • 3
    \$\begingroup\$ Relevant: How can I programmatically find the CPU frequency with C? – short version: on modern architectures you can't unless you know on which operating system your code is going to run; in that case use the relevant infrastructures of the operating system. \$\endgroup\$ – David Foerster Jan 31 '16 at 9:15
  • \$\begingroup\$ when compiling, enable all the warnings, then fix those warnings. (for gcc, at a minimum use: -Wall -Wextra -pedantic (I also use: -Wconversion -std=c99 ) ). Then you will see: unused parameter argc, unused parameter argv, mismatch between '%llu` and argument 2 of uint64_t for line ~35, mismatch between %llu and argument 2 uint64_t for line ~40, mismatch between %.3f and argument 2 'uint64_t` for line ~41 \$\endgroup\$ – user3629249 Feb 1 '16 at 0:53
  • 2
    \$\begingroup\$ suggest replace current signature for main() with int main( void ) \$\endgroup\$ – user3629249 Feb 1 '16 at 0:55
2
\$\begingroup\$

From a "How to get the right answer" point of view, David's comment which explains why asking the OS is the only trustworthy solution best answers the question.

But this is Code Review, not Stack Overflow. From that point of view:

The cpuid instruction returns different data depending on the input value of eax. Since you never set it before executing your asm, (conceivably) the length of the operation could vary between calls, mucking up your calculation.

Also, the mov statements are unnecessary:

asm ("cpuid\n\t"
     "rdtsc"
     : "=d"(my_edx), "=a"(my_eax)
     : "a"(0) 
     : "%ebx", "%ecx");

It might also be worth looking at rdtscp as a replacement for cpuid + rdtsc.

\$\endgroup\$

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.