summaryrefslogtreecommitdiffstats
path: root/include/my_rdtsc.h
diff options
context:
space:
mode:
Diffstat (limited to 'include/my_rdtsc.h')
-rw-r--r--include/my_rdtsc.h278
1 files changed, 278 insertions, 0 deletions
diff --git a/include/my_rdtsc.h b/include/my_rdtsc.h
new file mode 100644
index 00000000..8b9b0046
--- /dev/null
+++ b/include/my_rdtsc.h
@@ -0,0 +1,278 @@
+/* Copyright (c) 2008 MySQL AB, 2009 Sun Microsystems, Inc.
+ Copyright (c) 2019, MariaDB Corporation.
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; version 2 of the License.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1335 USA */
+
+/*
+ rdtsc3 -- multi-platform timer code
+ pgulutzan@mysql.com, 2005-08-29
+ modified 2008-11-02
+*/
+
+#ifndef MY_RDTSC_H
+#define MY_RDTSC_H
+
+# ifndef __has_builtin
+# define __has_builtin(x) 0 /* Compatibility with non-clang compilers */
+# endif
+# if __has_builtin(__builtin_readcyclecounter)
+# elif defined _WIN32
+# include <intrin.h>
+# elif defined __i386__ || defined __x86_64__
+# include <x86intrin.h>
+# elif defined(__INTEL_COMPILER) && defined(__ia64__) && defined(HAVE_IA64INTRIN_H)
+# include <ia64intrin.h>
+# elif defined(HAVE_SYS_TIMES_H) && defined(HAVE_GETHRTIME)
+# include <sys/times.h>
+# endif
+
+/**
+ Characteristics of a timer.
+*/
+struct my_timer_unit_info
+{
+ /** Routine used for the timer. */
+ ulonglong routine;
+ /** Overhead of the timer. */
+ ulonglong overhead;
+ /** Frequency of the timer. */
+ ulonglong frequency;
+ /** Resolution of the timer. */
+ ulonglong resolution;
+};
+
+/**
+ Characteristics of all the supported timers.
+ @sa my_timer_init().
+*/
+struct my_timer_info
+{
+ /** Characteristics of the cycle timer. */
+ struct my_timer_unit_info cycles;
+ /** Characteristics of the nanosecond timer. */
+ struct my_timer_unit_info nanoseconds;
+ /** Characteristics of the microsecond timer. */
+ struct my_timer_unit_info microseconds;
+ /** Characteristics of the millisecond timer. */
+ struct my_timer_unit_info milliseconds;
+ /** Characteristics of the tick timer. */
+ struct my_timer_unit_info ticks;
+};
+
+typedef struct my_timer_info MY_TIMER_INFO;
+
+#define MY_TIMER_ROUTINE_RDTSC 5
+#define MY_TIMER_ROUTINE_ASM_IA64 6
+#define MY_TIMER_ROUTINE_PPC_GET_TIMEBASE 7
+#define MY_TIMER_ROUTINE_GETHRTIME 9
+#define MY_TIMER_ROUTINE_READ_REAL_TIME 10
+#define MY_TIMER_ROUTINE_CLOCK_GETTIME 11
+#define MY_TIMER_ROUTINE_GETTIMEOFDAY 13
+#define MY_TIMER_ROUTINE_QUERYPERFORMANCECOUNTER 14
+#define MY_TIMER_ROUTINE_GETTICKCOUNT 15
+#define MY_TIMER_ROUTINE_TIME 16
+#define MY_TIMER_ROUTINE_TIMES 17
+#define MY_TIMER_ROUTINE_FTIME 18
+#define MY_TIMER_ROUTINE_ASM_GCC_SPARC64 23
+#define MY_TIMER_ROUTINE_ASM_GCC_SPARC32 24
+#define MY_TIMER_ROUTINE_MACH_ABSOLUTE_TIME 25
+#define MY_TIMER_ROUTINE_GETSYSTEMTIMEASFILETIME 26
+#define MY_TIMER_ROUTINE_ASM_S390 28
+#define MY_TIMER_ROUTINE_AARCH64 29
+#define MY_TIMER_ROUTINE_RISCV 30
+
+C_MODE_START
+
+/**
+ A cycle timer.
+
+ On clang we use __builtin_readcyclecounter(), except for AARCH64.
+ On other compilers:
+
+ On IA-32 and AMD64, we use the RDTSC instruction.
+ On IA-64, we read the ar.itc register.
+ On SPARC, we read the tick register.
+ On POWER, we read the Time Base Register (which is not really a cycle count
+ but a separate counter with less than nanosecond resolution).
+ On IBM S/390 System z we use the STCK instruction.
+ On ARM, we probably should use the Generic Timer, but should figure out
+ how to ensure that it can be accessed.
+ On AARCH64, we use the generic timer base register. We override clang
+ implementation for aarch64 as it access a PMU register which is not
+ guaranteed to be active.
+ On RISC-V, we use the rdcycle instruction to read from mcycle register.
+
+ Sadly, we have nothing for the Digital Alpha, MIPS, Motorola m68k,
+ HP PA-RISC or other non-mainstream (or obsolete) processors.
+
+ TODO: consider C++11 std::chrono::high_resolution_clock.
+
+ We fall back to gethrtime() where available.
+
+ On the platforms that do not have a CYCLE timer,
+ "wait" events are initialized to use NANOSECOND instead of CYCLE
+ during performance_schema initialization (at the server startup).
+
+ Linux performance monitor (see "man perf_event_open") can
+ provide cycle counter on the platforms that do not have
+ other kinds of cycle counters. But we don't use it so far.
+
+ ARM notes
+ ---------
+ Userspace high precision timing on CNTVCT_EL0 requires that CNTKCTL_EL1
+ is set to 1 for each CPU in privileged mode.
+
+ During tests on ARMv7 Debian, perf_even_open() based cycle counter provided
+ too low frequency with too high overhead:
+ MariaDB [performance_schema]> SELECT * FROM performance_timers;
+ +-------------+-----------------+------------------+----------------+
+ | TIMER_NAME | TIMER_FREQUENCY | TIMER_RESOLUTION | TIMER_OVERHEAD |
+ +-------------+-----------------+------------------+----------------+
+ | CYCLE | 689368159 | 1 | 970 |
+ | NANOSECOND | 1000000000 | 1 | 308 |
+ | MICROSECOND | 1000000 | 1 | 417 |
+ | MILLISECOND | 1000 | 1000 | 407 |
+ | TICK | 127 | 1 | 612 |
+ +-------------+-----------------+------------------+----------------+
+ Therefore, it was decided not to use perf_even_open() on ARM
+ (i.e. go without CYCLE and have "wait" events use NANOSECOND by default).
+
+ @return the current timer value, in cycles.
+*/
+static inline ulonglong my_timer_cycles(void)
+{
+# if __has_builtin(__builtin_readcyclecounter) && !defined (__aarch64__)
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_AARCH64
+ return __builtin_readcyclecounter();
+# elif defined _M_IX86 || defined _M_X64 || defined __i386__ || defined __x86_64__
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_RDTSC
+ return __rdtsc();
+#elif defined _M_ARM64
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_AARCH64
+ return _ReadStatusReg(ARM64_CNTVCT);
+# elif defined(__INTEL_COMPILER) && defined(__ia64__) && defined(HAVE_IA64INTRIN_H)
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_ASM_IA64
+ return (ulonglong) __getReg(_IA64_REG_AR_ITC); /* (3116) */
+#elif defined(__GNUC__) && defined(__ia64__)
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_ASM_IA64
+ {
+ ulonglong result;
+ __asm __volatile__ ("mov %0=ar.itc" : "=r" (result));
+ return result;
+ }
+#elif defined __GNUC__ && defined __powerpc__
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_PPC_GET_TIMEBASE
+ return __builtin_ppc_get_timebase();
+#elif defined(__GNUC__) && defined(__sparcv9) && defined(_LP64)
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_ASM_GCC_SPARC64
+ {
+ ulonglong result;
+ __asm __volatile__ ("rd %%tick,%0" : "=r" (result));
+ return result;
+ }
+#elif defined(__GNUC__) && defined(__sparc__) && !defined(_LP64)
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_ASM_GCC_SPARC32
+ {
+ union {
+ ulonglong wholeresult;
+ struct {
+ ulong high;
+ ulong low;
+ } splitresult;
+ } result;
+ __asm __volatile__ ("rd %%tick,%1; srlx %1,32,%0" : "=r" (result.splitresult.high), "=r" (result.splitresult.low));
+ return result.wholeresult;
+ }
+#elif defined(__GNUC__) && defined(__s390__)
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_ASM_S390
+ /* covers both s390 and s390x */
+ {
+ ulonglong result;
+ __asm__ __volatile__ ("stck %0" : "=Q" (result) : : "cc");
+ return result;
+ }
+#elif defined(__GNUC__) && defined (__aarch64__)
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_AARCH64
+ {
+ ulonglong result;
+ __asm __volatile("mrs %0, CNTVCT_EL0" : "=&r" (result));
+ return result;
+ }
+#elif defined(__riscv)
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_RISCV
+ /* Use RDCYCLE (and RDCYCLEH on riscv32) */
+ {
+# if __riscv_xlen == 32
+ ulong result_lo, result_hi0, result_hi1;
+ /* Implemented in assembly because Clang insisted on branching. */
+ __asm __volatile__(
+ "rdcycleh %0\n"
+ "rdcycle %1\n"
+ "rdcycleh %2\n"
+ "sub %0, %0, %2\n"
+ "seqz %0, %0\n"
+ "sub %0, zero, %0\n"
+ "and %1, %1, %0\n"
+ : "=r"(result_hi0), "=r"(result_lo), "=r"(result_hi1));
+ return (static_cast<ulonglong>(result_hi1) << 32) | result_lo;
+# else
+ ulonglong result;
+ __asm __volatile__("rdcycle %0" : "=r"(result));
+ return result;
+ }
+# endif
+#elif defined(HAVE_SYS_TIMES_H) && defined(HAVE_GETHRTIME)
+ #define MY_TIMER_ROUTINE_CYCLES MY_TIMER_ROUTINE_GETHRTIME
+ /* gethrtime may appear as either cycle or nanosecond counter */
+ return (ulonglong) gethrtime();
+#else
+ #define MY_TIMER_ROUTINE_CYCLES 0
+ return 0;
+#endif
+}
+
+/**
+ A nanosecond timer.
+ @return the current timer value, in nanoseconds.
+*/
+ulonglong my_timer_nanoseconds(void);
+
+/**
+ A microseconds timer.
+ @return the current timer value, in microseconds.
+*/
+ulonglong my_timer_microseconds(void);
+
+/**
+ A millisecond timer.
+ @return the current timer value, in milliseconds.
+*/
+ulonglong my_timer_milliseconds(void);
+
+/**
+ A ticks timer.
+ @return the current timer value, in ticks.
+*/
+ulonglong my_timer_ticks(void);
+
+/**
+ Timer initialization function.
+ @param [out] mti the timer characteristics.
+*/
+void my_timer_init(MY_TIMER_INFO *mti);
+
+C_MODE_END
+
+#endif
+