Skip to content

Instantly share code, notes, and snippets.

@dpzmick dpzmick/test.cpp
Created May 26, 2017

Embed
What would you like to do?
#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
You can’t perform that action at this time.
You signed in with another tab or window. Reload to refresh your session. You signed out in another tab or window. Reload to refresh your session.