diff --git a/include/nonius/chronometer.h++ b/include/nonius/chronometer.h++ index d5cc4b8..a72f73d 100644 --- a/include/nonius/chronometer.h++ +++ b/include/nonius/chronometer.h++ @@ -18,7 +18,6 @@ #include #include #include -#include namespace nonius { namespace detail { diff --git a/include/nonius/configuration.h++ b/include/nonius/configuration.h++ index c5930b7..9e1ac64 100644 --- a/include/nonius/configuration.h++ +++ b/include/nonius/configuration.h++ @@ -15,7 +15,7 @@ #define NONIUS_CONFIGURATION_HPP #include -#include +#include #include #include @@ -30,7 +30,7 @@ namespace nonius { struct param_configuration { parameters map; - boost::optional run; + optional run; }; struct configuration { diff --git a/include/nonius/detail/benchmark_function.h++ b/include/nonius/detail/benchmark_function.h++ index 0e9ab36..4572bd3 100644 --- a/include/nonius/detail/benchmark_function.h++ +++ b/include/nonius/detail/benchmark_function.h++ @@ -18,6 +18,7 @@ #include #include +#include #include #include #include diff --git a/include/nonius/detail/cpptempl.h b/include/nonius/detail/cpptempl.h index a929af7..1a29e99 100644 --- a/include/nonius/detail/cpptempl.h +++ b/include/nonius/detail/cpptempl.h @@ -66,14 +66,13 @@ #include #include #include -#include #include #include -#include #include +#include namespace cpptempl { @@ -123,7 +122,10 @@ namespace cpptempl template<> void data_ptr::operator = (const data_map& data); template void data_ptr::operator = (const T& data) { - std::string data_str = boost::lexical_cast(data); + std::stringstream ss; + ss << data; + ss.exceptions(std::ios::failbit); + std::string data_str = ss.str(); this->operator =(data_str); } @@ -384,7 +386,7 @@ namespace cpptempl // quoted string if (key[0] == '\"') { - return make_data(boost::trim_copy_if(key, [](char c){ return c == '"'; })); + return make_data(nonius::trim_copy_if(key, [](char c){ return c == '"'; })); } // check for dotted notation, i.e [foo.bar] size_t index = key.find(".") ; @@ -447,7 +449,7 @@ namespace cpptempl inline TokenFor::TokenFor(std::string expr) { std::vector elements ; - boost::split(elements, expr, boost::is_space()) ; + nonius::split(elements, expr, nonius::is_space()) ; if (elements.size() != 4u) { throw TemplateException("Invalid syntax in for statement") ; @@ -468,8 +470,8 @@ namespace cpptempl for (size_t i = 0 ; i < items.size() ; ++i) { data_map loop ; - loop["index"] = make_data(boost::lexical_cast(i+1)) ; - loop["index0"] = make_data(boost::lexical_cast(i)) ; + loop["index"] = make_data(std::to_string(i+1)) ; + loop["index0"] = make_data(std::to_string(i)) ; data["loop"] = make_data(loop); data[m_val] = items[i] ; for(size_t j = 0 ; j < m_children.size() ; ++j) @@ -509,7 +511,7 @@ namespace cpptempl inline bool TokenIf::is_true( std::string expr, data_map &data ) { std::vector elements ; - boost::split(elements, expr, boost::is_space()) ; + nonius::split(elements, expr, nonius::is_space()) ; if (elements[1] == "not") { @@ -634,19 +636,19 @@ namespace cpptempl pos = text.find("}") ; if (pos != std::string::npos) { - std::string expression = boost::trim_copy(text.substr(1, pos-2)) ; + std::string expression = nonius::trim_copy(text.substr(1, pos-2)) ; text = text.substr(pos+1) ; - if (boost::starts_with(expression, "for")) + if (nonius::starts_with(expression, "for")) { tokens.push_back(token_ptr (new TokenFor(expression))) ; } - else if (boost::starts_with(expression, "if")) + else if (nonius::starts_with(expression, "if")) { tokens.push_back(token_ptr (new TokenIf(expression))) ; } else { - tokens.push_back(token_ptr (new TokenEnd(boost::trim_copy(expression)))) ; + tokens.push_back(token_ptr (new TokenEnd(nonius::trim_copy(expression)))) ; } } } diff --git a/include/nonius/detail/optional.h++ b/include/nonius/detail/optional.h++ new file mode 100644 index 0000000..5dc7be8 --- /dev/null +++ b/include/nonius/detail/optional.h++ @@ -0,0 +1,132 @@ +// Nonius - C++ benchmarking tool +// +// Written in 2014- by the nonius contributors +// +// To the extent possible under law, the author(s) have dedicated all copyright and related +// and neighboring rights to this software to the public domain worldwide. This software is +// distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication along with this software. +// If not, see + +// Simple optional type implementation partially based on C++17 interface + +#ifndef NONIUS_OPTIONAL_HPP +#define NONIUS_OPTIONAL_HPP + +#include +#include + +namespace nonius { + struct nullopt_t { + constexpr nullopt_t(int) {} + }; + constexpr nullopt_t nullopt() { return {0}; } + + template + class optional { + typename std::aligned_storage::type storage; + bool initialized; + + T& get_impl() { + assert(initialized); + return *reinterpret_cast(&storage); + } + T const& get_impl() const { + assert(initialized); + return *reinterpret_cast(&storage); + } + + void construct(T const& value) { + assert(!initialized); + new (&storage) T(value); + initialized = true; + } + void construct(T&& value) { + assert(!initialized); + new (&storage) T(std::move(value)); + initialized = true; + } + void destroy() { + if (initialized) { + get_impl().~T(); + initialized = false; + } + } + optional& assign(T const& value) { + destroy(); + construct(value); + return *this; + } + optional& assign(T&& value) { + destroy(); + construct(std::move(value)); + return *this; + } + public: + constexpr optional() : initialized(false) {} + constexpr optional(nullopt_t) : initialized(false) {} + optional(optional const& rhs) : initialized(false) { + if (rhs.initialized) { + construct(rhs.get_impl()); + } + } + optional(optional&& rhs) : initialized(false) { + if (rhs.initialized) { + construct(std::move(rhs.get_impl())); + } + } + optional(T const& value) : initialized(false) { + construct(value); + } + optional(T&& value) : initialized(false) { + construct(std::move(value)); + } + ~optional() { + destroy(); + } + + optional& operator=(optional const& rhs) { + destroy(); + if (rhs.initialized) { + construct(rhs.get_impl()); + } + return *this; + } + optional& operator=(optional && rhs) { + destroy(); + if (rhs.initialized) { + construct(std::move(rhs.get_impl())); + } + return *this; + } + optional& operator=(T const& rhs) { + destroy(); + construct(rhs); + return *this; + } + optional& operator=(T&& rhs) { + destroy(); + construct(std::move(rhs)); + return *this; + } + optional& operator=(nullopt_t) { + destroy(); + return *this; + } + + constexpr T const* operator->() const { return &get_impl(); } + T* operator->() { return &get_impl(); } + + constexpr T const& operator*() const { return get_impl(); } + T& operator*() { return get_impl(); } + + constexpr bool has_value() const { return initialized; } + constexpr explicit operator bool() const { return initialized; } + + constexpr bool operator!() const { return !initialized; } + }; +} // namespace nonius + +#endif // NONIUS_OPTIONAL_HPP + diff --git a/include/nonius/detail/stats.h++ b/include/nonius/detail/stats.h++ index e74a874..c84d017 100644 --- a/include/nonius/detail/stats.h++ +++ b/include/nonius/detail/stats.h++ @@ -18,9 +18,8 @@ #include #include -#include - #include +#include #include #include #include @@ -124,10 +123,109 @@ namespace nonius { return results; } + inline double normal_cdf(double x) { + return std::erfc(-x / std::sqrt(2.0)) / 2.0; + } + + inline double erf_inv(double x) { + // Code accompanying the article "Approximating the erfinv function" in GPU Computing Gems, Volume 2 + double w, p; + + w = - log((1.0-x)*(1.0+x)); + + if (w < 6.250000) { + w = w - 3.125000; + p = -3.6444120640178196996e-21; + p = -1.685059138182016589e-19 + p*w; + p = 1.2858480715256400167e-18 + p*w; + p = 1.115787767802518096e-17 + p*w; + p = -1.333171662854620906e-16 + p*w; + p = 2.0972767875968561637e-17 + p*w; + p = 6.6376381343583238325e-15 + p*w; + p = -4.0545662729752068639e-14 + p*w; + p = -8.1519341976054721522e-14 + p*w; + p = 2.6335093153082322977e-12 + p*w; + p = -1.2975133253453532498e-11 + p*w; + p = -5.4154120542946279317e-11 + p*w; + p = 1.051212273321532285e-09 + p*w; + p = -4.1126339803469836976e-09 + p*w; + p = -2.9070369957882005086e-08 + p*w; + p = 4.2347877827932403518e-07 + p*w; + p = -1.3654692000834678645e-06 + p*w; + p = -1.3882523362786468719e-05 + p*w; + p = 0.0001867342080340571352 + p*w; + p = -0.00074070253416626697512 + p*w; + p = -0.0060336708714301490533 + p*w; + p = 0.24015818242558961693 + p*w; + p = 1.6536545626831027356 + p*w; + } + else if (w < 16.000000) { + w = sqrt(w) - 3.250000; + p = 2.2137376921775787049e-09; + p = 9.0756561938885390979e-08 + p*w; + p = -2.7517406297064545428e-07 + p*w; + p = 1.8239629214389227755e-08 + p*w; + p = 1.5027403968909827627e-06 + p*w; + p = -4.013867526981545969e-06 + p*w; + p = 2.9234449089955446044e-06 + p*w; + p = 1.2475304481671778723e-05 + p*w; + p = -4.7318229009055733981e-05 + p*w; + p = 6.8284851459573175448e-05 + p*w; + p = 2.4031110387097893999e-05 + p*w; + p = -0.0003550375203628474796 + p*w; + p = 0.00095328937973738049703 + p*w; + p = -0.0016882755560235047313 + p*w; + p = 0.0024914420961078508066 + p*w; + p = -0.0037512085075692412107 + p*w; + p = 0.005370914553590063617 + p*w; + p = 1.0052589676941592334 + p*w; + p = 3.0838856104922207635 + p*w; + } + else { + w = sqrt(w) - 5.000000; + p = -2.7109920616438573243e-11; + p = -2.5556418169965252055e-10 + p*w; + p = 1.5076572693500548083e-09 + p*w; + p = -3.7894654401267369937e-09 + p*w; + p = 7.6157012080783393804e-09 + p*w; + p = -1.4960026627149240478e-08 + p*w; + p = 2.9147953450901080826e-08 + p*w; + p = -6.7711997758452339498e-08 + p*w; + p = 2.2900482228026654717e-07 + p*w; + p = -9.9298272942317002539e-07 + p*w; + p = 4.5260625972231537039e-06 + p*w; + p = -1.9681778105531670567e-05 + p*w; + p = 7.5995277030017761139e-05 + p*w; + p = -0.00021503011930044477347 + p*w; + p = -0.00013871931833623122026 + p*w; + p = 1.0103004648645343977 + p*w; + p = 4.8499064014085844221 + p*w; + } + return p*x; + } + + inline double erfc_inv(double x) { + return erf_inv(1.0 - x); + } + + inline double normal_quantile(double p) { + static const double ROOT_TWO = std::sqrt(2.0); + + double result = 0.0; + assert(p >= 0 && p <= 1); + if (p < 0 || p > 1) { + return result; + } + + result = -erfc_inv(2.0 * p); + // result *= normal distribution standard deviation (1.0) * sqrt(2) + result *= /*sd * */ ROOT_TWO; + // result += normal disttribution mean (0) + return result; + } + template estimate bootstrap(double confidence_level, Iterator first, Iterator last, sample const& resample, Estimator&& estimator) { - namespace bm = boost::math; - auto n_samples = last - first; double point = estimator(first, last); @@ -150,10 +248,11 @@ namespace nonius { // degenerate case with uniform samples if(prob_n == 0) return { point, point, point, confidence_level }; - double bias = bm::quantile(bm::normal{}, prob_n); - double z1 = bm::quantile(bm::normal{}, (1. - confidence_level) / 2.); + double bias = normal_quantile(prob_n); + double z1 = normal_quantile((1. - confidence_level) / 2.); - auto cumn = [n](double x) -> int { return std::lround(bm::cdf(bm::normal{}, x) * n); }; + auto cumn = [n](double x) -> int { + return std::lround(normal_cdf(x) * n); }; auto a = [bias, accel](double b) { return bias + b / (1. - accel * b); }; double b1 = bias + z1; double b2 = bias - z1; diff --git a/include/nonius/detail/string_utils.h++ b/include/nonius/detail/string_utils.h++ new file mode 100644 index 0000000..4116f47 --- /dev/null +++ b/include/nonius/detail/string_utils.h++ @@ -0,0 +1,85 @@ +// Nonius - C++ benchmarking tool +// +// Written in 2014- by the nonius contributors +// +// To the extent possible under law, the author(s) have dedicated all copyright and related +// and neighboring rights to this software to the public domain worldwide. This software is +// distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication along with this software. +// If not, see + +// String processing utilities + +#ifndef NONIUS_STRING_UTILS_HPP +#define NONIUS_STRING_UTILS_HPP + +#include +#include +#include +#include + +namespace nonius { + namespace detail { + struct is_spaceF { + bool operator()(const char c) const { + return std::isspace(c) != 0; + } + }; + + struct is_any_ofF { + std::string chars; + is_any_ofF(std::string chars) : chars(chars) {} + bool operator()(const char c) const { + return chars.find_first_of(c) != std::string::npos; + } + }; + } // namespace detail + + detail::is_spaceF is_space() { return detail::is_spaceF {}; } + + detail::is_any_ofF is_any_of(const char* chars) { return detail::is_any_ofF{chars}; } + + bool starts_with(std::string const& input, std::string const& test) { + if (test.size() <= input.size()) { + return std::equal(test.begin(), test.end(), input.begin()); + } + return false; + } + + template + std::string trim_copy_if(std::string input, PredicateT predicate) { + const auto begin = std::find_if_not(input.begin(), input.end(), predicate); + const auto end = std::find_if_not(input.rbegin(), input.rend(), predicate).base(); + return std::string(begin, end); + } + + std::string trim_copy(std::string const& input) { + return trim_copy_if(input, is_space()); + } + + template + std::vector& split(std::vector& result, std::string const& input, PredicateT predicate) { + std::vector tmp; + + const auto end = input.end(); + auto itr = input.begin(); + + for (;;) { + auto sep = std::find_if(itr, end, predicate); + if (sep == end) { + break; + } + tmp.emplace_back(itr, sep); + itr = std::find_if_not(sep, end, predicate); + } + tmp.emplace_back(itr, end); + std::swap(result, tmp); + + return result; + } + +} // namespace nonius + +#endif // NONIUS_STRING_UTILS_HPP + diff --git a/include/nonius/execution_plan.h++ b/include/nonius/execution_plan.h++ index 5c24197..f29c2e3 100644 --- a/include/nonius/execution_plan.h++ +++ b/include/nonius/execution_plan.h++ @@ -21,6 +21,8 @@ #include #include +#include + namespace nonius { template struct execution_plan { diff --git a/include/nonius/main.h++ b/include/nonius/main.h++ index ec4d4f8..3984f35 100644 --- a/include/nonius/main.h++ +++ b/include/nonius/main.h++ @@ -16,8 +16,7 @@ #include #include - -#include +#include #include #include @@ -51,7 +50,7 @@ namespace nonius { struct parser { static param_configuration parse(std::string const& param) { auto v = std::vector{}; - boost::split(v, param, boost::is_any_of(":")); + split(v, param, is_any_of(":")); try { if (v.size() > 0) { auto name = v[0]; @@ -62,12 +61,14 @@ namespace nonius { auto oper = v[1]; auto init = def.parse(v[2]); auto step = def.parse(v[3]); - auto count = boost::lexical_cast(v[4]); + std::stringstream ss(v[4]); + auto count = size_t(); + ss >> count; + ss.exceptions(std::ios::failbit); return {{}, run_configuration{name, oper, init, step, count}}; } } } - catch (boost::bad_lexical_cast const&) {} catch (std::out_of_range const&) {} return {}; } diff --git a/include/nonius/param.h++ b/include/nonius/param.h++ index b0d7bf3..540c8b5 100644 --- a/include/nonius/param.h++ +++ b/include/nonius/param.h++ @@ -16,10 +16,10 @@ #include #include -#include #include #include #include +#include namespace nonius { diff --git a/test/stats.c++ b/test/stats.c++ index 69020a7..fb0501a 100644 --- a/test/stats.c++ +++ b/test/stats.c++ @@ -17,6 +17,30 @@ #include +TEST_CASE("normal_cdf") { + using nonius::detail::normal_cdf; + CHECK(normal_cdf(0.000000) == Approx(0.50000000000000000)); + CHECK(normal_cdf(1.000000) == Approx(0.84134474606854293)); + CHECK(normal_cdf(-1.000000) == Approx(0.15865525393145705)); + CHECK(normal_cdf(2.809729) == Approx(0.99752083845315409)); + CHECK(normal_cdf(-1.352570) == Approx(0.08809652095066035)); +} + +TEST_CASE("erfc_inv") { + using nonius::detail::erfc_inv; + CHECK(erfc_inv(1.103560) == Approx(-0.09203687623843015)); + CHECK(erfc_inv(1.067400) == Approx(-0.05980291115763361)); + CHECK(erfc_inv(0.050000) == Approx(1.38590382434967796)); +} + +TEST_CASE("normal_quantile") { + using nonius::detail::normal_quantile; + CHECK(normal_quantile(0.551780) == Approx(0.13015979861484198)); + CHECK(normal_quantile(0.533700) == Approx(0.08457408802851875)); + CHECK(normal_quantile(0.025000) == Approx(-1.95996398454005449)); +} + + TEST_CASE("mean") { std::vector x { 10., 20., 14., 16., 30., 24. }; diff --git a/test/string_utils.c++ b/test/string_utils.c++ new file mode 100644 index 0000000..8987c87 --- /dev/null +++ b/test/string_utils.c++ @@ -0,0 +1,83 @@ +// Nonius - C++ benchmarking tool +// +// Written in 2014- by the nonius contributors +// +// To the extent possible under law, the author(s) have dedicated all copyright and related +// and neighboring rights to this software to the public domain worldwide. This software is +// distributed without any warranty. +// +// You should have received a copy of the CC0 Public Domain Dedication along with this software. +// If not, see + +// Tests for statistical stuff + +#include + +#include + +#include + +TEST_CASE("is_space") { + std::string spaces = "a b\tc"; + std::string nospace = "ab"; + REQUIRE(std::count_if(spaces.begin(), spaces.end(), nonius::is_space()) == 2); + REQUIRE(std::count_if(nospace.begin(), nospace.end(), nonius::is_space()) == 0); +} + +TEST_CASE("is_any_of") { + std::string str = "a:b;c:d"; + REQUIRE(std::count_if(str.begin(), str.end(), nonius::is_any_of("")) == 0); + REQUIRE(std::count_if(str.begin(), str.end(), nonius::is_any_of(":")) == 2); + REQUIRE(std::count_if(str.begin(), str.end(), nonius::is_any_of(":;")) == 3); +} + +TEST_CASE("starts_with") { + REQUIRE(nonius::starts_with("abc", "ab")); + REQUIRE_FALSE(nonius::starts_with("abc", "bc")); + REQUIRE_FALSE(nonius::starts_with("ab", "abc")); +} + +TEST_CASE("trim_copy") { + REQUIRE(nonius::trim_copy("") == std::string("")); + REQUIRE(nonius::trim_copy("abc") == std::string("abc")); + REQUIRE(nonius::trim_copy(" abc") == std::string("abc")); + REQUIRE(nonius::trim_copy("abc ") == std::string("abc")); + REQUIRE(nonius::trim_copy(" abc ") == std::string("abc")); +} + +TEST_CASE("split") { + const std::string str1("xx abc xx abb"); + const std::string str2("Xx abc xX abb xx"); + const std::string str3("xx"); + const std::string strempty(""); + std::vector result; + + nonius::split(result, str2, nonius::is_any_of("xX")); + REQUIRE(result.size() == 4); + CHECK(result[0] == std::string("")); + CHECK(result[1] == std::string(" abc ")); + CHECK(result[2] == std::string(" abb ")); + CHECK(result[3] == std::string("")); + + nonius::split(result, str1, nonius::is_any_of("x")); + REQUIRE(result.size() == 3); + CHECK(result[0] == std::string("")); + CHECK(result[1] == std::string(" abc ")); + CHECK(result[2] == std::string(" abb")); + + nonius::split(result, str1, nonius::is_space()); + REQUIRE(result.size() == 4); + CHECK(result[0] == std::string("xx")); + CHECK(result[1] == std::string("abc")); + CHECK(result[2] == std::string("xx")); + CHECK(result[3] == std::string("abb")); + + nonius::split(result, str3, nonius::is_any_of(",")); + REQUIRE(result.size() == 1); + CHECK(result[0] == std::string("xx")); + + nonius::split(result, strempty, nonius::is_space()); + REQUIRE(result.size() == 1); + CHECK(result[0] == std::string("")); +} +