Created
May 26, 2017 04:35
-
-
Save dpzmick/a8f937c5e35185092b6af9a5ed87a7b8 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include "benchmark/benchmark.h" | |
#include <cstdlib> | |
#include <random> | |
#define LIMIT 5*1024*1024 | |
enum class Variant { eA, eB }; | |
struct Wrapper { | |
Variant v; | |
char payload[]; | |
}; | |
struct WrapperWithOffset { | |
Variant v; | |
size_t offset; | |
char payload[]; | |
}; | |
struct __attribute__((packed)) A { | |
int64_t a; | |
int64_t b; | |
char arr[16]; | |
int64_t c; | |
}; | |
struct __attribute__((packed)) B { | |
int64_t a; | |
int64_t b; | |
char arr[17]; | |
int64_t c; | |
}; | |
template <typename T> | |
void writeFields(T* t) | |
{ | |
t->a = 12; | |
t->b = 25; | |
t->c = 16; | |
} | |
void writeFieldsWithOffset(A* t, size_t c_offset) | |
{ | |
// make sure a and b are always at the same offset in struct A and struct B | |
static_assert(offsetof(A, a) == offsetof(B, a), "!"); | |
static_assert(offsetof(A, b) == offsetof(B, b), "!"); | |
t->a = 12; | |
t->b = 25; | |
// c will be at the offset we've provided | |
*(int64_t*)(((char*)t + c_offset)) = 16; | |
} | |
void __attribute__((noinline)) writeLessSafe(WrapperWithOffset* w) | |
{ | |
A* a = reinterpret_cast<A*>(w->payload); | |
writeFieldsWithOffset(a, w->offset); | |
} | |
void __attribute__((noinline)) write(Wrapper* w) | |
{ | |
if (w->v == Variant::eA) { | |
writeFields<A>(reinterpret_cast<A*>(w->payload)); | |
} | |
else { | |
writeFields<B>(reinterpret_cast<B*>(w->payload)); | |
} | |
} | |
static void extra(Wrapper*) { } | |
static void extra(WrapperWithOffset* w) | |
{ | |
w->offset = w->v == Variant::eA ? offsetof(A, c) : offsetof(B, c); | |
} | |
template <typename T = Wrapper> | |
static T* populateBuffer(char* buffer, Variant v) | |
{ | |
auto* w = reinterpret_cast<T*>(buffer); | |
new (w) T; | |
w->v = v; | |
if (w->v == Variant::eA) { | |
new (w->payload) A; | |
} | |
else { | |
new (w->payload) B; | |
} | |
// building with gcc-6.3, this isn't supported | |
// if constexpr (std::is_same<T, WrapperWithOffset>::value) { | |
// w->offset = w->v == Variant::eA ? offsetof(A, c) : offsetof(B, c); | |
// } | |
extra(w); | |
return w; | |
} | |
static void BMA(benchmark::State& state) { | |
char buffer[sizeof(Wrapper) + sizeof(A)]; | |
auto* w = populateBuffer(buffer, Variant::eA); | |
while (state.KeepRunning()) { | |
for (size_t i = 0; i < LIMIT; ++i) { | |
write(w); | |
} | |
} | |
auto* v = reinterpret_cast<A*>(w->payload); | |
assert(v->a == 12); | |
assert(v->b == 25); | |
assert(v->c == 16); | |
} | |
static void BMB(benchmark::State& state) { | |
char buffer[sizeof(Wrapper) + sizeof(B)]; | |
auto* w = populateBuffer(buffer, Variant::eB); | |
while (state.KeepRunning()) { | |
for (size_t i = 0; i < LIMIT; ++i) { | |
write(w); | |
} | |
} | |
auto* v = reinterpret_cast<B*>(w->payload); | |
assert(v->a == 12); | |
assert(v->b == 25); | |
assert(v->c == 16); | |
} | |
static void BMA_less_safe(benchmark::State& state) { | |
char buffer[sizeof(WrapperWithOffset) + sizeof(A)]; | |
auto* w = populateBuffer<WrapperWithOffset>(buffer, Variant::eA); | |
while (state.KeepRunning()) { | |
for (size_t i = 0; i < LIMIT; ++i) { | |
writeLessSafe(w); | |
} | |
} | |
auto* v = reinterpret_cast<A*>(w->payload); | |
assert(v->a == 12); | |
assert(v->b == 25); | |
assert(v->c == 16); | |
} | |
static void BMB_less_safe(benchmark::State& state) { | |
char buffer[sizeof(WrapperWithOffset) + sizeof(B)]; | |
auto* w = populateBuffer<WrapperWithOffset>(buffer, Variant::eB); | |
while (state.KeepRunning()) { | |
for (size_t i = 0; i < LIMIT; ++i) { | |
writeLessSafe(w); | |
} | |
} | |
auto* v = reinterpret_cast<B*>(w->payload); | |
assert(v->a == 12); | |
assert(v->b == 25); | |
assert(v->c == 16); | |
} | |
Variant pick() { | |
static std::random_device rd; | |
static std::mt19937 gen(rd()); | |
std::uniform_int_distribution<> dis(1, 2); | |
auto d = dis(gen); | |
if (d == 1) { | |
return Variant::eA; | |
} | |
else if (d == 2) { | |
return Variant::eB; | |
} | |
else { | |
abort(); | |
} | |
} | |
static void BM_switch(benchmark::State& state) { | |
char buffer[sizeof(Wrapper) + std::max(sizeof(A), sizeof(B))]; | |
auto* w = populateBuffer(buffer, Variant::eA); | |
while (state.KeepRunning()) { | |
state.PauseTiming(); | |
auto v = pick(); | |
w = populateBuffer(buffer, v); | |
state.ResumeTiming(); | |
write(w); | |
state.PauseTiming(); | |
if (v == Variant::eA) { | |
auto* v = reinterpret_cast<A*>(w->payload); | |
assert(v->a == 12); | |
assert(v->b == 25); | |
assert(v->c == 16); | |
} | |
else { | |
auto* v = reinterpret_cast<B*>(w->payload); | |
assert(v->a == 12); | |
assert(v->b == 25); | |
assert(v->c == 16); | |
} | |
state.ResumeTiming(); | |
} | |
} | |
static void BM_less_safe_switch(benchmark::State& state) { | |
char buffer[sizeof(WrapperWithOffset) + std::max(sizeof(A), sizeof(B))]; | |
auto* w = populateBuffer<WrapperWithOffset>(buffer, Variant::eA); | |
while (state.KeepRunning()) { | |
state.PauseTiming(); | |
auto v = pick(); | |
w = populateBuffer<WrapperWithOffset>(buffer, v); | |
state.ResumeTiming(); | |
writeLessSafe(w); | |
state.PauseTiming(); | |
if (v == Variant::eA) { | |
auto* v = reinterpret_cast<A*>(w->payload); | |
assert(v->a == 12); | |
assert(v->b == 25); | |
assert(v->c == 16); | |
} | |
else { | |
auto* v = reinterpret_cast<B*>(w->payload); | |
assert(v->a == 12); | |
assert(v->b == 25); | |
assert(v->c == 16); | |
} | |
state.ResumeTiming(); | |
} | |
} | |
BENCHMARK(BMA); | |
BENCHMARK(BMA_less_safe); | |
BENCHMARK(BMB); | |
BENCHMARK(BMB_less_safe); | |
BENCHMARK(BM_switch); | |
BENCHMARK(BM_less_safe_switch); | |
BENCHMARK_MAIN(); |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment