Browse Source

initial commit

master
Stefan Bühler 5 years ago
commit
8444e5ce55

+ 1
- 0
.gitignore View File

@@ -0,0 +1 @@
*-build

+ 54
- 0
CMakeLists.txt View File

@@ -0,0 +1,54 @@
cmake_minimum_required(VERSION 2.8.8 FATAL_ERROR)
project(c++-benchmarks CXX)

include(CMakeDetermineCXXCompiler)

# default to Release
if("${CMAKE_BUILD_TYPE}" STREQUAL "")
set(CMAKE_BUILD_TYPE Release CACHE STRING "Choose the type of build, options are: None(CMAKE_CXX_FLAGS or CMAKE_C_FLAGS used) Debug Release RelWithDebInfo MinSizeRel." FORCE)
endif("${CMAKE_BUILD_TYPE}" STREQUAL "")


if("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(EXTRA_CXX_FLAGS "-std=c++11 -Wall -Wextra -Wno-unused-parameter -pedantic" CACHE STRING "Extra flags used by the compiler during all build types.")
else("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")
set(EXTRA_CXX_FLAGS "" CACHE STRING "Extra flags used by the compiler during all build types.")
endif("${CMAKE_CXX_COMPILER_ID}" STREQUAL "Clang" OR "${CMAKE_CXX_COMPILER_ID}" STREQUAL "GNU")

set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} ${EXTRA_CXX_FLAGS}")

add_executable(benchmark-atomic-costs
benchmark-atomic-costs.cpp)

add_executable(benchmark-atomic-costs-pthread
benchmark-atomic-costs.cpp)
set_target_properties(benchmark-atomic-costs-pthread
PROPERTIES
COMPILE_FLAGS "-pthread"
LINK_FLAGS "-pthread")

add_executable(benchmark-shared-unique
benchmark-shared-unique.cpp
benchmark-shared-unique-impl.cpp)

add_executable(test-NULL-type
test-NULL-type.cpp)

add_executable(test-base-constr
test-base-constr.cpp)

add_executable(test-const-ref-results
test-const-ref-results.cpp)

add_executable(test-constructor-throw
test-constructor-throw.cpp)

add_executable(test-copy-shared-ptr
test-copy-shared-ptr.cpp
test-copy-shared-ptr-impl.cpp)

add_executable(test-rvalue-ref
test-rvalue-ref.cpp)

add_executable(test-smart-factory
test-smart-factory.cpp)

+ 102
- 0
benchmark-atomic-costs.cpp View File

@@ -0,0 +1,102 @@

#include "measure_time.h"

#include <atomic>
#include <cstdint>
#include <thread>
#include <ext/concurrence.h>

#define COUNT (1u << 24)

// use "incl" for 32-bit and "incq" for 64-bit in asm below:
// use 32-bit for now: _Atomic_word should be 32-bit too;
// 64-bit is basically as fast as 32-bit if it fits in your cache
typedef std::uint32_t counting;

volatile counting __attribute__ ((aligned (64))) volatile_counter;
counting __attribute__ ((aligned (64))) simple_counter;
std::atomic<counting> __attribute__ ((aligned (64))) atomic_counter;

_Atomic_word __attribute__ ((aligned (64))) gxx_counter;

void benchmark_volatile() {
// should be the same as asm below
volatile_counter = 0;
for (size_t i = 0; i < COUNT; ++i) {
++volatile_counter;
}
}

void benchmark_asm() {
simple_counter = 0;
for (size_t i = 0; i < COUNT; ++i) {
asm volatile(
"incl %0;"
: "+m"(simple_counter)
);
}
}

void benchmark_local_atomic() {
// compiler doesn't realize no one else can read this counter and
// uses real atomic (i.e. "lock")
std::atomic<counting> local_atomic_counter;
local_atomic_counter = 0;
for (size_t i = 0; i < COUNT; ++i) {
++local_atomic_counter;
}
}

void benchmark_atomic() {
// should be the same as "lock_asm"
atomic_counter = 0;
for (size_t i = 0; i < COUNT; ++i) {
++atomic_counter;
}
}

void benchmark_lock_asm() {
simple_counter = 0;
for (size_t i = 0; i < COUNT; ++i) {
asm volatile(
"lock incl %0;"
: "+m"(simple_counter)
);
}
}

void benchmark_gxx_atomic() {
// if linked with pthread this will use "lock" same as atomic, otherwise
// it uses a simple non-volatile non-atomic add instruction
// https://gcc.gnu.org/onlinedocs/libstdc++/manual/ext_concurrency.html
// claims __atomic_add_dispatch uses a volatile counter, but this isn't
// true in gcc 4.9
gxx_counter = 0;
for (size_t i = 0; i < COUNT; ++i) {
__gnu_cxx::__atomic_add_dispatch(&gxx_counter, 1);
}
}


int main() {
// std::thread fake([] { return; });
// fake.join();

std::cout << "volatile counter : ";
measure_time_with_dry_run(benchmark_volatile);

std::cout << "asm counter : ";
measure_time_with_dry_run(benchmark_asm);

std::cout << "local atomic counter: ";
measure_time_with_dry_run(benchmark_local_atomic);

std::cout << "atomic counter : ";
measure_time_with_dry_run(benchmark_atomic);

std::cout << "lock asm counter : ";
measure_time_with_dry_run(benchmark_lock_asm);

std::cout << "gxx atomic counter : ";
measure_time_with_dry_run(benchmark_gxx_atomic);
}

+ 0
- 0
benchmark-results.txt View File


+ 23
- 0
benchmark-shared-unique-impl.cpp View File

@@ -0,0 +1,23 @@

#include "benchmark-shared-unique.h"

void* forward_raw(void* p) {
return p;
}


std::unique_ptr<size_t> forward_unique(std::unique_ptr<size_t>&& p) {
return std::move(p);
}

std::shared_ptr<size_t> forward_shared(std::shared_ptr<size_t>&& p) {
return std::move(p);
}

std::unique_ptr<Large> forward_unique_large(std::unique_ptr<Large>&& p) {
return std::move(p);
}

std::shared_ptr<Large> forward_shared_large(std::shared_ptr<Large>&& p) {
return std::move(p);
}

+ 128
- 0
benchmark-shared-unique.cpp View File

@@ -0,0 +1,128 @@

#include "benchmark-shared-unique.h"

#include "measure_time.h"
#include "make_unique.h"


#define COUNT (1u << 26)

void benchmark_forward_empty_raw() {
for (size_t i = 0; i < COUNT; ++i) {
(void) forward_raw(nullptr);
}
}

void benchmark_forward_empty_unique() {
for (size_t i = 0; i < COUNT; ++i) {
(void) forward_unique({});
}
}

void benchmark_forward_empty_shared() {
for (size_t i = 0; i < COUNT; ++i) {
(void) forward_shared({});
}
}

void benchmark_forward_empty_shared_from_unique() {
for (size_t i = 0; i < COUNT; ++i) {
(void) forward_shared(std::unique_ptr<size_t>{});
}
}


void benchmark_forward_new_raw() {
for (size_t i = 0; i < COUNT; ++i) {
size_t* p = new size_t{i};
(void) forward_raw(p);
delete p;
}
}

void benchmark_forward_new_unique() {
for (size_t i = 0; i < COUNT; ++i) {
(void) forward_unique(std::make_unique<size_t>(i));
}
}

void benchmark_forward_new_shared() {
for (size_t i = 0; i < COUNT; ++i) {
(void) forward_shared(std::make_shared<size_t>(i));
}
}

void benchmark_forward_new_shared_from_unique() {
for (size_t i = 0; i < COUNT; ++i) {
(void) forward_shared(std::make_unique<size_t>(i));
}
}


void benchmark_forward_new_raw_large() {
for (size_t i = 0; i < COUNT; ++i) {
Large* p = new Large{i};
(void) forward_raw(p);
delete p;
}
}

