mirror of
https://github.com/boostorg/leaf.git
synced 2026-01-25 06:12:27 +00:00
Much simplified benchmark.
This commit is contained in:
@@ -17,12 +17,24 @@
|
||||
# error Please disable diagnostics.
|
||||
#endif
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define NOINLINE __declspec(noinline)
|
||||
# define ALWAYS_INLINE __forceinline
|
||||
#else
|
||||
# define NOINLINE __attribute__((noinline))
|
||||
# define ALWAYS_INLINE __attribute__((always_inline)) inline
|
||||
#endif
|
||||
|
||||
#include <cstring>
|
||||
#include <cstdlib>
|
||||
#include <cassert>
|
||||
#include <chrono>
|
||||
#include <iostream>
|
||||
#include <fstream>
|
||||
#include <iomanip>
|
||||
#include <numeric>
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
|
||||
namespace boost
|
||||
{
|
||||
@@ -37,63 +49,47 @@ namespace boost
|
||||
|
||||
namespace leaf = boost::leaf;
|
||||
|
||||
#ifdef _MSC_VER
|
||||
# define NOINLINE __declspec(noinline)
|
||||
#else
|
||||
# define NOINLINE __attribute__((noinline))
|
||||
#endif
|
||||
#define USING_RESULT_TYPE "leaf::result<T>"
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
enum class e_error_code
|
||||
struct e_int
|
||||
{
|
||||
ec1=1,
|
||||
ec2
|
||||
int value;
|
||||
};
|
||||
|
||||
namespace boost { namespace leaf {
|
||||
|
||||
template <> struct is_e_type<e_error_code>: std::true_type { };
|
||||
|
||||
} }
|
||||
|
||||
struct e_system_error
|
||||
{
|
||||
e_error_code value;
|
||||
e_int value;
|
||||
std::string what;
|
||||
};
|
||||
|
||||
// Note: in LEAF, handling of error objects is O(1) no matter how many stack frames.
|
||||
struct e_heavy_payload
|
||||
{
|
||||
char value[4096];
|
||||
|
||||
e_heavy_payload() noexcept
|
||||
{
|
||||
std::memset(value, std::rand(), sizeof(value));
|
||||
}
|
||||
std::array<char, 4096> value;
|
||||
};
|
||||
|
||||
template <class E>
|
||||
E make_error() noexcept;
|
||||
|
||||
template <>
|
||||
inline e_error_code make_error<e_error_code>() noexcept
|
||||
NOINLINE e_int make_error<e_int>() noexcept
|
||||
{
|
||||
return (std::rand()%2) ? e_error_code::ec1 : e_error_code::ec2;
|
||||
return { std::rand() };
|
||||
}
|
||||
|
||||
template <>
|
||||
inline e_system_error make_error<e_system_error>() noexcept
|
||||
NOINLINE e_system_error make_error<e_system_error>() noexcept
|
||||
{
|
||||
e_error_code ec = make_error<e_error_code>();
|
||||
return { ec, std::string(ec==e_error_code::ec1 ? "ec1" : "ec2") };
|
||||
return { make_error<e_int>(), std::string(std::rand()%32, ' ') };
|
||||
}
|
||||
|
||||
template <>
|
||||
inline e_heavy_payload make_error<e_heavy_payload>() noexcept
|
||||
NOINLINE e_heavy_payload make_error<e_heavy_payload>() noexcept
|
||||
{
|
||||
return e_heavy_payload();
|
||||
e_heavy_payload e;
|
||||
std::fill(e.value.begin(), e.value.end(), std::rand());
|
||||
return e;
|
||||
}
|
||||
|
||||
inline bool should_fail( int failure_rate ) noexcept
|
||||
@@ -103,151 +99,88 @@ inline bool should_fail( int failure_rate ) noexcept
|
||||
return (std::rand()%100) < failure_rate;
|
||||
}
|
||||
|
||||
inline int handle_error(e_int e ) noexcept
|
||||
{
|
||||
return e.value;
|
||||
}
|
||||
|
||||
inline int handle_error( e_system_error const & e ) noexcept
|
||||
{
|
||||
return handle_error(e.value) + e.what.size();
|
||||
}
|
||||
|
||||
inline int handle_error( e_heavy_payload const & e ) noexcept
|
||||
{
|
||||
return std::accumulate(e.value.begin(), e.value.end(), 0);
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
template <int N, class T, class E>
|
||||
struct benchmark_check_error_noinline
|
||||
// This is used to change the "success" type at each level.
|
||||
// Generally, functions return values of different types.
|
||||
template <int N, class E, bool Odd = N%2>
|
||||
struct select_result_type;
|
||||
|
||||
template <int N, class E>
|
||||
struct select_result_type<N, E, true>
|
||||
{
|
||||
NOINLINE static leaf::result<T> f( int failure_rate ) noexcept
|
||||
using type = leaf::result<int>; // Does not depend on E
|
||||
};
|
||||
|
||||
template <int N, class E>
|
||||
struct select_result_type<N, E, false>
|
||||
{
|
||||
using type = leaf::result<float>; // Does not depend on E
|
||||
};
|
||||
|
||||
template <int N, class E>
|
||||
using select_result_t = typename select_result_type<N, E>::type;
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
template <int N, class E>
|
||||
struct benchmark
|
||||
{
|
||||
using e_type = E;
|
||||
|
||||
NOINLINE static select_result_t<N, E> f( int failure_rate ) noexcept
|
||||
{
|
||||
LEAF_AUTO(x, (benchmark_check_error_noinline<N-1, T, E>::f(failure_rate)));
|
||||
LEAF_AUTO(x, (benchmark<N-1, E>::f(failure_rate)));
|
||||
return x+1;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class E>
|
||||
struct benchmark_check_error_noinline<1, T, E>
|
||||
template <class E>
|
||||
struct benchmark<1, E>
|
||||
{
|
||||
NOINLINE static leaf::result<T> f( int failure_rate ) noexcept
|
||||
using e_type = E;
|
||||
|
||||
NOINLINE static select_result_t<1, E> f( int failure_rate ) noexcept
|
||||
{
|
||||
if( should_fail(failure_rate) )
|
||||
return leaf::new_error(make_error<E>());
|
||||
else
|
||||
return T{ };
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
template <int N, class T, class E>
|
||||
struct benchmark_check_error_inline
|
||||
{
|
||||
static leaf::result<T> f( int failure_rate ) noexcept
|
||||
{
|
||||
LEAF_AUTO(x, (benchmark_check_error_inline<N-1, T, E>::f(failure_rate)));
|
||||
return x+1;
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class E>
|
||||
struct benchmark_check_error_inline<1, T, E>
|
||||
{
|
||||
static leaf::result<T> f( int failure_rate ) noexcept
|
||||
{
|
||||
if( should_fail(failure_rate) )
|
||||
return leaf::new_error(make_error<E>());
|
||||
else
|
||||
return T{ };
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
template <int N, class T, class E>
|
||||
struct benchmark_handle_some_noinline
|
||||
{
|
||||
NOINLINE static leaf::result<T> f( int failure_rate ) noexcept
|
||||
{
|
||||
if( N%4 )
|
||||
{
|
||||
LEAF_AUTO(x, (benchmark_handle_some_noinline<N-1, T, E>::f(failure_rate)));
|
||||
return x+1;
|
||||
}
|
||||
else
|
||||
return leaf::try_handle_some(
|
||||
[=]() -> leaf::result<T>
|
||||
{
|
||||
LEAF_AUTO(x, (benchmark_handle_some_noinline<N-1, T, E>::f(failure_rate)));
|
||||
return x+1;
|
||||
},
|
||||
[]( leaf::match<e_error_code, e_error_code::ec2> )
|
||||
{
|
||||
return 2;
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class E>
|
||||
struct benchmark_handle_some_noinline<1, T, E>
|
||||
{
|
||||
NOINLINE static leaf::result<T> f( int failure_rate ) noexcept
|
||||
{
|
||||
if( should_fail(failure_rate) )
|
||||
return leaf::new_error(make_error<E>());
|
||||
else
|
||||
return T{ };
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
template <int N, class T, class E>
|
||||
struct benchmark_handle_some_inline
|
||||
{
|
||||
static leaf::result<T> f( int failure_rate ) noexcept
|
||||
{
|
||||
if( N%4 )
|
||||
{
|
||||
LEAF_AUTO(x, (benchmark_handle_some_inline<N-1, T, E>::f(failure_rate)));
|
||||
return x+1;
|
||||
}
|
||||
else
|
||||
return leaf::try_handle_some(
|
||||
[=]() -> leaf::result<T>
|
||||
{
|
||||
LEAF_AUTO(x, (benchmark_handle_some_inline<N-1, T, E>::f(failure_rate)));
|
||||
return x+1;
|
||||
},
|
||||
[]( leaf::match<e_error_code, e_error_code::ec2> )
|
||||
{
|
||||
return 2;
|
||||
} );
|
||||
}
|
||||
};
|
||||
|
||||
template <class T, class E>
|
||||
struct benchmark_handle_some_inline<1, T, E>
|
||||
{
|
||||
static leaf::result<T> f( int failure_rate ) noexcept
|
||||
{
|
||||
if( should_fail(failure_rate) )
|
||||
return leaf::new_error(make_error<E>());
|
||||
else
|
||||
return T{ };
|
||||
return std::rand();
|
||||
}
|
||||
};
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
template <class Benchmark>
|
||||
int runner( int failure_rate ) noexcept
|
||||
NOINLINE int runner( int failure_rate ) noexcept
|
||||
{
|
||||
return leaf::try_handle_all(
|
||||
[=]
|
||||
{
|
||||
return Benchmark::f(failure_rate);
|
||||
},
|
||||
[]( e_error_code const & )
|
||||
[]( typename Benchmark::e_type const & e )
|
||||
{
|
||||
return -1;
|
||||
},
|
||||
[]( e_heavy_payload const & )
|
||||
{
|
||||
return -2;
|
||||
return handle_error(e);
|
||||
},
|
||||
[]
|
||||
{
|
||||
return -3;
|
||||
return -1;
|
||||
} );
|
||||
}
|
||||
|
||||
@@ -263,7 +196,7 @@ std::fstream append_csv()
|
||||
else
|
||||
{
|
||||
std::fstream fs("benchmark.csv", std::fstream::out | std::fstream::app);
|
||||
fs << ",\"Check, noinline\",\"Check, inline\",\"Handle some, noinline\", \"Handle some, inline\",\"Error rate\"\n";
|
||||
fs << "\"Result Type\",2%,98%\n";
|
||||
return fs;
|
||||
}
|
||||
}
|
||||
@@ -277,7 +210,7 @@ int print_elapsed_time( int iteration_count, F && f )
|
||||
val += std::forward<F>(f)();
|
||||
auto stop = std::chrono::steady_clock::now();
|
||||
int elapsed = std::chrono::duration_cast<std::chrono::microseconds>(stop-start).count();
|
||||
std::cout << std::right << std::setw(8) << elapsed;
|
||||
std::cout << std::right << std::setw(9) << elapsed;
|
||||
append_csv() << ',' << elapsed;
|
||||
return val;
|
||||
}
|
||||
@@ -287,43 +220,34 @@ int print_elapsed_time( int iteration_count, F && f )
|
||||
template <int Depth, class E>
|
||||
int benchmark_type( char const * type_name, int iteration_count )
|
||||
{
|
||||
int const test_rates[ ] = { 2, 50, 98 };
|
||||
int x=0;
|
||||
std::cout << "----------------|--------------------|----------|-------|--------";
|
||||
for( auto fr : test_rates )
|
||||
{
|
||||
append_csv() << "LEAF";
|
||||
std::cout << '\n' << std::left << std::setw(16) << type_name << "| LEAF_AUTO | Disabled | " << std::right << std::setw(4) << fr << "% |";
|
||||
std::srand(0);
|
||||
x += print_elapsed_time( iteration_count, [=] { return runner<benchmark_check_error_noinline<Depth, int, E>>(fr); } );
|
||||
std::cout << '\n' << std::left << std::setw(16) << type_name << "| LEAF_AUTO | Enabled | " << std::right << std::setw(4) << fr << "% |";
|
||||
std::srand(0);
|
||||
x += print_elapsed_time( iteration_count, [=] { return runner<benchmark_check_error_inline<Depth, int, E>>(fr); } );
|
||||
std::cout << '\n' << std::left << std::setw(16) << type_name << "| try_handle_some | Disabled | " << std::right << std::setw(4) << fr << "% |";
|
||||
std::srand(0);
|
||||
x += print_elapsed_time( iteration_count, [=] { return runner<benchmark_handle_some_noinline<Depth, int, E>>(fr); } );
|
||||
std::cout << '\n' << std::left << std::setw(16) << type_name << "| try_handle_some | Enabled | " << std::right << std::setw(4) << fr << "% |";
|
||||
std::srand(0);
|
||||
x += print_elapsed_time( iteration_count, [=] { return runner<benchmark_handle_some_inline<Depth, int, E>>(fr); } );
|
||||
append_csv() << ',' << fr << '\n';
|
||||
};
|
||||
std::cout << '\n';
|
||||
append_csv() << "\"" USING_RESULT_TYPE "\"";
|
||||
std::cout << '\n' << std::left << std::setw(16) << type_name << '|';
|
||||
std::srand(0);
|
||||
x += print_elapsed_time( iteration_count, [] { return runner<benchmark<Depth, E>>(2); } );
|
||||
std::cout << " |";
|
||||
std::srand(0);
|
||||
x += print_elapsed_time( iteration_count, [] { return runner<benchmark<Depth, E>>(98); } );
|
||||
append_csv() << '\n';
|
||||
return x;
|
||||
}
|
||||
|
||||
//////////////////////////////////////
|
||||
|
||||
int main( int argc, char const * argv[] )
|
||||
int main()
|
||||
{
|
||||
int const depth = 32;
|
||||
int const depth = 10;
|
||||
int const iteration_count = 10000;
|
||||
std::cout <<
|
||||
iteration_count << " iterations, call depth " << depth << ", sizeof(e_heavy_payload) = " << sizeof(e_heavy_payload) << "\n"
|
||||
"LEAF\n"
|
||||
" | | Function | Error | Elapsed\n"
|
||||
"Error type | At each level | inlining | rate | (μs)\n";
|
||||
return
|
||||
benchmark_type<depth, e_error_code>("e_error_code", iteration_count) +
|
||||
benchmark_type<depth, e_system_error>("e_system_error", iteration_count) +
|
||||
benchmark_type<depth, e_heavy_payload>("e_heavy_payload", iteration_count);
|
||||
USING_RESULT_TYPE "\n"
|
||||
"Error type | 2% (μs) | 98% (μs)\n"
|
||||
"----------------|----------|---------";
|
||||
int r = 0;
|
||||
r += benchmark_type<depth, e_int>("e_int", iteration_count);
|
||||
r += benchmark_type<depth, e_system_error>("e_system_error", iteration_count);
|
||||
r += benchmark_type<depth, e_heavy_payload>("e_heavy_payload", iteration_count);
|
||||
std::cout << '\n';
|
||||
// std::cout << std::rand() << '\n';
|
||||
return r;
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user