xref: /llvm-project/third-party/benchmark/src/cycleclock.h (revision 4d34b3295f005f739e431f379ef02da7eac75688)
15dda2efdSMircea Trofin // ----------------------------------------------------------------------
25dda2efdSMircea Trofin // CycleClock
35dda2efdSMircea Trofin //    A CycleClock tells you the current time in Cycles.  The "time"
45dda2efdSMircea Trofin //    is actually time since power-on.  This is like time() but doesn't
55dda2efdSMircea Trofin //    involve a system call and is much more precise.
65dda2efdSMircea Trofin //
75dda2efdSMircea Trofin // NOTE: Not all cpu/platform/kernel combinations guarantee that this
85dda2efdSMircea Trofin // clock increments at a constant rate or is synchronized across all logical
95dda2efdSMircea Trofin // cpus in a system.
105dda2efdSMircea Trofin //
115dda2efdSMircea Trofin // If you need the above guarantees, please consider using a different
125dda2efdSMircea Trofin // API. There are efforts to provide an interface which provides a millisecond
135dda2efdSMircea Trofin // granularity and implemented as a memory read. A memory read is generally
145dda2efdSMircea Trofin // cheaper than the CycleClock for many architectures.
155dda2efdSMircea Trofin //
165dda2efdSMircea Trofin // Also, in some out of order CPU implementations, the CycleClock is not
175dda2efdSMircea Trofin // serializing. So if you're trying to count at cycles granularity, your
185dda2efdSMircea Trofin // data might be inaccurate due to out of order instruction execution.
195dda2efdSMircea Trofin // ----------------------------------------------------------------------
205dda2efdSMircea Trofin 
215dda2efdSMircea Trofin #ifndef BENCHMARK_CYCLECLOCK_H_
225dda2efdSMircea Trofin #define BENCHMARK_CYCLECLOCK_H_
235dda2efdSMircea Trofin 
245dda2efdSMircea Trofin #include <cstdint>
255dda2efdSMircea Trofin 
265dda2efdSMircea Trofin #include "benchmark/benchmark.h"
275dda2efdSMircea Trofin #include "internal_macros.h"
285dda2efdSMircea Trofin 
295dda2efdSMircea Trofin #if defined(BENCHMARK_OS_MACOSX)
305dda2efdSMircea Trofin #include <mach/mach_time.h>
315dda2efdSMircea Trofin #endif
325dda2efdSMircea Trofin // For MSVC, we want to use '_asm rdtsc' when possible (since it works
335dda2efdSMircea Trofin // with even ancient MSVC compilers), and when not possible the
345dda2efdSMircea Trofin // __rdtsc intrinsic, declared in <intrin.h>.  Unfortunately, in some
355dda2efdSMircea Trofin // environments, <windows.h> and <intrin.h> have conflicting
365dda2efdSMircea Trofin // declarations of some other intrinsics, breaking compilation.
375dda2efdSMircea Trofin // Therefore, we simply declare __rdtsc ourselves. See also
385dda2efdSMircea Trofin // http://connect.microsoft.com/VisualStudio/feedback/details/262047
39a5b79717SMircea Trofin #if defined(COMPILER_MSVC) && !defined(_M_IX86) && !defined(_M_ARM64) && \
40a5b79717SMircea Trofin     !defined(_M_ARM64EC)
415dda2efdSMircea Trofin extern "C" uint64_t __rdtsc();
425dda2efdSMircea Trofin #pragma intrinsic(__rdtsc)
435dda2efdSMircea Trofin #endif
445dda2efdSMircea Trofin 
455dda2efdSMircea Trofin #if !defined(BENCHMARK_OS_WINDOWS) || defined(BENCHMARK_OS_MINGW)
465dda2efdSMircea Trofin #include <sys/time.h>
475dda2efdSMircea Trofin #include <time.h>
485dda2efdSMircea Trofin #endif
495dda2efdSMircea Trofin 
505dda2efdSMircea Trofin #ifdef BENCHMARK_OS_EMSCRIPTEN
515dda2efdSMircea Trofin #include <emscripten.h>
525dda2efdSMircea Trofin #endif
535dda2efdSMircea Trofin 
545dda2efdSMircea Trofin namespace benchmark {
555dda2efdSMircea Trofin // NOTE: only i386 and x86_64 have been well tested.
565dda2efdSMircea Trofin // PPC, sparc, alpha, and ia64 are based on
575dda2efdSMircea Trofin //    http://peter.kuscsik.com/wordpress/?p=14
585dda2efdSMircea Trofin // with modifications by m3b.  See also
595dda2efdSMircea Trofin //    https://setisvn.ssl.berkeley.edu/svn/lib/fftw-3.0.1/kernel/cycle.h
605dda2efdSMircea Trofin namespace cycleclock {
615dda2efdSMircea Trofin // This should return the number of cycles since power-on.  Thread-safe.
Now()625dda2efdSMircea Trofin inline BENCHMARK_ALWAYS_INLINE int64_t Now() {
635dda2efdSMircea Trofin #if defined(BENCHMARK_OS_MACOSX)
645dda2efdSMircea Trofin   // this goes at the top because we need ALL Macs, regardless of
655dda2efdSMircea Trofin   // architecture, to return the number of "mach time units" that
665dda2efdSMircea Trofin   // have passed since startup.  See sysinfo.cc where
675dda2efdSMircea Trofin   // InitializeSystemInfo() sets the supposed cpu clock frequency of
685dda2efdSMircea Trofin   // macs to the number of mach time units per second, not actual
695dda2efdSMircea Trofin   // CPU clock frequency (which can change in the face of CPU
705dda2efdSMircea Trofin   // frequency scaling).  Also note that when the Mac sleeps, this
715dda2efdSMircea Trofin   // counter pauses; it does not continue counting, nor does it
725dda2efdSMircea Trofin   // reset to zero.
735dda2efdSMircea Trofin   return mach_absolute_time();
745dda2efdSMircea Trofin #elif defined(BENCHMARK_OS_EMSCRIPTEN)
755dda2efdSMircea Trofin   // this goes above x86-specific code because old versions of Emscripten
765dda2efdSMircea Trofin   // define __x86_64__, although they have nothing to do with it.
775dda2efdSMircea Trofin   return static_cast<int64_t>(emscripten_get_now() * 1e+6);
785dda2efdSMircea Trofin #elif defined(__i386__)
795dda2efdSMircea Trofin   int64_t ret;
805dda2efdSMircea Trofin   __asm__ volatile("rdtsc" : "=A"(ret));
815dda2efdSMircea Trofin   return ret;
825dda2efdSMircea Trofin #elif defined(__x86_64__) || defined(__amd64__)
835dda2efdSMircea Trofin   uint64_t low, high;
845dda2efdSMircea Trofin   __asm__ volatile("rdtsc" : "=a"(low), "=d"(high));
855dda2efdSMircea Trofin   return (high << 32) | low;
865dda2efdSMircea Trofin #elif defined(__powerpc__) || defined(__ppc__)
875dda2efdSMircea Trofin   // This returns a time-base, which is not always precisely a cycle-count.
885dda2efdSMircea Trofin #if defined(__powerpc64__) || defined(__ppc64__)
895dda2efdSMircea Trofin   int64_t tb;
905dda2efdSMircea Trofin   asm volatile("mfspr %0, 268" : "=r"(tb));
915dda2efdSMircea Trofin   return tb;
925dda2efdSMircea Trofin #else
935dda2efdSMircea Trofin   uint32_t tbl, tbu0, tbu1;
945dda2efdSMircea Trofin   asm volatile(
955dda2efdSMircea Trofin       "mftbu %0\n"
965dda2efdSMircea Trofin       "mftb %1\n"
975dda2efdSMircea Trofin       "mftbu %2"
985dda2efdSMircea Trofin       : "=r"(tbu0), "=r"(tbl), "=r"(tbu1));
995dda2efdSMircea Trofin   tbl &= -static_cast<int32_t>(tbu0 == tbu1);
1005dda2efdSMircea Trofin   // high 32 bits in tbu1; low 32 bits in tbl  (tbu0 is no longer needed)
1015dda2efdSMircea Trofin   return (static_cast<uint64_t>(tbu1) << 32) | tbl;
1025dda2efdSMircea Trofin #endif
1035dda2efdSMircea Trofin #elif defined(__sparc__)
1045dda2efdSMircea Trofin   int64_t tick;
1055dda2efdSMircea Trofin   asm(".byte 0x83, 0x41, 0x00, 0x00");
1065dda2efdSMircea Trofin   asm("mov   %%g1, %0" : "=r"(tick));
1075dda2efdSMircea Trofin   return tick;
1085dda2efdSMircea Trofin #elif defined(__ia64__)
1095dda2efdSMircea Trofin   int64_t itc;
1105dda2efdSMircea Trofin   asm("mov %0 = ar.itc" : "=r"(itc));
1115dda2efdSMircea Trofin   return itc;
1125dda2efdSMircea Trofin #elif defined(COMPILER_MSVC) && defined(_M_IX86)
1135dda2efdSMircea Trofin   // Older MSVC compilers (like 7.x) don't seem to support the
1145dda2efdSMircea Trofin   // __rdtsc intrinsic properly, so I prefer to use _asm instead
1155dda2efdSMircea Trofin   // when I know it will work.  Otherwise, I'll use __rdtsc and hope
1165dda2efdSMircea Trofin   // the code is being compiled with a non-ancient compiler.
1175dda2efdSMircea Trofin   _asm rdtsc
118a5b79717SMircea Trofin #elif defined(COMPILER_MSVC) && (defined(_M_ARM64) || defined(_M_ARM64EC))
119a290770fSMircea Trofin   // See // https://docs.microsoft.com/en-us/cpp/intrinsics/arm64-intrinsics
1205dda2efdSMircea Trofin   // and https://reviews.llvm.org/D53115
1215dda2efdSMircea Trofin   int64_t virtual_timer_value;
1225dda2efdSMircea Trofin   virtual_timer_value = _ReadStatusReg(ARM64_CNTVCT);
1235dda2efdSMircea Trofin   return virtual_timer_value;
1245dda2efdSMircea Trofin #elif defined(COMPILER_MSVC)
1255dda2efdSMircea Trofin   return __rdtsc();
1265dda2efdSMircea Trofin #elif defined(BENCHMARK_OS_NACL)
1275dda2efdSMircea Trofin   // Native Client validator on x86/x86-64 allows RDTSC instructions,
1285dda2efdSMircea Trofin   // and this case is handled above. Native Client validator on ARM
1295dda2efdSMircea Trofin   // rejects MRC instructions (used in the ARM-specific sequence below),
1305dda2efdSMircea Trofin   // so we handle it here. Portable Native Client compiles to
1315dda2efdSMircea Trofin   // architecture-agnostic bytecode, which doesn't provide any
1325dda2efdSMircea Trofin   // cycle counter access mnemonics.
1335dda2efdSMircea Trofin 
1345dda2efdSMircea Trofin   // Native Client does not provide any API to access cycle counter.
1355dda2efdSMircea Trofin   // Use clock_gettime(CLOCK_MONOTONIC, ...) instead of gettimeofday
136a5b79717SMircea Trofin   // because is provides nanosecond resolution (which is noticeable at
1375dda2efdSMircea Trofin   // least for PNaCl modules running on x86 Mac & Linux).
1385dda2efdSMircea Trofin   // Initialize to always return 0 if clock_gettime fails.
1395dda2efdSMircea Trofin   struct timespec ts = {0, 0};
1405dda2efdSMircea Trofin   clock_gettime(CLOCK_MONOTONIC, &ts);
1415dda2efdSMircea Trofin   return static_cast<int64_t>(ts.tv_sec) * 1000000000 + ts.tv_nsec;
1425dda2efdSMircea Trofin #elif defined(__aarch64__)
1435dda2efdSMircea Trofin   // System timer of ARMv8 runs at a different frequency than the CPU's.
1445dda2efdSMircea Trofin   // The frequency is fixed, typically in the range 1-50MHz.  It can be
1455dda2efdSMircea Trofin   // read at CNTFRQ special register.  We assume the OS has set up
1465dda2efdSMircea Trofin   // the virtual timer properly.
1475dda2efdSMircea Trofin   int64_t virtual_timer_value;
1485dda2efdSMircea Trofin   asm volatile("mrs %0, cntvct_el0" : "=r"(virtual_timer_value));
1495dda2efdSMircea Trofin   return virtual_timer_value;
1505dda2efdSMircea Trofin #elif defined(__ARM_ARCH)
1515dda2efdSMircea Trofin   // V6 is the earliest arch that has a standard cyclecount
1525dda2efdSMircea Trofin   // Native Client validator doesn't allow MRC instructions.
1535dda2efdSMircea Trofin #if (__ARM_ARCH >= 6)
1545dda2efdSMircea Trofin   uint32_t pmccntr;
1555dda2efdSMircea Trofin   uint32_t pmuseren;
1565dda2efdSMircea Trofin   uint32_t pmcntenset;
1575dda2efdSMircea Trofin   // Read the user mode perf monitor counter access permissions.
1585dda2efdSMircea Trofin   asm volatile("mrc p15, 0, %0, c9, c14, 0" : "=r"(pmuseren));
1595dda2efdSMircea Trofin   if (pmuseren & 1) {  // Allows reading perfmon counters for user mode code.
1605dda2efdSMircea Trofin     asm volatile("mrc p15, 0, %0, c9, c12, 1" : "=r"(pmcntenset));
1615dda2efdSMircea Trofin     if (pmcntenset & 0x80000000ul) {  // Is it counting?
1625dda2efdSMircea Trofin       asm volatile("mrc p15, 0, %0, c9, c13, 0" : "=r"(pmccntr));
1635dda2efdSMircea Trofin       // The counter is set up to count every 64th cycle
1645dda2efdSMircea Trofin       return static_cast<int64_t>(pmccntr) * 64;  // Should optimize to << 6
1655dda2efdSMircea Trofin     }
1665dda2efdSMircea Trofin   }
1675dda2efdSMircea Trofin #endif
1685dda2efdSMircea Trofin   struct timeval tv;
1695dda2efdSMircea Trofin   gettimeofday(&tv, nullptr);
1705dda2efdSMircea Trofin   return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
1715dda2efdSMircea Trofin #elif defined(__mips__) || defined(__m68k__)
1725dda2efdSMircea Trofin   // mips apparently only allows rdtsc for superusers, so we fall
1735dda2efdSMircea Trofin   // back to gettimeofday.  It's possible clock_gettime would be better.
1745dda2efdSMircea Trofin   struct timeval tv;
1755dda2efdSMircea Trofin   gettimeofday(&tv, nullptr);
1765dda2efdSMircea Trofin   return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
177a5b79717SMircea Trofin #elif defined(__loongarch__) || defined(__csky__)
1785dda2efdSMircea Trofin   struct timeval tv;
1795dda2efdSMircea Trofin   gettimeofday(&tv, nullptr);
1805dda2efdSMircea Trofin   return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
1815dda2efdSMircea Trofin #elif defined(__s390__)  // Covers both s390 and s390x.
1825dda2efdSMircea Trofin   // Return the CPU clock.
1835dda2efdSMircea Trofin   uint64_t tsc;
184*4d34b329SFanbo Meng #if defined(BENCHMARK_OS_ZOS)
185*4d34b329SFanbo Meng   // z/OS HLASM syntax.
1865dda2efdSMircea Trofin   asm(" stck %0" : "=m"(tsc) : : "cc");
1875dda2efdSMircea Trofin #else
188*4d34b329SFanbo Meng   // Linux on Z syntax.
1895dda2efdSMircea Trofin   asm("stck %0" : "=Q"(tsc) : : "cc");
1905dda2efdSMircea Trofin #endif
1915dda2efdSMircea Trofin   return tsc;
1925dda2efdSMircea Trofin #elif defined(__riscv)  // RISC-V
193a5b79717SMircea Trofin   // Use RDTIME (and RDTIMEH on riscv32).
194a5b79717SMircea Trofin   // RDCYCLE is a privileged instruction since Linux 6.6.
1955dda2efdSMircea Trofin #if __riscv_xlen == 32
1965dda2efdSMircea Trofin   uint32_t cycles_lo, cycles_hi0, cycles_hi1;
1975dda2efdSMircea Trofin   // This asm also includes the PowerPC overflow handling strategy, as above.
1985dda2efdSMircea Trofin   // Implemented in assembly because Clang insisted on branching.
1995dda2efdSMircea Trofin   asm volatile(
200a5b79717SMircea Trofin       "rdtimeh %0\n"
201a5b79717SMircea Trofin       "rdtime %1\n"
202a5b79717SMircea Trofin       "rdtimeh %2\n"
2035dda2efdSMircea Trofin       "sub %0, %0, %2\n"
2045dda2efdSMircea Trofin       "seqz %0, %0\n"
2055dda2efdSMircea Trofin       "sub %0, zero, %0\n"
2065dda2efdSMircea Trofin       "and %1, %1, %0\n"
2075dda2efdSMircea Trofin       : "=r"(cycles_hi0), "=r"(cycles_lo), "=r"(cycles_hi1));
2085dda2efdSMircea Trofin   return (static_cast<uint64_t>(cycles_hi1) << 32) | cycles_lo;
2095dda2efdSMircea Trofin #else
2105dda2efdSMircea Trofin   uint64_t cycles;
211a5b79717SMircea Trofin   asm volatile("rdtime %0" : "=r"(cycles));
2125dda2efdSMircea Trofin   return cycles;
2135dda2efdSMircea Trofin #endif
2145dda2efdSMircea Trofin #elif defined(__e2k__) || defined(__elbrus__)
2155dda2efdSMircea Trofin   struct timeval tv;
2165dda2efdSMircea Trofin   gettimeofday(&tv, nullptr);
2175dda2efdSMircea Trofin   return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
218a5b79717SMircea Trofin #elif defined(__hexagon__)
219a5b79717SMircea Trofin   uint64_t pcycle;
220a5b79717SMircea Trofin   asm volatile("%0 = C15:14" : "=r"(pcycle));
221a5b79717SMircea Trofin   return static_cast<double>(pcycle);
222a5b79717SMircea Trofin #elif defined(__alpha__)
223a5b79717SMircea Trofin   // Alpha has a cycle counter, the PCC register, but it is an unsigned 32-bit
224a5b79717SMircea Trofin   // integer and thus wraps every ~4s, making using it for tick counts
225a5b79717SMircea Trofin   // unreliable beyond this time range.  The real-time clock is low-precision,
226a5b79717SMircea Trofin   // roughtly ~1ms, but it is the only option that can reasonable count
227a5b79717SMircea Trofin   // indefinitely.
228a5b79717SMircea Trofin   struct timeval tv;
229a5b79717SMircea Trofin   gettimeofday(&tv, nullptr);
230a5b79717SMircea Trofin   return static_cast<int64_t>(tv.tv_sec) * 1000000 + tv.tv_usec;
2315dda2efdSMircea Trofin #else
2325dda2efdSMircea Trofin   // The soft failover to a generic implementation is automatic only for ARM.
2335dda2efdSMircea Trofin   // For other platforms the developer is expected to make an attempt to create
234a5b79717SMircea Trofin   // a fast implementation and use generic version if nothing better is
235a5b79717SMircea Trofin   // available.
2365dda2efdSMircea Trofin #error You need to define CycleTimer for your OS and CPU
2375dda2efdSMircea Trofin #endif
2385dda2efdSMircea Trofin }
2395dda2efdSMircea Trofin }  // end namespace cycleclock
2405dda2efdSMircea Trofin }  // end namespace benchmark
2415dda2efdSMircea Trofin 
2425dda2efdSMircea Trofin #endif  // BENCHMARK_CYCLECLOCK_H_
243