void benchmark_forward_new_unique_large() {
for (size_t i = 0; i < COUNT; ++i) {
(void) forward_unique_large(std::make_unique<Large>(i));
}
}

void benchmark_forward_new_shared_large() {
for (size_t i = 0; i < COUNT; ++i) {
(void) forward_shared_large(std::make_shared<Large>(i));
}
}

void benchmark_forward_new_shared_large_from_unique() {
for (size_t i = 0; i < COUNT; ++i) {
(void) forward_shared_large(std::make_unique<Large>(i));
}
}

int main() {
std::cout << "forward empty raw : ";
measure_time(benchmark_forward_empty_raw);

std::cout << "forward empty unique : ";
measure_time(benchmark_forward_empty_unique);

std::cout << "forward empty shared : ";
measure_time(benchmark_forward_empty_shared);

std::cout << "forward empty shared from unique : ";
measure_time(benchmark_forward_empty_shared_from_unique);

std::cout << "\n";

std::cout << "forward new raw : ";
measure_time(benchmark_forward_new_raw);

std::cout << "forward new unique : ";
measure_time(benchmark_forward_new_unique);

std::cout << "forward new shared : ";
measure_time(benchmark_forward_new_shared);

std::cout << "forward new shared from unique : ";
measure_time(benchmark_forward_new_shared_from_unique);

std::cout << "\n";

std::cout << "forward new raw large : ";
measure_time(benchmark_forward_new_raw_large);

std::cout << "forward new unique large : ";
measure_time(benchmark_forward_new_unique_large);

std::cout << "forward new shared large : ";
measure_time(benchmark_forward_new_shared_large);

std::cout << "forward new shared large from unique: ";
measure_time(benchmark_forward_new_shared_large_from_unique);
}

+ 16
- 0
benchmark-shared-unique.h View File

@@ -0,0 +1,16 @@
#include <memory>

struct Large {
size_t x;
char filler[512 - sizeof(size_t)] = {};

Large(size_t x) : x(x) {}
};

void* forward_raw(void* p);

std::unique_ptr<size_t> forward_unique(std::unique_ptr<size_t>&& p);
std::shared_ptr<size_t> forward_shared(std::shared_ptr<size_t>&& p);

std::unique_ptr<Large> forward_unique_large(std::unique_ptr<Large>&& p);
std::shared_ptr<Large> forward_shared_large(std::shared_ptr<Large>&& p);

+ 36
- 0
make_unique.h View File

@@ -0,0 +1,36 @@
#pragma once

#include <memory>

#if __cplusplus <= 201103L

namespace {
template<class T>
struct is_unbound_array : std::false_type {};

template<class T>
struct is_unbound_array<T[]> : std::true_type {};

template<class T>
struct is_bound_array : std::false_type {};

template<class T, std::size_t N>
struct is_bound_array<T[N]> : std::true_type {};
}

namespace std {
template<typename T, typename... Args>
typename std::enable_if<!std::is_array<T>::value, std::unique_ptr<T>>::type make_unique(Args&&... args) {
return std::unique_ptr<T>(new T(std::forward<Args>(args)...));
}

template<typename T>
typename std::enable_if<is_unbound_array<T>::value, std::unique_ptr<T>>::type make_unique(size_t size) {
return std::unique_ptr<T>(new typename std::remove_extent<T>::type[size]());
}

template< class T, class... Args >
typename std::enable_if<is_bound_array<T>::value>::type make_unique( Args&&... args ) = delete;
}

#endif

+ 20
- 0
measure_time.h View File

@@ -0,0 +1,20 @@
#pragma once

#include <chrono>
#include <iostream>

template<typename Callable>
void measure_time(Callable&& callable) {
using namespace std::chrono;
steady_clock::time_point t_start = steady_clock::now();
callable();
duration<double> time_span = duration_cast<duration<double>>(steady_clock::now() - t_start);

std::cout << "took " << time_span.count() << " seconds\n";
}

