@ -0,0 +1 @@ | |||
*-build |
@ -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) |
@ -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 +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); | |||
} |
@ -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); | |||
} |
@ -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); |
@ -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 |
@ -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)); | |||
} |
@ -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'; | |||
} |
@ -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; | |||
} |
@ -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"; | |||
} |
@ -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; | |||
} |
@ -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); | |||
} |
@ -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() { | |||
} |
@ -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; | |||
} |
@ -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); | |||
} |
@ -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)>()) |