commit 8444e5ce5575a93a270c8c48081693f6ae9ed9d1 Author: Stefan Bühler Date: Sat Jul 19 15:35:25 2014 +0200 initial commit diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..1ff49ea --- /dev/null +++ b/.gitignore @@ -0,0 +1 @@ +*-build diff --git a/CMakeLists.txt b/CMakeLists.txt new file mode 100644 index 0000000..d971ac5 --- /dev/null +++ b/CMakeLists.txt @@ -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) diff --git a/benchmark-atomic-costs.cpp b/benchmark-atomic-costs.cpp new file mode 100644 index 0000000..136a5c7 --- /dev/null +++ b/benchmark-atomic-costs.cpp @@ -0,0 +1,102 @@ + +#include "measure_time.h" + +#include +#include +#include +#include + +#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 __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 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); +} diff --git a/benchmark-results.txt b/benchmark-results.txt new file mode 100644 index 0000000..e69de29 diff --git a/benchmark-shared-unique-impl.cpp b/benchmark-shared-unique-impl.cpp new file mode 100644 index 0000000..f1261ba --- /dev/null +++ b/benchmark-shared-unique-impl.cpp @@ -0,0 +1,23 @@ + +#include "benchmark-shared-unique.h" + +void* forward_raw(void* p) { + return p; +} + + +std::unique_ptr forward_unique(std::unique_ptr&& p) { + return std::move(p); +} + +std::shared_ptr forward_shared(std::shared_ptr&& p) { + return std::move(p); +} + +std::unique_ptr forward_unique_large(std::unique_ptr&& p) { + return std::move(p); +} + +std::shared_ptr forward_shared_large(std::shared_ptr&& p) { + return std::move(p); +} diff --git a/benchmark-shared-unique.cpp b/benchmark-shared-unique.cpp new file mode 100644 index 0000000..c8c4a53 --- /dev/null +++ b/benchmark-shared-unique.cpp @@ -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{}); + } +} + + +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(i)); + } +} + +void benchmark_forward_new_shared() { + for (size_t i = 0; i < COUNT; ++i) { + (void) forward_shared(std::make_shared(i)); + } +} + +void benchmark_forward_new_shared_from_unique() { + for (size_t i = 0; i < COUNT; ++i) { + (void) forward_shared(std::make_unique(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(i)); + } +} + +void benchmark_forward_new_shared_large() { + for (size_t i = 0; i < COUNT; ++i) { + (void) forward_shared_large(std::make_shared(i)); + } +} + +void benchmark_forward_new_shared_large_from_unique() { + for (size_t i = 0; i < COUNT; ++i) { + (void) forward_shared_large(std::make_unique(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); +} diff --git a/benchmark-shared-unique.h b/benchmark-shared-unique.h new file mode 100644 index 0000000..1f593b2 --- /dev/null +++ b/benchmark-shared-unique.h @@ -0,0 +1,16 @@ +#include + +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 forward_unique(std::unique_ptr&& p); +std::shared_ptr forward_shared(std::shared_ptr&& p); + +std::unique_ptr forward_unique_large(std::unique_ptr&& p); +std::shared_ptr forward_shared_large(std::shared_ptr&& p); diff --git a/make_unique.h b/make_unique.h new file mode 100644 index 0000000..efc04b5 --- /dev/null +++ b/make_unique.h @@ -0,0 +1,36 @@ +#pragma once + +#include + +#if __cplusplus <= 201103L + +namespace { + template + struct is_unbound_array : std::false_type {}; + + template + struct is_unbound_array : std::true_type {}; + + template + struct is_bound_array : std::false_type {}; + + template + struct is_bound_array : std::true_type {}; +} + +namespace std { + template + typename std::enable_if::value, std::unique_ptr>::type make_unique(Args&&... args) { + return std::unique_ptr(new T(std::forward(args)...)); + } + + template + typename std::enable_if::value, std::unique_ptr>::type make_unique(size_t size) { + return std::unique_ptr(new typename std::remove_extent::type[size]()); + } + + template< class T, class... Args > + typename std::enable_if::value>::type make_unique( Args&&... args ) = delete; +} + +#endif diff --git a/measure_time.h b/measure_time.h new file mode 100644 index 0000000..bf33b27 --- /dev/null +++ b/measure_time.h @@ -0,0 +1,20 @@ +#pragma once + +#include +#include + +template +void measure_time(Callable&& callable) { + using namespace std::chrono; + steady_clock::time_point t_start = steady_clock::now(); + callable(); + duration time_span = duration_cast>(steady_clock::now() - t_start); + + std::cout << "took " << time_span.count() << " seconds\n"; +} + +template +void measure_time_with_dry_run(Callable&& callable) { + callable(); + measure_time(std::forward(callable)); +} diff --git a/test-NULL-type.cpp b/test-NULL-type.cpp new file mode 100644 index 0000000..e14a85e --- /dev/null +++ b/test-NULL-type.cpp @@ -0,0 +1,15 @@ + +#include "type_name.h" + +#include +#include +#include + +int main() { + auto o = NULL; + int* n(reinterpret_cast(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'; +} diff --git a/test-base-constr.cpp b/test-base-constr.cpp new file mode 100644 index 0000000..601645b --- /dev/null +++ b/test-base-constr.cpp @@ -0,0 +1,30 @@ + +#include + +#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(b1)); + return 0; +} diff --git a/test-const-ref-results.cpp b/test-const-ref-results.cpp new file mode 100644 index 0000000..2f778a3 --- /dev/null +++ b/test-const-ref-results.cpp @@ -0,0 +1,22 @@ +#include +#include +#include + +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(&a) < reinterpret_cast(&b); +} + +int main() { + auto const& m = + // std::max(*std::make_shared(), *std::make_shared()); + std::max(C(), C()); + std::cout << m.valid << "\n"; +} diff --git a/test-constructor-throw.cpp b/test-constructor-throw.cpp new file mode 100644 index 0000000..a468ed1 --- /dev/null +++ b/test-constructor-throw.cpp @@ -0,0 +1,35 @@ + +#include +#include +#include + +#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 m_i; + D() : m_i(std::make_unique()) { m_i = std::make_unique(); throw std::exception(); } +}; + +int main() { + try { + C c; + } catch (...) { } + + auto a = std::make_unique(10); + + return 0; +} diff --git a/test-copy-shared-ptr-impl.cpp b/test-copy-shared-ptr-impl.cpp new file mode 100644 index 0000000..9224ba0 --- /dev/null +++ b/test-copy-shared-ptr-impl.cpp @@ -0,0 +1,10 @@ + +#include + +void copy_ptr(std::shared_ptr& a, std::shared_ptr const& b) { + a = b; +} + +void move_ptr(std::shared_ptr& a, std::shared_ptr& b) { + a = std::move(b); +} diff --git a/test-copy-shared-ptr.cpp b/test-copy-shared-ptr.cpp new file mode 100644 index 0000000..a28efaf --- /dev/null +++ b/test-copy-shared-ptr.cpp @@ -0,0 +1,8 @@ + +#include + +void copy_ptr(std::shared_ptr& a, std::shared_ptr const& b); +void move_ptr(std::shared_ptr& a, std::shared_ptr& b); + +int main() { +} diff --git a/test-rvalue-ref.cpp b/test-rvalue-ref.cpp new file mode 100644 index 0000000..26035c6 --- /dev/null +++ b/test-rvalue-ref.cpp @@ -0,0 +1,23 @@ + +#include "type_name.h" + +#include + +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; +} diff --git a/test-smart-factory.cpp b/test-smart-factory.cpp new file mode 100644 index 0000000..fd10792 --- /dev/null +++ b/test-smart-factory.cpp @@ -0,0 +1,118 @@ + +#include +#include + +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 Create(int x) { + return std::unique_ptr(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 Create(int x) { + Private p; + return std::make_shared(p, 4*x); + } +}; + + +class A { + struct this_is_private { + }; + + public: + explicit A(const this_is_private &, int) {} + + static ::std::shared_ptr create(int x) { + return ::std::make_shared(this_is_private{}, x); + } + + protected: + A(const A &) = delete; + const A &operator =(const A &) = delete; +}; + +::std::shared_ptr foo(int x) +{ + return A::create(x); +} + + + /* usage: decltype(return_args(&Class::memberfunction)) */ + template + std::tuple return_args(Result (Base::*func)(Args...)); + + /* usage: decltype(result_of(&Class::memberfunction)) */ + template + Result result_of(Result (Base::*func)(Args...)); + + /* usage: decay_tuple_element<0, decltype(return_args(&Class::memberfunction))> */ + template + using decay_tuple_element = typename std::decay::type>::type; + + + struct _is_static_castable_impl + { + template(std::declval<_From>()))> + static std::true_type __test(int); + + template + static std::false_type __test(...); + }; + + template + struct is_static_castable + : public _is_static_castable_impl + { + typedef decltype(__test<_From, _To>(0)) type; + }; + + template + 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(new A({}, 0)); + +// C c1({}, 0, 0); +} diff --git a/type_name.h b/type_name.h new file mode 100644 index 0000000..d6b6032 --- /dev/null +++ b/type_name.h @@ -0,0 +1,38 @@ +#pragma once + +#include +#include +#ifndef _MSC_VER +# include +#endif +#include +#include +#include + +template +std::string +type_name() +{ + typedef typename std::remove_reference::type TR; + std::unique_ptr 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::value) + r += " const"; + if (std::is_volatile::value) + r += " volatile"; + if (std::is_lvalue_reference::value) + r += "&"; + else if (std::is_rvalue_reference::value) + r += "&&"; + return r; +} + +#define TYPE_NAME(p) (type_name())