Permalink
Find file
Fetching contributors…
Cannot retrieve contributors at this time
249 lines (228 sloc) 7.28 KB
#include <cstring>
#include <cstdlib>
#include <cstdio>
#include <cassert>
#include <cstdint>
#include <cinttypes>
#include <getopt.h>
#ifdef __APPLE__
#include <libkern/OSAtomic.h>
#else
#include <pthread.h>
#endif
#ifdef __APPLE__
#include <mach/mach.h>
#include <mach/mach_time.h>
#else
#include <time.h>
#include <sys/time.h>
#endif
#include <string>
#include <algorithm>
#include <vector>
#include <thread>
#include <mutex>
#include <atomic>
// modified from
// http://stackoverflow.com/questions/8177031/does-mac-os-x-have-pthread-spinlock-t-type
namespace {
class spinlock {
private:
spinlock operator=(const spinlock &) = delete;
spinlock(const spinlock &) = delete;
#ifdef __APPLE__
OSSpinLock Lock;
public:
spinlock() : Lock(0) {}
void lock() { OSSpinLockLock(&Lock); }
bool try_lock() { return OSSpinLockTry(&Lock); }
void unlock() { OSSpinLockUnlock(&Lock); }
#else
pthread_spinlock_t Lock;
public:
spinlock() { pthread_spin_init(&Lock, 0); }
void lock() { pthread_spin_lock(&Lock); }
bool try_lock() {
int ret = pthread_spin_trylock(&Lock);
return ret != EBUSY;
}
void unlock() { pthread_spin_unlock(&Lock); }
~spinlock() { pthread_spin_destroy(&Lock); }
#endif
};
} // anonymous namespace
enum BenchType {
Spinlock,
Mutex,
Atomic,
};
static int BenchLoopTimes = 100000;
static int NumOfThread = std::thread::hardware_concurrency();
static const char *BenchName[]{"SpinLock", "Mutex", "Atomic"};
static BenchType Type = Mutex;
static uint64_t getMonoticTime() {
#ifdef __APPLE__
static mach_timebase_info_data_t timebase_info;
if (timebase_info.denom == 0) {
kern_return_t kr = mach_timebase_info(&timebase_info);
if (kr != KERN_SUCCESS)
return -1;
}
assert(timebase_info.denom && "denom of timebase info should not be zero");
return mach_absolute_time() / timebase_info.denom * timebase_info.numer;
#else
struct timespec ts;
if (clock_gettime(CLOCK_MONOTONIC, &ts) < 0)
return 0;
return (uint64_t)ts.tv_sec * 1000 * 1000 * 1000 + (uint64_t)ts.tv_nsec;
#endif
}
static volatile int Value;
static void benchMutex(bool inc, int limit) {
static std::mutex Mutex;
for (int i = 0; i < limit; ++i) {
std::lock_guard<std::mutex> Lock(Mutex);
inc ? ++Value : --Value;
}
}
static void benchSpinlock(bool inc, int limit) {
static spinlock Spinlock;
for (int i = 0; i < limit; ++i) {
std::lock_guard<spinlock> Lock(Spinlock);
inc ? ++Value : --Value;
}
}
// use constructor for atomic
// work around with gcc bug https://gcc.gnu.org/bugzilla/show_bug.cgi?id=64658
static volatile std::atomic<int> AtomicValue(0);
static std::memory_order AtomicOrder = std::memory_order_seq_cst;
static void benchAtomic(bool inc, int limit) {
for (int i = 0; i < limit; ++i) {
inc ? AtomicValue.fetch_add(1, AtomicOrder)
: AtomicValue.fetch_sub(1, AtomicOrder);
}
}
static void bench(const std::function<void(bool, int)> &BenchFunction) {
std::vector<std::thread> Threads;
Threads.reserve(NumOfThread);
for (int i = 0; i < NumOfThread; ++i)
Threads.emplace_back(BenchFunction, true, BenchLoopTimes);
BenchFunction(false, BenchLoopTimes * NumOfThread);
std::for_each(Threads.begin(), Threads.end(),
std::mem_fn(&std::thread::join));
}
static void usage() {
fprintf(stderr, "usage: bench_atomic [options]\n"
"\n"
"if synchronization method is not specified, use 'mutex'.\n"
"\n"
"options:\n"
" --help print this help message\n"
"\n"
" -n the loop times of benchmark [default=%d]\n"
" -j the number threads of benchmark [default=%d]\n"
" -o the atomic order of benchmark [default=seq_cst]\n"
"\n"
" -m use Mutex synchronization [default]\n"
" -s use Spinlock synchronization\n"
" -a use atomic synchronization\n"
"\n",
BenchLoopTimes, NumOfThread);
}
static bool parseGetOpt(int argc, char *argv[]) {
int Opt;
struct option LongOptions[] = {{"num", required_argument, nullptr, 'n'},
{"mutex", no_argument, nullptr, 'm'},
{"spinlock", no_argument, nullptr, 's'},
{"atomic", no_argument, nullptr, 'a'},
{"jobs", no_argument, nullptr, 'j'},
{"order", no_argument, nullptr, 'o'},
{"help", no_argument, nullptr, 'h'},
{nullptr, 0, nullptr, 0, }};
while ((Opt = getopt_long(argc, argv, "n:j:msao:h", LongOptions, nullptr)) !=
-1) {
switch (Opt) {
case 'n': {
char *Stop;
BenchLoopTimes = strtol(optarg, &Stop, 10);
if (*Stop != '\0' || BenchLoopTimes < 0) {
fprintf(stderr, "unknown argument num: %s\n", optarg);
return false;
}
} break;
case 'j': {
char *Stop;
NumOfThread = strtol(optarg, &Stop, 10);
if (*Stop != '\0' || NumOfThread < 1) {
fprintf(stderr, "unknown argument thread: %s\n", optarg);
return false;
}
} break;
case 'o': {
std::string Order = optarg;
Type = Atomic;
if (Order == "relaxed") {
AtomicOrder = std::memory_order_relaxed;
} else if (Order == "consume") {
AtomicOrder = std::memory_order_consume;
} else if (Order == "acquire") {
AtomicOrder = std::memory_order_acquire;
} else if (Order == "release") {
AtomicOrder = std::memory_order_release;
} else if (Order == "acq_rel") {
AtomicOrder = std::memory_order_acq_rel;
} else if (Order == "seq_cst") {
AtomicOrder = std::memory_order_seq_cst;
} else {
fprintf(stderr, "unknown argument atomic order: %s\n", optarg);
return false;
}
} break;
case 'm':
Type = Mutex;
break;
case 's':
Type = Spinlock;
break;
case 'a':
Type = Atomic;
break;
case 'h':
default:
usage();
return false;
}
}
return true;
}
int main(int argc, char *argv[]) {
if (!parseGetOpt(argc, argv))
return -1;
std::function<void(bool, int)> Callable;
switch (Type) {
case Mutex:
Callable = benchMutex;
break;
case Spinlock:
Callable = benchSpinlock;
break;
case Atomic:
assert(AtomicValue.is_lock_free() && "Platform don't support lock free atomic");
Callable = benchAtomic;
break;
}
uint64_t Begin = getMonoticTime();
bench(Callable);
uint64_t Duration = getMonoticTime() - Begin;
int FinalValue = Type == Atomic ? AtomicValue.load() : Value;
assert(FinalValue == 0 && "Result should be zero");
const char *AtomicOrderSuffix = Type != Atomic ? "" :
(AtomicOrder == std::memory_order_relaxed ? " Relaxed" :
(AtomicOrder == std::memory_order_consume ? " Consume" :
(AtomicOrder == std::memory_order_acquire ? " Acquire" :
(AtomicOrder == std::memory_order_release ? " Release" :
(AtomicOrder == std::memory_order_acq_rel ? " AcqRel" : " SeqCst")))));
fprintf(stdout, "jobs: %2d\ttotal time: %16" PRIu64 " ns\taverage time: %8" PRIu64 " ns (%s%s)\n",
NumOfThread, Duration, Duration / BenchLoopTimes, BenchName[Type],
AtomicOrderSuffix);
}