A simple multi-platform library for reading TSC values on x86-64 and ARM architectures.
Most of the code in this library is adapted from the implementation of a similar mechanism in DPDK project. Most source and header files maintain original copyright notices on top, since I just adjusted function names and a few things here and there. In the adaptation process, I might have broken some stuff here and there, but the most essential functionality of the library remains.
For now, the library has been tested on Linux on a x86-64 platform and an ARM64 one. Testing on other ARM platforms will soon be carried out.
This project uses CMake as build system. It provides a set of CMake option
toggles that must be set ON
or OFF
to configure the build system before
building for any platform.
The toggles are used to identify the target OS and architecture, as well as the features of each architecture that should be enabled.
For now, only Linux is supported, hence the following option should always be ON
(default):
LIBRDTSC_OS_LINUX
The target architecture can be selected by enabling only one among the
following toggles (the others must be set to OFF
):
LIBRDTSC_ARCH_x86
(defaultON
) for x86-64 platformsLIBRDTSC_ARCH_ARM
for ARM 32-bit platformsLIBRDTSC_ARCH_ARM64
for ARM64 platforms
The library supports multiple mechanisms to access the TSC value on the
supported platforms. These can be enabled using the following toggles (both are
OFF
by default):
LIBRDTSC_USE_HPET
to enable HPET usage on x86-64 platforms (currently not working)LIBRDTSC_USE_PMC
to enable PMU usage on ARM platforms (requires some options enabled from privileged kernel context)
I personally suggest users to leave either of these features disabled.
To build the library, once the set of toggles to enable/disabled has been decided, use the following snippet from the project top level directory:
mkdir build
cd build
cmake <options> ..
where options should be the complete list of CMake options to enable/disable.
Example for Linux x86-64 (nothing is selected because this is the default configuration):
mkdir build
cd build
cmake ..
Example for Linux ARM64:
mkdir build
cd build
cmake -DLIBRDTSC_ARCH_x86=OFF -DLIBRDTSC_ARCH_ARM64=ON ..
I understand that this is not the most user-friendly way of providing parameters, but it was very quick for me to implement. Future revisions of the library may change this behavior.
Once built, simply run make install
(with the appropriate privileges) to install the library in the system.
A sample application is provided in the test directory. For simplicity, the code is also included here.
This sample application should output a difference between NEW
and OLD
TSC
values that is approximately equal to the TSC HZ
value. While a more accurate
implementation should probably use clock_nanosleep
and similar precision, to
get familiar with the library this should be enough.
#include <librdtsc/rdtsc.h>
#include <stdio.h>
#include <unistd.h>
int main() {
// Initialization must be performed only if rdtsc_get_tsc_hz() is used.
// The basic rdtsc() call does not require any initialization.
int res = rdtsc_init();
if (res != 0) return res;
uint64_t tsc_hz = rdtsc_get_tsc_hz();
uint64_t tsc_old = rdtsc();
sleep(1);
uint64_t tsc_new = rdtsc();
printf("TSC OLD: %ld\n", tsc_old);
printf("TSC NEW: %ld\n", tsc_new);
printf("TSC DIFF: %ld\n", tsc_new - tsc_old);
printf("TSC HZ: %ld\n", tsc_hz);
printf("Elapsed time ( s): %ld\n", rdtsc_elapsed_s(tsc_old, tsc_new));
printf("Elapsed time (ms): %ld\n", rdtsc_elapsed_ms(tsc_old, tsc_new));
printf("Elapsed time (us): %ld\n", rdtsc_elapsed_us(tsc_old, tsc_new));
printf("Elapsed time (ns): %ld\n", rdtsc_elapsed_ns(tsc_old, tsc_new));
return 0;
}