blob: 3477b9a0a3abc57ae423f2411fa52e57e03fca0b [file] [log] [blame]
#define __STDC_FORMAT_MACROS
#include <sys/time.h>
#include <time.h>
#include <stdlib.h>
#include <sys/syscall.h>
#include <unistd.h>
#include <dlfcn.h>
#include <stdio.h>
#include <string.h>
#include <inttypes.h>
#include <atomic>
#include <signal.h>
#include <err.h>
typedef int (*vgettime_t)(clockid_t, timespec *);
typedef long (*vgetcpu_t)(unsigned *cpu, unsigned *node, void *unused);
void describe_clock(const char *name, int id)
{
struct timespec res;
int ret = clock_getres(id, &res);
if (ret < 0) {
printf(" %d (%s) [failed to query resolution]\n",
id, name);
} else {
printf(" %d (%s) resolution = %" PRIu64 ".%09u\n",
id, name,
(uint64_t)res.tv_sec, (unsigned)res.tv_nsec);
}
}
static void sethandler(int sig, void (*handler)(int, siginfo_t *, void *),
int flags)
{
struct sigaction sa;
memset(&sa, 0, sizeof(sa));
sa.sa_sigaction = handler;
sa.sa_flags = SA_SIGINFO | flags;
sigemptyset(&sa.sa_mask);
if (sigaction(sig, &sa, 0))
err(1, "sigaction");
}
static void sigusr1(int sig, siginfo_t *info, void *ctx_void)
{
}
int main(int argc, char **argv)
{
if (argc < 3) {
printf("Usage: time <Miters> <mode> [POSIX clock id]\n");
printf("\nClocks are:\n");
describe_clock("CLOCK_REALTIME", CLOCK_REALTIME);
describe_clock("CLOCK_MONOTONIC", CLOCK_MONOTONIC);
describe_clock("CLOCK_REALTIME_COARSE", CLOCK_REALTIME_COARSE);
describe_clock("CLOCK_MONOTONIC_COARSE", CLOCK_MONOTONIC_COARSE);
return 1;
}
void *vdso = dlopen("linux-vdso.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso)
vdso = dlopen("linux-gate.so.1", RTLD_LAZY | RTLD_LOCAL | RTLD_NOLOAD);
if (!vdso)
printf("dlopen failed\n");;
vgettime_t vgettime = (vgettime_t)dlsym(vdso, "__vdso_clock_gettime");
if (!vgettime)
printf("dlsym failed: %s", dlerror());
vgetcpu_t vgetcpu = (vgetcpu_t)dlsym(vdso, "__vdso_getcpu");
if (!vgetcpu)
printf("dlsym failed: %s", dlerror());
size_t loops = (size_t)atol(argv[1]) * 1000000;
clockid_t c = argc > 3 ? atoi(argv[3]) : 0;
const char *mode = argv[2];
sethandler(SIGUSR1, sigusr1, 0);
timespec start;
clock_gettime(CLOCK_MONOTONIC, &start);
timespec t;
if (!strcmp(mode, "clock_gettime")) {
for (size_t i = 0; i < loops; ++i)
clock_gettime(c, &t);
} else if (!strcmp(mode, "rdtsc")) {
for (size_t i = 0; i < loops; ++i) {
unsigned int a, d;
asm volatile ("rdtsc" : "=a" (a), "=d" (d));
}
} else if (!strcmp(mode, "lfence_rdtsc")) {
for (size_t i = 0; i < loops; ++i) {
unsigned int a, d;
asm volatile ("lfence;rdtsc" : "=a" (a), "=d" (d));
}
} else if (!strcmp(mode, "lfence_rdtsc_lfence")) {
for (size_t i = 0; i < loops; ++i) {
unsigned int a, d;
asm volatile ("");
asm volatile ("lfence;rdtsc;lfence" : "=a" (a), "=d" (d));
}
} else if (!strcmp(mode, "mfence_rdtsc_mfence")) {
for (size_t i = 0; i < loops; ++i) {
unsigned int a, d;
asm volatile ("mfence;rdtsc;mfence" : "=a" (a), "=d" (d));
}
} else if (!strcmp(mode, "mfence")) {
for (size_t i = 0; i < loops; ++i) {
unsigned int a, d;
asm volatile ("mfence" : "=a" (a), "=d" (d));
}
} else if (!strcmp(mode, "sfence")) {
for (size_t i = 0; i < loops; ++i) {
unsigned int a, d;
asm volatile ("sfence" : "=a" (a), "=d" (d));
}
} else if (!strcmp(mode, "lock_addl")) {
std::atomic<unsigned int> x;
for (size_t i = 0; i < loops; ++i)
x += 2;
} else if (!strcmp(mode, "lock_xchg")) {
std::atomic<unsigned int> x;
for (size_t i = 0; i < loops; ++i)
x.exchange(2);
} else if (!strcmp(mode, "cmpxchg_mismatch")) {
std::atomic<unsigned long> x;
for (size_t i = 0; i < loops; ++i)
asm volatile ("cmpxchg %[newval], %[mem]"
: [mem] "+m" (x)
: "a" (1), [newval] "r" (2) : "flags");
} else if (!strcmp(mode, "cmpxchg_match")) {
std::atomic<unsigned long> x;
for (size_t i = 0; i < loops/2; ++i) {
asm volatile ("cmpxchg %[newval], %[mem]"
: [mem] "+m" (x)
: "a" (0), [newval] "r" (2) : "flags");
asm volatile ("cmpxchg %[newval], %[mem]"
: [mem] "+m" (x)
: "a" (2), [newval] "r" (0) : "flags");
}
} else if (!strcmp(mode, "rdtscp")) {
for (size_t i = 0; i < loops; ++i) {
unsigned int a, c, d;
asm volatile ("rdtscp" : "=a" (a), "=c" (c), "=d" (d));
}
} else if (!strcmp(mode, "lsl15")) {
for (size_t i = 0; i < loops; ++i) {
uint16_t index = (15 << 3) + 3;
uint32_t limit;
asm volatile ("lsl %[index], %[limit]"
: [limit] "=r" (limit)
: [index] "r" (index) : "cc");
}
} else if (!strcmp(mode, "lsl100")) {
for (size_t i = 0; i < loops; ++i) {
uint16_t index = (100 << 3) + 3;
uint32_t limit;
asm volatile ("lsl %[index], %[limit]"
: [limit] "=r" (limit)
: [index] "r" (index) : "cc");
}
} else if (!strcmp(mode, "mov_to_ds")) {
for (size_t i = 0; i < loops; ++i)
asm volatile ("mov %0, %%ds" : : "rm" (0));
} else if (!strcmp(mode, "zero_gs")) {
for (size_t i = 0; i < loops; ++i)
asm volatile ("mov %0, %%gs" : : "rm" (0));
} else if (!strcmp(mode, "nonzero_gs")) {
unsigned short sel;
asm ("mov %%ss, %0" : "=rm" (sel));
for (size_t i = 0; i < loops; ++i)
asm volatile ("mov %0, %%gs" : : "rm" (sel));
} else if (!strcmp(mode, "sgdt")) {
struct {
unsigned short limit;
unsigned long base;
} __attribute__((packed)) val;
for (size_t i = 0; i < loops; ++i)
asm volatile ("sgdt %0" : "=m" (val));
} else if (!strcmp(mode, "gettimeofday")) {
struct timeval tv;
for (size_t i = 0; i < loops; ++i)
gettimeofday(&tv, 0);
} else if (!strcmp(mode, "sys_clock_gettime")) {
for (size_t i = 0; i < loops; ++i)
syscall(__NR_clock_gettime, c, &t);
} else if (!strcmp(mode, "vclock_gettime")) {
for (size_t i = 0; i < loops; ++i)
vgettime(c, &t);
} else if (!strcmp(mode, "vgetcpu")) {
unsigned cpu;
for (size_t i = 0; i < loops; ++i)
vgetcpu(&cpu, NULL, NULL);
} else if (!strcmp(mode, "getpid")) {
for (size_t i = 0; i < loops; ++i)
syscall(SYS_getpid);
} else if (!strcmp(mode, "sys_enosys")) {
for (size_t i = 0; i < loops; ++i)
syscall(0xffffffff, c, &t);
} else if (!strcmp(mode, "rdpmc")) {
// Unlikely to work.
unsigned int eax, edx;
unsigned int ecx = 0;
for (size_t i = 0; i < loops; ++i)
asm volatile ("rdpmc" : "=a" (eax), "=d" (edx) : "c" (ecx));
} else if (!strcmp(mode, "memcpy_2k")) {
unsigned char src[2048] = {}, dst[2048];
for (size_t i = 0; i < loops; ++i) {
asm volatile ("" : "=m" (*src) : "m" (*dst) : "memory");
memcpy(dst, src, 2048);
}
#ifdef __x86_64__
} else if (!strcmp(mode, "vsyscall_time")) {
auto vsyscall_time = (long (*)(long *))0xffffffffff600400;
for (size_t i = 0; i < loops; ++i)
vsyscall_time(nullptr);
#endif
} else if (!strcmp(mode, "raise")) {
for (size_t i = 0; i < loops; ++i)
raise(SIGUSR1);
} else {
printf("Unknown mode %s\n", mode);
return 1;
}
timespec end;
clock_gettime(CLOCK_MONOTONIC, &end);
unsigned long long duration = (end.tv_nsec - start.tv_nsec) + 1000000000ULL * (end.tv_sec - start.tv_sec);
printf("%ld loops in %.5fs = %.2f nsec / loop\n",
(long)loops, float(duration) * 1e-9,
float(duration) / loops);
if (duration == 0)
printf("[WARN]\tThe apparent elapsed time was exactly 0. You have precision issues.\n");
return 0;
}