template<typename Callable>
void measure_time_with_dry_run(Callable&& callable) {
callable();
measure_time(std::forward<Callable>(callable));
}

+ 15
- 0
test-NULL-type.cpp View File

@@ -0,0 +1,15 @@

#include "type_name.h"

#include <memory>
#include <utility>
#include <iostream>

int main() {
auto o = NULL;
int* n(reinterpret_cast<int*>(o));
// auto n = std::make_pair(0, (intptr_t) NULL);
std::cout << "decltype(n) is " << TYPE_NAME(n) << '\n';
auto p = std::make_pair(std::ref(o), nullptr);
std::cout << "decltype(p) is " << TYPE_NAME(p) << '\n';
}

+ 30
- 0
test-base-constr.cpp View File

@@ -0,0 +1,30 @@

#include <iostream>

#define L(s) std::cerr << s << "\n"

struct A {
A() { L("A::A()"); }
A(A&&) { L("A::A(A&&)"); }
A(const A&) { L("A::A(const A&)"); }
~A() { L("A::~A()"); }
};

struct B: A {
B() { L("B::B()"); }
B(A&&) { L("B::B(A&&)"); }
//B(B&&) { L("B::B(B&&)"); }
B(const A&) { L("B::B(const A&)"); }
// B(const B&) { L("B::B(const B&)"); }
~B() { L("B::~B()"); }
};

int main() {
L("A a1;");
A a1;
L("B b1(a1);");
B b1(a1);
L("B b1(b1);");
B b2(static_cast<A&>(b1));
return 0;
}

+ 22
- 0
test-const-ref-results.cpp View File

@@ -0,0 +1,22 @@
#include <algorithm>
#include <memory>
#include <iostream>

class C {
public:
int valid;
C() : valid(1) { }
~C() { valid = 0; }
};

/* some "random" comparison, doesn't matter */
bool operator<(C const& a, C const& b) {
return reinterpret_cast<uintptr_t>(&a) < reinterpret_cast<uintptr_t>(&b);
}

int main() {
auto const& m =
// std::max(*std::make_shared<C>(), *std::make_shared<C>());
std::max(C(), C());
std::cout << m.valid << "\n";
}

+ 35
- 0
test-constructor-throw.cpp View File

@@ -0,0 +1,35 @@

#include <exception>
#include <memory>
#include <type_traits>

#include "make_unique.h"

struct C {
private:
struct private_type { };
C(private_type) { }
int *m_i = nullptr;

public:
C() : C(private_type()) { m_i = new int; throw std::exception(); }
~C() { delete m_i; }
C(C const&) = delete;
C& operator=(C const&) = delete;
};


struct D {
std::unique_ptr<int> m_i;
D() : m_i(std::make_unique<int>()) { m_i = std::make_unique<int>(); throw std::exception(); }
};

int main() {
try {
C c;
} catch (...) { }

auto a = std::make_unique<int[]>(10);

return 0;
}

+ 10
- 0
test-copy-shared-ptr-impl.cpp View File

@@ -0,0 +1,10 @@

#include <memory>

void copy_ptr(std::shared_ptr<int>& a, std::shared_ptr<int> const& b) {
a = b;
}

void move_ptr(std::shared_ptr<int>& a, std::shared_ptr<int>& b) {
a = std::move(b);
}

+ 8
- 0
test-copy-shared-ptr.cpp View File

@@ -0,0 +1,8 @@

#include <memory>

void copy_ptr(std::shared_ptr<int>& a, std::shared_ptr<int> const& b);
void move_ptr(std::shared_ptr<int>& a, std::shared_ptr<int>& b);

int main() {
}

+ 23
- 0
test-rvalue-ref.cpp View File

@@ -0,0 +1,23 @@

#include "type_name.h"

#include <iostream>

void f(const int&) {
}

#define T(v) std::cout << #v << ": " << TYPE_NAME(v) << "\n"

