Skip to content

Instantly share code, notes, and snippets.

@dpzmick
Created May 26, 2017 04:35
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save dpzmick/a8f937c5e35185092b6af9a5ed87a7b8 to your computer and use it in GitHub Desktop.
Save dpzmick/a8f937c5e35185092b6af9a5ed87a7b8 to your computer and use it in GitHub Desktop.
#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