-
Notifications
You must be signed in to change notification settings - Fork 115
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Add but not build local perf test for discovery
- Loading branch information
1 parent
bd0cc13
commit ff824a2
Showing
1 changed file
with
346 additions
and
0 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,346 @@ | ||
|
||
#include <boost/core/lightweight_test.hpp> | ||
#include <boost/math/special_functions/bessel.hpp> | ||
#include <boost/multiprecision/cpp_double_fp.hpp> | ||
#include <boost/multiprecision/cpp_dec_float.hpp> | ||
#if defined(BOOST_HAS_FLOAT128) | ||
#include <boost/multiprecision/float128.hpp> | ||
#endif | ||
|
||
#include <charconv> | ||
#include <cstdint> | ||
#include <ctime> | ||
#include <limits> | ||
#include <iomanip> | ||
#include <iostream> | ||
#include <random> | ||
#include <sstream> | ||
#include <vector> | ||
|
||
#if defined(_MSC_VER) && !defined(__GNUC__) | ||
#define STOPWATCH_NODISCARD | ||
#else | ||
#if (defined(__cplusplus) && (__cplusplus >= 201703L)) | ||
#define STOPWATCH_NODISCARD [[nodiscard]] // NOLINT(cppcoreguidelines-macro-usage) | ||
#else | ||
#define STOPWATCH_NODISCARD | ||
#endif | ||
#endif | ||
|
||
namespace concurrency { | ||
|
||
// See also: https://godbolt.org/z/37a4n9f4Y | ||
|
||
struct stopwatch | ||
{ | ||
public: | ||
using time_point_type = std::uintmax_t; | ||
|
||
auto reset() -> void | ||
{ | ||
m_start = now(); | ||
} | ||
|
||
template<typename RepresentationRequestedTimeType> | ||
static auto elapsed_time(const stopwatch& my_stopwatch) noexcept -> RepresentationRequestedTimeType | ||
{ | ||
using local_time_type = RepresentationRequestedTimeType; | ||
|
||
return | ||
local_time_type | ||
{ | ||
static_cast<local_time_type>(my_stopwatch.elapsed()) | ||
/ local_time_type { UINTMAX_C(1000000000) } | ||
}; | ||
} | ||
|
||
STOPWATCH_NODISCARD static auto now() -> time_point_type | ||
{ | ||
#if defined(__CYGWIN__) | ||
|
||
return static_cast<time_point_type>(std::clock()); | ||
|
||
#else | ||
|
||
timespec ts { }; | ||
|
||
const int ntsp { timespec_get(&ts, TIME_UTC) }; | ||
|
||
static_cast<void>(ntsp); | ||
|
||
return | ||
static_cast<time_point_type> | ||
( | ||
static_cast<time_point_type>(static_cast<time_point_type>(ts.tv_sec) * UINTMAX_C(1000000000)) | ||
+ static_cast<time_point_type>(ts.tv_nsec) | ||
); | ||
|
||
#endif | ||
} | ||
|
||
private: | ||
time_point_type m_start { now() }; // NOLINT(readability-identifier-naming) | ||
|
||
STOPWATCH_NODISCARD auto elapsed() const -> time_point_type | ||
{ | ||
const time_point_type stop { now() }; | ||
|
||
#if defined(__CYGWIN__) | ||
|
||
const time_point_type | ||
elapsed_ns | ||
{ | ||
static_cast<time_point_type> | ||
( | ||
static_cast<time_point_type>(static_cast<time_point_type>(stop - m_start) * UINTMAX_C(1000000000)) | ||
/ static_cast<time_point_type>(CLOCKS_PER_SEC) | ||
) | ||
}; | ||
|
||
#else | ||
|
||
const time_point_type | ||
elapsed_ns | ||
{ | ||
static_cast<time_point_type> | ||
( | ||
stop - m_start | ||
) | ||
}; | ||
|
||
#endif | ||
|
||
return elapsed_ns; | ||
} | ||
}; | ||
|
||
} // namespace concurrency | ||
|
||
namespace util { | ||
|
||
template<typename UnsignedIntegerType, | ||
const std::uint_fast8_t BaseRepresentation = static_cast<std::uint_fast8_t>(UINT8_C(10)), | ||
const bool UpperCase = true> | ||
auto baselexical_cast(const UnsignedIntegerType& u, char* first, char* last) -> const char* | ||
{ | ||
constexpr auto my_base = static_cast<int>(BaseRepresentation); | ||
|
||
const auto result = std::to_chars(first, last, u, my_base); | ||
|
||
return result.ptr; | ||
} | ||
|
||
template<typename UnsignedIntegralType> | ||
auto util_pseudorandom_time_point_seed() -> UnsignedIntegralType | ||
{ | ||
using stopwatch_type = concurrency::stopwatch; | ||
|
||
return static_cast<UnsignedIntegralType>(stopwatch_type::now()); | ||
} | ||
|
||
} // namespace util | ||
|
||
std::uniform_int_distribution<std::uint32_t> dist_sgn(UINT32_C( 0), UINT32_C( 1)); // NOLINT(cert-err58-cpp,cppcoreguidelines-avoid-non-const-global-variables) | ||
std::uniform_int_distribution<std::uint32_t> dist_dig(UINT32_C(0x31), UINT32_C( 0x39)); // NOLINT(cert-err58-cpp,cppcoreguidelines-avoid-non-const-global-variables) | ||
|
||
using eng_sgn_type = std::ranlux24; | ||
using eng_dig_type = std::minstd_rand0; | ||
using eng_exp_type = std::mt19937; | ||
|
||
eng_sgn_type eng_sgn; // NOLINT(cert-msc32-c,cert-msc51-cpp,cert-err58-cpp,cppcoreguidelines-avoid-non-const-global-variables) | ||
eng_dig_type eng_dig; // NOLINT(cert-msc32-c,cert-msc51-cpp,cert-err58-cpp,cppcoreguidelines-avoid-non-const-global-variables) | ||
|
||
template<typename FloatingPointTypeWithStringConstruction> | ||
auto generate_wide_decimal_value(bool is_positive = false, | ||
int digits10_to_get = std::numeric_limits<FloatingPointTypeWithStringConstruction>::digits10 - 2) -> FloatingPointTypeWithStringConstruction | ||
{ | ||
using local_floating_point_type = FloatingPointTypeWithStringConstruction; | ||
|
||
static_assert(std::numeric_limits<local_floating_point_type>::digits10 > static_cast<int>(INT8_C(9)), | ||
"Error: Floating-point type destination does not have enough digits10"); | ||
|
||
std::string str_x(static_cast<std::size_t>(digits10_to_get), '0'); | ||
|
||
std::generate(str_x.begin(), | ||
str_x.end(), | ||
[]() // NOLINT(modernize-use-trailing-return-type,-warnings-as-errors) | ||
{ | ||
return static_cast<char>(dist_dig(eng_dig)); | ||
}); | ||
|
||
// Insert a decimal point. | ||
str_x.insert(static_cast<std::size_t>(UINT8_C(1)), static_cast<std::size_t>(UINT8_C(1)), '.'); | ||
|
||
// Insert either a positive sign or a negative sign | ||
// (always one or the other) depending on the sign of x. | ||
const auto sign_char_to_insert = | ||
static_cast<char> | ||
( | ||
is_positive | ||
? '+' | ||
: static_cast<char>((dist_sgn(eng_sgn) != static_cast<std::uint32_t>(UINT8_C(0))) ? '+' : '-') | ||
); | ||
|
||
str_x.insert(static_cast<std::size_t>(UINT8_C(0)), static_cast<std::size_t>(UINT8_C(1)), sign_char_to_insert); | ||
|
||
return local_floating_point_type(str_x.c_str()); | ||
} | ||
|
||
template<typename NumericType> | ||
auto is_close_fraction(const NumericType& a, | ||
const NumericType& b, | ||
const NumericType& tol) noexcept -> bool | ||
{ | ||
using std::fabs; | ||
|
||
auto result_is_ok = bool { }; | ||
|
||
if(b == static_cast<NumericType>(0)) | ||
{ | ||
result_is_ok = (fabs(a - b) < tol); // LCOV_EXCL_LINE | ||
} | ||
else | ||
{ | ||
const auto delta = fabs(1 - (a / b)); | ||
|
||
result_is_ok = (delta < tol); | ||
} | ||
|
||
return result_is_ok; | ||
} | ||
|
||
auto main() -> int | ||
{ | ||
using dbl_float_type = boost::multiprecision::cpp_double_double; | ||
using dec_float_type = boost::multiprecision::number<boost::multiprecision::cpp_dec_float<31>, boost::multiprecision::et_off>; | ||
#if defined(BOOST_HAS_FLOAT128) | ||
using flt_float_type = boost::multiprecision::float128; | ||
#endif | ||
|
||
constexpr std::size_t trials { UINT32_C(100000) }; | ||
|
||
std::vector<dbl_float_type> dbl_float_a_vec (trials); | ||
std::vector<dbl_float_type> dbl_float_b_vec (dbl_float_a_vec.size()); | ||
|
||
std::vector<dec_float_type> dec_float_a_vec (dbl_float_a_vec.size()); | ||
std::vector<dec_float_type> dec_float_b_vec (dbl_float_a_vec.size()); | ||
#if defined(BOOST_HAS_FLOAT128) | ||
std::vector<flt_float_type> flt_float_a_vec (dbl_float_a_vec.size()); | ||
std::vector<flt_float_type> flt_float_b_vec (dbl_float_a_vec.size()); | ||
#endif | ||
|
||
for(std::size_t index { UINT8_C(0) }; index < dbl_float_a_vec.size(); ++index) | ||
{ | ||
if(std::size_t { index % unsigned { UINT32_C(0x800) } } == std::size_t { UINT8_C(0) }) | ||
{ | ||
eng_sgn.seed(util::util_pseudorandom_time_point_seed<typename eng_sgn_type::result_type>()); | ||
eng_dig.seed(util::util_pseudorandom_time_point_seed<typename eng_dig_type::result_type>()); | ||
} | ||
|
||
auto | ||
gen | ||
{ | ||
[](bool is_positive, const dbl_float_type& gate = 0 ) | ||
{ | ||
dbl_float_type val { }; | ||
|
||
if(is_positive && gate != 0) | ||
{ | ||
while((val = generate_wide_decimal_value<dbl_float_type>(true)) < gate) { ; } | ||
} | ||
else | ||
{ | ||
val = generate_wide_decimal_value<dbl_float_type>(true); | ||
} | ||
|
||
return val; | ||
} | ||
}; | ||
|
||
dbl_float_a_vec[index] = dbl_float_type { gen(true, dbl_float_type { 9 } / 8) }; | ||
dbl_float_b_vec[index] = dbl_float_type { gen(true, dbl_float_type { 9 } / 8) }; | ||
|
||
dec_float_a_vec[index] = dec_float_type { dbl_float_a_vec[index] }; | ||
dec_float_b_vec[index] = dec_float_type { dbl_float_b_vec[index] }; | ||
|
||
#if defined(BOOST_HAS_FLOAT128) | ||
flt_float_a_vec[index] = flt_float_type { dbl_float_a_vec[index] }; | ||
flt_float_b_vec[index] = flt_float_type { dbl_float_b_vec[index] }; | ||
#endif | ||
} | ||
|
||
std::vector<dbl_float_type> dbl_float_c_vec (dbl_float_a_vec.size()); | ||
std::vector<dec_float_type> dec_float_c_vec (dbl_float_a_vec.size()); | ||
#if defined(BOOST_HAS_FLOAT128) | ||
std::vector<flt_float_type> flt_float_c_vec (dbl_float_a_vec.size()); | ||
#endif | ||
|
||
using stopwatch_type = concurrency::stopwatch; | ||
|
||
stopwatch_type my_stopwatch { }; | ||
|
||
for(std::size_t count { UINT8_C(0) }; count < trials; ++count) | ||
{ | ||
dbl_float_c_vec[count] = pow(dbl_float_a_vec[count], dbl_float_b_vec[count]); | ||
} | ||
|
||
const double elapsed_dbl { stopwatch_type::elapsed_time<double>(my_stopwatch) }; | ||
|
||
my_stopwatch.reset(); | ||
|
||
for(std::size_t count { UINT8_C(0) }; count < trials; ++count) | ||
{ | ||
dec_float_c_vec[count] = pow(dec_float_a_vec[count], dec_float_b_vec[count]); | ||
} | ||
|
||
const double elapsed_dec { stopwatch_type::elapsed_time<double>(my_stopwatch) }; | ||
|
||
#if defined(BOOST_HAS_FLOAT128) | ||
my_stopwatch.reset(); | ||
|
||
for(std::size_t count { UINT8_C(0) }; count < trials; ++count) | ||
{ | ||
flt_float_c_vec[count] = pow(flt_float_a_vec[count], flt_float_b_vec[count]); | ||
} | ||
|
||
const double elapsed_flt { stopwatch_type::elapsed_time<double>(my_stopwatch) }; | ||
#endif | ||
|
||
std::stringstream strm { }; | ||
|
||
strm << std::fixed << std::setprecision(3) << "elapsed_dbl : " << elapsed_dbl << "s\n" | ||
<< std::fixed << std::setprecision(3) << "elapsed_dec : " << elapsed_dec << "s\n" | ||
#if defined(BOOST_HAS_FLOAT128) | ||
<< std::fixed << std::setprecision(3) << "elapsed_flt : " << elapsed_flt << "s\n" | ||
#endif | ||
<< std::fixed << std::setprecision(3) << "ratio (dec/dbl) : " << elapsed_dec / elapsed_dbl << "\n" | ||
#if defined(BOOST_HAS_FLOAT128) | ||
<< std::fixed << std::setprecision(3) << "ratio (flt/dbl) : " << elapsed_flt / elapsed_dbl << "\n" | ||
#endif | ||
; | ||
|
||
std::cout << strm.str() << std::endl; | ||
|
||
std::cout << "verifying results...\n"; | ||
|
||
std::size_t count { UINT8_C(0) }; | ||
|
||
constexpr dbl_float_type tol_dbl { std::numeric_limits<dbl_float_type>::epsilon() * 1024 }; | ||
|
||
for(const auto& lhs : dbl_float_c_vec) | ||
{ | ||
const dbl_float_type ctrl_dec { dec_float_c_vec[count] }; | ||
|
||
BOOST_TEST(is_close_fraction(lhs, ctrl_dec, tol_dbl)); | ||
|
||
#if defined(BOOST_HAS_FLOAT128) | ||
const dbl_float_type ctrl_flt { flt_float_c_vec[count] }; | ||
|
||
BOOST_TEST(is_close_fraction(lhs, ctrl_flt, tol_dbl)); | ||
#endif | ||
|
||
++count; | ||
} | ||
|
||
return boost::report_errors(); | ||
} |