int main() {
auto&& i = 1;
auto& ri = i;
auto const& x = std::move(ri);
decltype(ri) y = ri;
f(i);
// f(std::forward(i));
T(i);
T(ri);
T(x);
T(y);
return 0;
}

+ 118
- 0
test-smart-factory.cpp View File

@@ -0,0 +1,118 @@

#include <memory>
#include <type_traits>

class C {
protected:
struct Protected {
private:
Protected() {};
public:
static Protected make() { return { }; }
};
int m_x;

private:
struct Private {
private:
Private() {};
public:
static Private make() { return { }; }
};
int m_y;

public:
/* private constructor: */
C(Private, int x, int y) : m_x(x), m_y(y) { }
/* protected constructor: */
C(Protected, int x) : C(Private::make(), x, 2*x) { }

void leak_private_type(Private) { }

static std::unique_ptr<C> Create(int x) {
return std::unique_ptr<C>(new C(Private::make(), x, 2*x));
}
};

class D : public C {
private:
struct Private { };
public:
D(Private&, int x) : C(Protected::make(), x) { }

static std::shared_ptr<D> Create(int x) {
Private p;
return std::make_shared<D>(p, 4*x);
}
};


class A {
struct this_is_private {
};

public:
explicit A(const this_is_private &, int) {}

static ::std::shared_ptr<A> create(int x) {
return ::std::make_shared<A>(this_is_private{}, x);
}

protected:
A(const A &) = delete;
const A &operator =(const A &) = delete;
};

::std::shared_ptr<A> foo(int x)
{
return A::create(x);
}


/* usage: decltype(return_args(&Class::memberfunction)) */
template<typename Base, typename Result, typename... Args>
std::tuple<Args...> return_args(Result (Base::*func)(Args...));

/* usage: decltype(result_of(&Class::memberfunction)) */
template<typename Base, typename Result, typename... Args>
Result result_of(Result (Base::*func)(Args...));

/* usage: decay_tuple_element<0, decltype(return_args(&Class::memberfunction))> */
template <size_t i, typename Tuple>
using decay_tuple_element = typename std::decay<typename std::tuple_element<i, Tuple>::type>::type;


struct _is_static_castable_impl
{
template<typename _From, typename _To, typename = decltype(static_cast<_To>(std::declval<_From>()))>
static std::true_type __test(int);

template<typename, typename>
static std::false_type __test(...);
};

template<typename _From, typename _To>
struct is_static_castable
: public _is_static_castable_impl
{
typedef decltype(__test<_From, _To>(0)) type;
};

template<typename _From, typename _To>
using is_static_castable_t = typename is_static_castable<_From, _To>::type;



int main() {
auto c = C::Create(0);
auto d = D::Create(0);

typedef decay_tuple_element<0, decltype(return_args(&C::leak_private_type))> Private;
Private::make();


auto a1 = foo(0);
auto a2 = std::shared_ptr<A>(new A({}, 0));

// C c1({}, 0, 0);
}

+ 38
- 0
type_name.h View File

@@ -0,0 +1,38 @@
#pragma once

#include <type_traits>
#include <typeinfo>
#ifndef _MSC_VER
# include <cxxabi.h>
#endif
#include <memory>
#include <string>
#include <cstdlib>

template <class T>
std::string
type_name()
{
typedef typename std::remove_reference<T>::type TR;
std::unique_ptr<char, void(*)(void*)> own
(
#ifndef _MSC_VER
abi::__cxa_demangle(typeid(TR).name(), nullptr, nullptr, nullptr),
#else
nullptr,
#endif
std::free
);
std::string r = own != nullptr ? own.get() : typeid(TR).name();
if (std::is_const<TR>::value)
r += " const";
if (std::is_volatile<TR>::value)
r += " volatile";
if (std::is_lvalue_reference<T>::value)
r += "&";
else if (std::is_rvalue_reference<T>::value)
r += "&&";
return r;
}

#define TYPE_NAME(p) (type_name<decltype(p)>())

Loading…
Cancel
Save