30#ifndef ANKERL_NANOBENCH_H_INCLUDED
31#define ANKERL_NANOBENCH_H_INCLUDED
34#define ANKERL_NANOBENCH_VERSION_MAJOR 4
35#define ANKERL_NANOBENCH_VERSION_MINOR 3
36#define ANKERL_NANOBENCH_VERSION_PATCH 11
55#include <unordered_map>
59#define ANKERL_NANOBENCH(x) ANKERL_NANOBENCH_PRIVATE_##x()
61#define ANKERL_NANOBENCH_PRIVATE_CXX() __cplusplus
62#define ANKERL_NANOBENCH_PRIVATE_CXX98() 199711L
63#define ANKERL_NANOBENCH_PRIVATE_CXX11() 201103L
64#define ANKERL_NANOBENCH_PRIVATE_CXX14() 201402L
65#define ANKERL_NANOBENCH_PRIVATE_CXX17() 201703L
67#if ANKERL_NANOBENCH(CXX) >= ANKERL_NANOBENCH(CXX17)
68# define ANKERL_NANOBENCH_PRIVATE_NODISCARD() [[nodiscard]]
70# define ANKERL_NANOBENCH_PRIVATE_NODISCARD()
74# define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_PUSH() \
75 _Pragma("clang diagnostic push") _Pragma("clang diagnostic ignored \"-Wpadded\"")
76# define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_POP() _Pragma("clang diagnostic pop")
78# define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_PUSH()
79# define ANKERL_NANOBENCH_PRIVATE_IGNORE_PADDED_POP()
83# define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_PUSH() _Pragma("GCC diagnostic push") _Pragma("GCC diagnostic ignored \"-Weffc++\"")
84# define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_POP() _Pragma("GCC diagnostic pop")
86# define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_PUSH()
87# define ANKERL_NANOBENCH_PRIVATE_IGNORE_EFFCPP_POP()
90#if defined(ANKERL_NANOBENCH_LOG_ENABLED)
92# define ANKERL_NANOBENCH_LOG(x) \
94 std::cout << __FUNCTION__ << "@" << __LINE__ << ": " << x << std::endl; \
97# define ANKERL_NANOBENCH_LOG(x) \
102#define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 0
103#if defined(__linux__) && !defined(ANKERL_NANOBENCH_DISABLE_PERF_COUNTERS)
104# include <linux/version.h>
105# if LINUX_VERSION_CODE >= KERNEL_VERSION(3, 3, 0)
108# undef ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS
109# define ANKERL_NANOBENCH_PRIVATE_PERF_COUNTERS() 1
113#if defined(__clang__)
114# define ANKERL_NANOBENCH_NO_SANITIZE(...) __attribute__((no_sanitize(__VA_ARGS__)))
116# define ANKERL_NANOBENCH_NO_SANITIZE(...)
120# define ANKERL_NANOBENCH_PRIVATE_NOINLINE() __declspec(noinline)
122# define ANKERL_NANOBENCH_PRIVATE_NOINLINE() __attribute__((noinline))
127#if defined(__GNUC__) && __GNUC__ < 5
128# define ANKERL_NANOBENCH_IS_TRIVIALLY_COPYABLE(...) __has_trivial_copy(__VA_ARGS__)
130# define ANKERL_NANOBENCH_IS_TRIVIALLY_COPYABLE(...) std::is_trivially_copyable<__VA_ARGS__>::value
135#define ANKERL_NANOBENCH_PRIVATE_NOEXCEPT_STRING_MOVE() std::is_nothrow_move_assignable<std::string>::value
147using Clock = std::conditional<std::chrono::high_resolution_clock::is_steady, std::chrono::high_resolution_clock,
148 std::chrono::steady_clock>::type;
156template <
typename SetupOp>
336char const*
csv() noexcept;
377class PerformanceCounters;
379#if ANKERL_NANOBENCH(PERF_COUNTERS)
410 std::string mBenchmarkTitle =
"benchmark";
411 std::string mBenchmarkName =
"noname";
412 std::string mUnit =
"op";
414 double mComplexityN = -1.0;
415 size_t mNumEpochs = 11;
416 size_t mClockResolutionMultiple =
static_cast<size_t>(1000);
417 std::chrono::nanoseconds mMaxEpochTime = std::chrono::milliseconds(100);
418 std::chrono::nanoseconds mMinEpochTime = std::chrono::milliseconds(1);
423 std::ostream* mOut =
nullptr;
424 std::chrono::duration<double> mTimeUnit = std::chrono::nanoseconds{1};
425 std::string mTimeUnitName =
"ns";
426 bool mShowPerformanceCounters =
true;
427 bool mIsRelative =
false;
428 std::unordered_map<std::string, std::string> mContext{};
489 std::vector<std::vector<double>> mNameToMeasurements{};
607 inline double uniform01() noexcept;
749 Bench& batch(
T b) noexcept;
954 Bench& complexityN(
T n) noexcept;
1014 BigO complexityBigO(
char const*
name, Op op) const;
1017 BigO complexityBigO(
std::
string const&
name, Op op) const;
1048 std::vector<Result> mResults{};
1058template <
typename Arg>
1063#if defined(_MSC_VER)
1066template <
typename T>
1074template <
typename T>
1077 asm volatile(
"" : :
"r,m"(val) :
"memory");
1080template <
typename T>
1082# if defined(__clang__)
1084 asm volatile(
"" :
"+r,m"(val) : :
"memory");
1087 asm volatile(
"" :
"+m,r"(val) : :
"memory");
1134#if ANKERL_NANOBENCH(PERF_COUNTERS)
1151 template <
typename Op>
1161 template <
typename Op>
1165 template <
typename Op>
1179 double mNormalizedRootMeanSquare{};
1190namespace nanobench {
1197 return (std::numeric_limits<uint64_t>::max)();
1204 mX =
UINT64_C(15241094284759029579) * mY;
1205 mY =
rotl(mY - x, 27);
1217double Rng::uniform01() noexcept {
1218 auto i = (
UINT64_C(0x3ff) << 52U) | (
operator()() >> 12U);
1222 std::memcpy(&d, &i,
sizeof(
double));
1226template <
typename Container>
1231 auto n = operator()();
1243 return (x <<
k) | (x >> (64U -
k));
1248template <
typename SetupOp>
1255 template <
typename Op>
1258 assert((mBench.epochIterations() <= 1) &&
1259 "setup() runs once per epoch, not once per iteration; it requires epochIterations(1)");
1260 mBench.epochIterations(1);
1261 return mBench.runImpl(mSetupOp, std::forward<Op>(op));
1270template <
typename Op>
1274 return runImpl(
setupOp, std::forward<Op>(op));
1277template <
typename SetupOp,
typename Op>
1282 auto&
pc = detail::performanceCounters();
1288 Clock::time_point
const before = Clock::now();
1292 Clock::time_point
const after = Clock::now();
1301template <
typename SetupOp>
1307template <
typename Op>
1310 return run(std::forward<Op>(op));
1313template <
typename Op>
1316 return run(std::forward<Op>(op));
1319template <
typename Op>
1324template <
typename Op>
1331template <
typename T>
1333 mConfig.mBatch =
static_cast<double>(b);
1338template <
typename T>
1340 mConfig.mComplexityN =
static_cast<double>(n);
1345template <
typename Arg>
1346Bench& Bench::doNotOptimizeAway(Arg&& arg) {
1347 detail::doNotOptimizeAway(std::forward<Arg>(arg));
1352template <
typename Arg>
1354 detail::doNotOptimizeAway(std::forward<Arg>(arg));
1359#if defined(_MSC_VER)
1360template <
typename T>
1361void doNotOptimizeAway(
T const& val) {
1371#if defined(ANKERL_NANOBENCH_IMPLEMENT)
1378# include <algorithm>
1383# include <functional>
1391# include <stdexcept>
1393# if defined(__linux__)
1396# if ANKERL_NANOBENCH(PERF_COUNTERS)
1399# include <linux/perf_event.h>
1400# include <sys/ioctl.h>
1401# include <sys/syscall.h>
1402# include <sys/types.h>
1413namespace nanobench {
1437namespace nanobench {
1444template <
typename T>
1445inline double d(
T t)
noexcept {
1446 return static_cast<double>(
t);
1448inline double d(Clock::duration duration)
noexcept {
1449 return std::chrono::duration_cast<std::chrono::duration<double>>(duration).
count();
1459char const*
csv() noexcept {
1460 return R
"DELIM("title";"name";"unit";"batch";"elapsed";"error %";"instructions";"branches";"branch misses";"total"
1461{{#result}}"{{title}}";"{{name}}";"{{unit}}";{{batch}};{{median(elapsed)}};{{medianAbsolutePercentError(elapsed)}};{{median(instructions)}};{{median(branchinstructions)}};{{median(branchmisses)}};{{sumProduct(iterations, elapsed)}}
1466 return R
"DELIM(<html>
1469 <script src="https://cdn.plot.ly/plotly-latest.min.js"></script>
1473 <div id="myDiv"></div>
1478 y: [{{#measurement}}{{elapsed}}{{^-last}}, {{/last}}{{/measurement}}],
1482 var title = '{{title}}';
1484 data = data.map(a => Object.assign(a, { boxpoints: 'all', pointpos: 0, type: 'box' }));
1485 var layout = { title: { text: title }, showlegend: false, yaxis: { title: 'time per unit', rangemode: 'tozero', autorange: true } }; Plotly.newPlot('myDiv', data, layout, {responsive: true});
1492char const*
pyperf() noexcept {
1499{{#measurement}} {{elapsed}}{{^-last}},
1500{{/last}}{{/measurement}}
1507 "loops": {{sum(iterations)}},
1508 "inner_loops": {{batch}},
1509 "name": "{{title}}",
1516char const*
json() noexcept {
1520 "title": "{{title}}",
1524 "complexityN": {{complexityN}},
1525 "epochs": {{epochs}},
1526 "clockResolution": {{clockResolution}},
1527 "clockResolutionMultiple": {{clockResolutionMultiple}},
1528 "maxEpochTime": {{maxEpochTime}},
1529 "minEpochTime": {{minEpochTime}},
1530 "minEpochIterations": {{minEpochIterations}},
1531 "epochIterations": {{epochIterations}},
1532 "warmup": {{warmup}},
1533 "relative": {{relative}},
1534 "median(elapsed)": {{median(elapsed)}},
1535 "medianAbsolutePercentError(elapsed)": {{medianAbsolutePercentError(elapsed)}},
1536 "median(instructions)": {{median(instructions)}},
1537 "medianAbsolutePercentError(instructions)": {{medianAbsolutePercentError(instructions)}},
1538 "median(cpucycles)": {{median(cpucycles)}},
1539 "median(contextswitches)": {{median(contextswitches)}},
1540 "median(pagefaults)": {{median(pagefaults)}},
1541 "median(branchinstructions)": {{median(branchinstructions)}},
1542 "median(branchmisses)": {{median(branchmisses)}},
1543 "totalTime": {{sumProduct(iterations, elapsed)}},
1546 "iterations": {{iterations}},
1547 "elapsed": {{elapsed}},
1548 "pagefaults": {{pagefaults}},
1549 "cpucycles": {{cpucycles}},
1550 "contextswitches": {{contextswitches}},
1551 "instructions": {{instructions}},
1552 "branchinstructions": {{branchinstructions}},
1553 "branchmisses": {{branchmisses}}
1554 }{{^-last}},{{/-last}}
1556 }{{^-last}},{{/-last}}
1567 std::vector<Node> children;
1572 bool operator==(
char const (&str)[
N])
const noexcept {
1574 return static_cast<size_t>(std::distance(begin, end) + 1) ==
N && 0 ==
strncmp(str, begin,
N - 1);
1581 std::vector<Node>
nodes;
1584 auto const* begin = std::strstr(*
tpl,
"{{");
1585 auto const* end = begin;
1586 if (begin !=
nullptr) {
1589 end = std::strstr(begin,
"}}");
1592 if (begin ==
nullptr || end ==
nullptr) {
1595 nodes.emplace_back(Node{*
tpl, *
tpl + std::strlen(*
tpl), std::vector<Node>{}, Node::Type::content});
1600 nodes.emplace_back(Node{*
tpl, begin - 2, std::vector<Node>{}, Node::Type::content});
1621 nodes.emplace_back(Node{begin, end, std::vector<Node>{}, Node::Type::tag});
1627static bool generateFirstLast(Node
const& n,
size_t idx,
size_t size, std::ostream& out) {
1636 if (n.type == Node::Type::section) {
1638 }
else if (n.type == Node::Type::inverted_section) {
1643 for (
auto const&
child : n.children) {
1644 if (
child.type == Node::Type::content) {
1656 if (
idxClose == std::string::npos) {
1665 if (str[i] ==
' ' || str[i] ==
'\t') {
1669 if (str[i] ==
',') {
1680static bool generateConfigTag(Node
const& n, Config
const& config, std::ostream& out) {
1684 out << config.mBenchmarkTitle;
1688 out << config.mBenchmarkName;
1692 out << config.mUnit;
1696 out << config.mBatch;
1699 if (n ==
"complexityN") {
1700 out << config.mComplexityN;
1703 if (n ==
"epochs") {
1704 out << config.mNumEpochs;
1707 if (n ==
"clockResolution") {
1708 out << d(detail::clockResolution());
1711 if (n ==
"clockResolutionMultiple") {
1712 out << config.mClockResolutionMultiple;
1715 if (n ==
"maxEpochTime") {
1716 out << d(config.mMaxEpochTime);
1719 if (n ==
"minEpochTime") {
1720 out << d(config.mMinEpochTime);
1723 if (n ==
"minEpochIterations") {
1724 out << config.mMinEpochIterations;
1727 if (n ==
"epochIterations") {
1728 out << config.mEpochIterations;
1731 if (n ==
"warmup") {
1732 out << config.mWarmup;
1735 if (n ==
"relative") {
1736 out << config.mIsRelative;
1743static std::ostream&
generateResultTag(Node
const& n, Result
const& r, std::ostream& out) {
1760 if (
m == Result::Measure::_size) {
1765 return out << r.median(
m);
1768 return out << r.average(
m);
1770 if (
matchResult[0] ==
"medianAbsolutePercentError") {
1771 return out << r.medianAbsolutePercentError(
m);
1774 return out << r.sum(
m);
1777 return out << r.minimum(
m);
1780 return out << r.maximum(
m);
1785 if (
m1 == Result::Measure::_size ||
m2 == Result::Measure::_size) {
1790 return out << r.sumProduct(
m1,
m2);
1799 throw std::runtime_error(
"command '" + std::string(n.begin, n.end) +
"' not understood");
1803 for (
auto const& n :
nodes) {
1807 case Node::Type::content:
1808 out.write(n.begin, std::distance(n.begin, n.end));
1811 case Node::Type::inverted_section:
1812 throw std::runtime_error(
"got a inverted section inside measurement");
1814 case Node::Type::section:
1815 throw std::runtime_error(
"got a section inside measurement");
1817 case Node::Type::tag: {
1818 auto m = Result::fromString(std::string(n.begin, n.end));
1819 if (
m == Result::Measure::_size || !r.has(
m)) {
1822 out << r.get(idx,
m);
1831static void generateResult(std::vector<Node>
const&
nodes,
size_t idx, std::vector<Result>
const&
results, std::ostream& out) {
1833 for (
auto const& n :
nodes) {
1837 case Node::Type::content:
1838 out.write(n.begin, std::distance(n.begin, n.end));
1841 case Node::Type::inverted_section:
1842 throw std::runtime_error(
"got a inverted section inside result");
1844 case Node::Type::section:
1845 if (n ==
"measurement") {
1846 for (
size_t i = 0; i < r.size(); ++i) {
1850 throw std::runtime_error(
"got a section inside result");
1854 case Node::Type::tag:
1871template <
typename T>
1872T parseFile(std::string
const& filename,
bool* fail);
1888class NumSep :
public std::numpunct<char> {
1919 std::streamsize
const mWidth;
1920 std::ostream::char_type
const mFill;
1934 std::ostream& write(std::ostream&
os)
const;
1969 std::ostream& write(std::ostream&
os)
const;
1971 std::string
mWhat{};
1984namespace nanobench {
1988 detail::fmt::StreamStateRestorer
const restorer(out);
1990 out.precision(std::numeric_limits<double>::digits10);
1993 for (
auto const& n :
nodes) {
1996 case templates::Node::Type::content:
1997 out.write(n.begin, std::distance(n.begin, n.end));
2000 case templates::Node::Type::inverted_section:
2001 throw std::runtime_error(
"unknown list '" + std::string(n.begin, n.end) +
"'");
2003 case templates::Node::Type::section:
2004 if (n ==
"result") {
2006 for (
size_t i = 0; i <
nbResults; ++i) {
2009 }
else if (n ==
"measurement") {
2011 throw std::runtime_error(
2012 "render: can only use section 'measurement' here if there is a single result, but there are " +
2013 detail::fmt::to_s(
results.size()));
2016 auto const& r =
results.front();
2017 for (
size_t i = 0; i < r.size(); ++i) {
2021 throw std::runtime_error(
"render: unknown section '" + std::string(n.begin, n.end) +
"'");
2025 case templates::Node::Type::tag:
2032 throw std::runtime_error(
"unknown tag '" + std::string(n.begin, n.end) +
"'");
2055# if defined(__clang__)
2056# pragma clang diagnostic push
2057# pragma clang diagnostic ignored "-Wexit-time-destructors"
2059 static PerformanceCounters
pc;
2060# if defined(__clang__)
2061# pragma clang diagnostic pop
2070# if defined(_MSC_VER)
2071# pragma optimize("", off)
2073# pragma optimize("", on)
2076template <
typename T>
2077T parseFile(std::string
const& filename,
bool* fail) {
2078 std::ifstream
fin(filename);
2081 if (fail !=
nullptr) {
2088# if defined(_MSC_VER)
2089# pragma warning(push)
2090# pragma warning(disable : 4996)
2092 return std::getenv(
name);
2093# if defined(_MSC_VER)
2094# pragma warning(pop)
2114 warnings.emplace_back(
"DEBUG defined");
2121# if defined(__linux__)
2124 warnings.emplace_back(
"couldn't figure out number of processors - no governor, turbo check possible");
2127 for (
long id = 0;
id <
nprocs; ++id) {
2129 auto sysCpu =
"/sys/devices/system/cpu/cpu" +
idStr;
2135 warnings.emplace_back(
"CPU frequency scaling enabled: CPU " +
idStr +
" between " +
2136 detail::fmt::Number(1, 1,
minMHz).
to_s() +
" and " + detail::fmt::Number(1, 1,
maxMHz).
to_s() +
2146 warnings.emplace_back(
"CPU governor is '" +
currentGovernor +
"' but should be 'performance'");
2152 warnings.emplace_back(
"Turbo is enabled, CPU frequency will fluctuate");
2162 recommendations.emplace_back(
"Use 'pyperf system tune' before benchmarking. See https://github.com/psf/pyperf");
2171 std::vector<std::string> warnings;
2174 if (warnings.empty()) {
2178 os <<
"Warning, results might be unstable:" << std::endl;
2179 for (
auto const&
w : warnings) {
2180 os <<
"* " <<
w << std::endl;
2183 os << std::endl <<
"Recommendations" << std::endl;
2185 os <<
"* " << r << std::endl;
2198 return seed ^ (val +
UINT64_C(0x9e3779b9) + (seed << 6U) + (seed >> 2U));
2204 Clock::time_point
tBegin;
2205 Clock::time_point
tEnd;
2209 tEnd = Clock::now();
2223struct IterationLogic::Impl {
2226 explicit Impl(Bench
const&
bench)
2241 std::cerr <<
"NANOBENCH_ENDLESS set: running '" << mBench.name() <<
"' endlessly" << std::endl;
2242 mNumIters = (std::numeric_limits<uint64_t>::max)();
2244 }
else if (0 != mBench.warmup()) {
2247 }
else if (0 != mBench.epochIterations()) {
2250 mState = State::measuring;
2252 mNumIters = mBench.minEpochIterations();
2253 mState = State::upscaling_runtime;
2279 showResult(
"iterations overflow. Maybe your code got optimized away?");
2289 void add(std::chrono::nanoseconds elapsed, PerformanceCounters
const&
pc)
noexcept {
2290# if defined(ANKERL_NANOBENCH_LOG_ENABLED)
2299 mState = State::measuring;
2303 mState = State::upscaling_runtime;
2308 case State::upscaling_runtime:
2311 mState = State::measuring;
2321 case State::measuring:
2327 if (0 != mBench.epochIterations()) {
2334 case State::endless:
2335 mNumIters = (std::numeric_limits<uint64_t>::max)();
2345 ANKERL_NANOBENCH_LOG(mBench.name() <<
": " << detail::fmt::Number(20, 3, d(elapsed.count())) <<
" elapsed, "
2354 if (mBench.output() !=
nullptr) {
2356 std::vector<fmt::MarkDownColumn> columns;
2360 if (mBench.relative()) {
2362 if (!mBench.results().empty()) {
2363 d =
rMedian <= 0.0 ? 0.0 : mBench.results().front().median(Result::Measure::elapsed) /
rMedian * 100.0;
2365 columns.emplace_back(11, 1,
"relative",
"%", d);
2368 if (mBench.complexityN() > 0) {
2369 columns.emplace_back(14, 0,
"complexityN",
"", mBench.complexityN());
2372 columns.emplace_back(22, 2, mBench.timeUnitName() +
"/" + mBench.unit(),
"",
2373 rMedian / (mBench.timeUnit().count() * mBench.batch()));
2374 columns.emplace_back(22, 2, mBench.unit() +
"/s",
"",
rMedian <= 0.0 ? 0.0 : mBench.batch() /
rMedian);
2377 columns.emplace_back(10, 1,
"err%",
"%",
rErrorMedian * 100.0);
2380 if (mBench.performanceCounters() &&
mResult.has(Result::Measure::instructions)) {
2382 columns.emplace_back(18, 2,
"ins/" + mBench.unit(),
"",
rInsMedian / mBench.batch());
2386 if (mBench.performanceCounters() &&
mResult.has(Result::Measure::cpucycles)) {
2388 columns.emplace_back(18, 2,
"cyc/" + mBench.unit(),
"",
rCycMedian / mBench.batch());
2393 if (mBench.performanceCounters() &&
mResult.has(Result::Measure::branchinstructions)) {
2395 columns.emplace_back(17, 2,
"bra/" + mBench.unit(),
"",
rBraMedian / mBench.batch());
2396 if (
mResult.has(Result::Measure::branchmisses)) {
2401 columns.emplace_back(10, 1,
"miss%",
"%",
p);
2405 columns.emplace_back(12, 2,
"total",
"",
mResult.sumProduct(Result::Measure::iterations, Result::Measure::elapsed));
2408 auto&
os = *mBench.output();
2412 hash =
hash_combine(std::hash<std::string>{}(mBench.unit()), hash);
2413 hash =
hash_combine(std::hash<std::string>{}(mBench.title()), hash);
2414 hash =
hash_combine(std::hash<std::string>{}(mBench.timeUnitName()), hash);
2416 hash =
hash_combine(std::hash<bool>{}(mBench.relative()), hash);
2417 hash =
hash_combine(std::hash<bool>{}(mBench.performanceCounters()), hash);
2424 for (
auto const&
col : columns) {
2427 os <<
"| " << mBench.title() << std::endl;
2429 for (
auto const&
col : columns) {
2430 os <<
col.separator();
2432 os <<
"|:" << std::string(mBench.title().size() + 1U,
'-') << std::endl;
2436 for (
auto const&
col : columns) {
2437 os <<
col.invalid();
2439 os <<
"| :boom: " << fmt::MarkDownCode(mBench.name()) <<
" (" <<
errorMessage <<
')' << std::endl;
2441 for (
auto const&
col : columns) {
2447 os <<
":wavy_dash: ";
2449 os << fmt::MarkDownCode(mBench.name());
2455 os <<
" (Unstable with ~" << detail::fmt::Number(1, 1,
avgIters)
2456 <<
" iters. Increase `minEpochIterations` to e.g. " <<
suggestedIters <<
")";
2468 Bench
const& mBench;
2478IterationLogic::IterationLogic(Bench
const&
bench)
2481IterationLogic::~IterationLogic() {
2485uint64_t IterationLogic::numIters() const noexcept {
2487 return mPimpl->mNumIters;
2490void IterationLogic::add(std::chrono::nanoseconds elapsed, PerformanceCounters
const&
pc)
noexcept {
2491 mPimpl->add(elapsed,
pc);
2494void IterationLogic::moveResultTo(std::vector<Result>&
results)
noexcept {
2495 results.emplace_back(std::move(mPimpl->mResult));
2498# if ANKERL_NANOBENCH(PERF_COUNTERS)
2522 inline void start() {}
2524 inline void stop() {}
2535 inline void beginMeasure() {
2550 inline void endMeasure() {
2569 template <
typename T>
2582 template <
typename Op>
2593 v = (std::numeric_limits<uint64_t>::max)();
2595 for (
size_t iter = 0; iter < 100; ++iter) {
2627 detail::doNotOptimizeAway(x);
2638 detail::doNotOptimizeAway(x);
2641 for (
size_t i = 0; i <
mCounters.size(); ++i) {
2658 std::vector<uint64_t>
mCounters = std::vector<uint64_t>(3);
2660 std::vector<uint64_t>
mLoopOverhead = std::vector<uint64_t>(3);
2669LinuxPerformanceCounters::~LinuxPerformanceCounters() {
2679bool LinuxPerformanceCounters::monitor(
perf_hw_id hwId, LinuxPerformanceCounters::Target
target) {
2699 auto idx =
static_cast<size_t>(3 + i * 2 + 0);
2705 auto&
tgt = it->second;
2707 if (
tgt.correctMeasuringOverhead) {
2711 *
tgt.targetValue = 0
U;
2714 if (
tgt.correctLoopOverhead) {
2719 *
tgt.targetValue = 0
U;
2727 *
target.targetValue = (std::numeric_limits<uint64_t>::max)();
2738 pea.exclude_kernel = 1;
2746# if defined(PERF_FLAG_FD_CLOEXEC)
2749 const unsigned long flags = 0;
2780PerformanceCounters::PerformanceCounters()
2787 if (!mHas.cpuCycles) {
2792 mHas.branchInstructions =
2799 mHas.contextSwitches =
2804 auto before = ankerl::nanobench::Clock::now();
2805 auto after = ankerl::nanobench::Clock::now();
2810 if (
mPc->hasError()) {
2812 mHas = PerfCountSet<bool>{};
2816PerformanceCounters::~PerformanceCounters() {
2821void PerformanceCounters::beginMeasure() {
2822 mPc->beginMeasure();
2825void PerformanceCounters::endMeasure() {
2835PerformanceCounters::PerformanceCounters() =
default;
2836PerformanceCounters::~PerformanceCounters() =
default;
2837void PerformanceCounters::beginMeasure() {}
2838void PerformanceCounters::endMeasure() {}
2839void PerformanceCounters::updateResults(
uint64_t) {}
2854NumSep::NumSep(
char sep)
2857char NumSep::do_thousands_sep()
const {
2861std::string NumSep::do_grouping()
const {
2866StreamStateRestorer::StreamStateRestorer(std::ostream& s)
2874StreamStateRestorer::~StreamStateRestorer() {
2879void StreamStateRestorer::restore() {
2897std::ostream& Number::write(std::ostream&
os)
const {
2899 os.imbue(std::locale(
os.getloc(),
new NumSep(
',')));
2904std::string Number::to_s()
const {
2905 std::stringstream
ss;
2913 str +=
static_cast<char>(
'0' +
static_cast<char>(n % 10));
2916 std::reverse(str.begin(), str.end());
2924MarkDownColumn::MarkDownColumn(
int w,
int prec, std::string
tit, std::string
suff,
double val) noexcept
2931std::string MarkDownColumn::title()
const {
2932 std::stringstream
ss;
2933 ss <<
'|' << std::setw(
mWidth - 2) << std::right <<
mTitle <<
' ';
2937std::string MarkDownColumn::separator()
const {
2938 std::string
sep(
static_cast<size_t>(
mWidth),
'-');
2944std::string MarkDownColumn::invalid()
const {
2945 std::string
sep(
static_cast<size_t>(
mWidth),
' ');
2947 sep[
sep.size() - 2] =
'-';
2951std::string MarkDownColumn::value()
const {
2952 std::stringstream
ss;
2959MarkDownCode::MarkDownCode(std::string
const& what) {
2960 mWhat.reserve(what.size() + 2);
2961 mWhat.push_back(
'`');
2962 for (
char const c : what) {
2965 mWhat.push_back(
'`');
2968 mWhat.push_back(
'`');
2971std::ostream& MarkDownCode::write(std::ostream&
os)
const {
2982Config::Config() =
default;
2983Config::~Config() =
default;
2984Config& Config::operator=(Config
const&) =
default;
2986Config::Config(Config const&) =
default;
2987Config::Config(Config&&) noexcept =
default;
2991Result& Result::operator=(Result const&) =
default;
2993Result::Result(Result const&) =
default;
2994Result::Result(Result&&) noexcept =
default;
2997template <
typename T>
2998inline constexpr typename std::underlying_type<T>::type u(
T val)
noexcept {
2999 return static_cast<typename std::underlying_type<T>::type
>(val);
3006 , mNameToMeasurements{
detail::u(
Result::Measure::_size)} {}
3013 mNameToMeasurements[u(Result::Measure::iterations)].push_back(
dIters);
3015 mNameToMeasurements[u(Result::Measure::elapsed)].push_back(d(
totalElapsed) /
dIters);
3016 if (
pc.has().pageFaults) {
3017 mNameToMeasurements[u(Result::Measure::pagefaults)].push_back(d(
pc.val().pageFaults) /
dIters);
3019 if (
pc.has().cpuCycles) {
3020 mNameToMeasurements[u(Result::Measure::cpucycles)].push_back(d(
pc.val().cpuCycles) /
dIters);
3022 if (
pc.has().contextSwitches) {
3023 mNameToMeasurements[u(Result::Measure::contextswitches)].push_back(d(
pc.val().contextSwitches) /
dIters);
3025 if (
pc.has().instructions) {
3026 mNameToMeasurements[u(Result::Measure::instructions)].push_back(d(
pc.val().instructions) /
dIters);
3028 if (
pc.has().branchInstructions) {
3029 double branchInstructions = 0.0;
3031 if (
pc.val().branchInstructions >
iters + 1U) {
3032 branchInstructions = d(
pc.val().branchInstructions - (
iters + 1U));
3034 mNameToMeasurements[u(Result::Measure::branchinstructions)].push_back(branchInstructions /
dIters);
3036 if (
pc.has().branchMisses) {
3038 double branchMisses = d(
pc.val().branchMisses);
3039 if (branchMisses > branchInstructions) {
3041 branchMisses = branchInstructions;
3045 branchMisses -= 1.0;
3046 if (branchMisses < 1.0) {
3049 mNameToMeasurements[u(Result::Measure::branchmisses)].push_back(branchMisses /
dIters);
3054Config
const& Result::config() const noexcept {
3062 std::sort(
data.begin(),
data.end());
3065 if (1U == (
data.size() & 1U)) {
3071double Result::median(Measure
m)
const {
3073 auto data = mNameToMeasurements[detail::u(
m)];
3077double Result::average(Measure
m)
const {
3079 auto const&
data = mNameToMeasurements[detail::u(
m)];
3088double Result::medianAbsolutePercentError(Measure
m)
const {
3090 auto data = mNameToMeasurements[detail::u(
m)];
3097 for (
auto& x :
data) {
3106double Result::sum(Measure
m)
const noexcept {
3107 auto const&
data = mNameToMeasurements[detail::u(
m)];
3108 return std::accumulate(
data.begin(),
data.end(), 0.0);
3111double Result::sumProduct(Measure
m1, Measure
m2)
const noexcept {
3112 auto const&
data1 = mNameToMeasurements[detail::u(
m1)];
3113 auto const&
data2 = mNameToMeasurements[detail::u(
m2)];
3119 double result = 0.0;
3120 for (
size_t i = 0, s =
data1.size(); i !=
s; ++i) {
3126bool Result::has(Measure
m)
const noexcept {
3127 return !mNameToMeasurements[detail::u(
m)].empty();
3130double Result::get(
size_t idx, Measure
m)
const {
3131 auto const&
data = mNameToMeasurements[detail::u(
m)];
3132 return data.at(idx);
3135bool Result::empty() const noexcept {
3136 return 0
U == size();
3139size_t Result::size() const noexcept {
3140 auto const&
data = mNameToMeasurements[detail::u(Measure::elapsed)];
3144double Result::minimum(Measure
m)
const noexcept {
3145 auto const&
data = mNameToMeasurements[detail::u(
m)];
3151 return *std::min_element(
data.begin(),
data.end());
3154double Result::maximum(Measure
m)
const noexcept {
3155 auto const&
data = mNameToMeasurements[detail::u(
m)];
3161 return *std::max_element(
data.begin(),
data.end());
3164std::string
const& Result::context(
char const*
variableName)
const {
3168std::string
const& Result::context(std::string
const&
variableName)
const {
3172Result::Measure Result::fromString(std::string
const& str) {
3173 if (str ==
"elapsed") {
3174 return Measure::elapsed;
3176 if (str ==
"iterations") {
3177 return Measure::iterations;
3179 if (str ==
"pagefaults") {
3180 return Measure::pagefaults;
3182 if (str ==
"cpucycles") {
3183 return Measure::cpucycles;
3185 if (str ==
"contextswitches") {
3186 return Measure::contextswitches;
3188 if (str ==
"instructions") {
3189 return Measure::instructions;
3191 if (str ==
"branchinstructions") {
3192 return Measure::branchinstructions;
3194 if (str ==
"branchmisses") {
3195 return Measure::branchmisses;
3198 return Measure::_size;
3203 mConfig.mOut = &std::cout;
3206Bench::Bench(Bench&&) noexcept =
default;
3208Bench::Bench(Bench const&) =
default;
3209Bench& Bench::operator=(Bench const&) =
default;
3210Bench::~Bench() noexcept =
default;
3212double Bench::batch() const noexcept {
3213 return mConfig.mBatch;
3216double Bench::complexityN() const noexcept {
3217 return mConfig.mComplexityN;
3226bool Bench::relative() const noexcept {
3227 return mConfig.mIsRelative;
3234bool Bench::performanceCounters() const noexcept {
3235 return mConfig.mShowPerformanceCounters;
3241Bench& Bench::unit(
char const* u) {
3242 if (u != mConfig.mUnit) {
3249Bench& Bench::unit(std::string
const& u) {
3250 return unit(u.c_str());
3253std::string
const& Bench::unit() const noexcept {
3254 return mConfig.mUnit;
3257Bench& Bench::timeUnit(std::chrono::duration<double>
const&
tu, std::string
const&
tuName) {
3258 mConfig.mTimeUnit =
tu;
3259 mConfig.mTimeUnitName =
tuName;
3263std::string
const& Bench::timeUnitName() const noexcept {
3264 return mConfig.mTimeUnitName;
3267std::chrono::duration<double>
const& Bench::timeUnit() const noexcept {
3268 return mConfig.mTimeUnit;
3287std::string
const& Bench::title() const noexcept {
3288 return mConfig.mBenchmarkTitle;
3301std::string
const& Bench::name() const noexcept {
3302 return mConfig.mBenchmarkName;
3315Bench& Bench::clearContext() {
3316 mConfig.mContext.clear();
3321Bench& Bench::epochs(
size_t numEpochs)
noexcept {
3325size_t Bench::epochs() const noexcept {
3326 return mConfig.mNumEpochs;
3330Bench& Bench::clockResolutionMultiple(
size_t multiple)
noexcept {
3331 mConfig.mClockResolutionMultiple =
multiple;
3334size_t Bench::clockResolutionMultiple() const noexcept {
3335 return mConfig.mClockResolutionMultiple;
3339Bench& Bench::maxEpochTime(std::chrono::nanoseconds
t)
noexcept {
3340 mConfig.mMaxEpochTime =
t;
3343std::chrono::nanoseconds Bench::maxEpochTime() const noexcept {
3344 return mConfig.mMaxEpochTime;
3348Bench& Bench::minEpochTime(std::chrono::nanoseconds
t)
noexcept {
3349 mConfig.mMinEpochTime =
t;
3352std::chrono::nanoseconds Bench::minEpochTime() const noexcept {
3353 return mConfig.mMinEpochTime;
3360uint64_t Bench::minEpochIterations() const noexcept {
3361 return mConfig.mMinEpochIterations;
3365 mConfig.mEpochIterations =
numIters;
3368uint64_t Bench::epochIterations() const noexcept {
3369 return mConfig.mEpochIterations;
3376uint64_t Bench::warmup() const noexcept {
3377 return mConfig.mWarmup;
3384Config
const& Bench::config() const noexcept {
3388Bench& Bench::output(std::ostream*
outstream)
noexcept {
3394 return mConfig.mOut;
3397std::vector<Result>
const& Bench::results() const noexcept {
3411std::vector<BigO> Bench::complexityBigO()
const {
3412 std::vector<BigO>
bigOs;
3413 auto rangeMeasure = BigO::collectRangeMeasure(mResults);
3421 return std::log2(n);
3424 return n * std::log2(n);
3439 std::random_device
rd;
3440 std::uniform_int_distribution<uint64_t>
dist;
3444 }
while (mX == 0 && mY == 0);
3450 z = (z ^ (z >> 30U)) *
UINT64_C(0xbf58476d1ce4e5b9);
3451 z = (z ^ (z >> 27U)) *
UINT64_C(0x94d049bb133111eb);
3452 return z ^ (z >> 31U);
3459 for (
size_t i = 0; i < 10; ++i) {
3469Rng Rng::copy() const noexcept {
3473Rng::Rng(std::vector<uint64_t>
const&
data)
3476 if (
data.size() != 2) {
3477 throw std::runtime_error(
"ankerl::nanobench::Rng::Rng: needed exactly 2 entries in data, but got " +
3478 detail::fmt::to_s(
data.size()));
3484std::vector<uint64_t> Rng::state()
const {
3485 std::vector<uint64_t>
data(2);
3491BigO::RangeMeasure BigO::collectRangeMeasure(std::vector<Result>
const&
results) {
3493 for (
auto const& result :
results) {
3494 if (result.config().mComplexityN > 0.0) {
3495 rangeMeasure.emplace_back(result.config().mComplexityN, result.median(Result::Measure::elapsed));
3518 auto diff = mConstant *
rm.first -
rm.second;
3526 mNormalizedRootMeanSquare = std::sqrt(err / n) /
mean;
3532std::string
const& BigO::name() const noexcept {
3536double BigO::constant() const noexcept {
3540double BigO::normalizedRootMeanSquare() const noexcept {
3541 return mNormalizedRootMeanSquare;
3544bool BigO::operator<(BigO
const& other)
const noexcept {
3545 return std::tie(mNormalizedRootMeanSquare, mName) < std::tie(other.mNormalizedRootMeanSquare, other.mName);
3549 return os <<
bigO.constant() <<
" * " <<
bigO.name() <<
", rms=" <<
bigO.normalizedRootMeanSquare();
3552std::ostream&
operator<<(std::ostream&
os, std::vector<ankerl::nanobench::BigO>
const&
bigOs) {
3553 detail::fmt::StreamStateRestorer
const restorer(
os);
3554 os << std::endl <<
"| coefficient | err% | complexity" << std::endl <<
"|--------------:|-------:|------------" << std::endl;
3556 os <<
"|" << std::setw(14) << std::setprecision(7) << std::scientific <<
bigO.constant() <<
" ";
3557 os <<
"|" << detail::fmt::Number(6, 1,
bigO.normalizedRootMeanSquare() * 100.0) <<
"% ";
3558 os <<
"| " <<
bigO.name();
Main entry point to nanobench's benchmarking facility.
Bench & operator=(Bench const &other)
Bench()
Creates a new benchmark for configuration and running of benchmarks.
Bench & operator=(Bench &&other) noexcept(ANKERL_NANOBENCH(NOEXCEPT_STRING_MOVE))
Bench(Bench &&other) noexcept
Bench(Bench const &other)
static RangeMeasure mapRangeMeasure(RangeMeasure data, Op op)
BigO(std::string bigOName, RangeMeasure const &scaledRangeMeasure)
std::vector< std::pair< double, double > > RangeMeasure
BigO(char const *bigOName, RangeMeasure const &rangeMeasure, Op rangeToN)
static RangeMeasure collectRangeMeasure(std::vector< Result > const &results)
BigO(std::string bigOName, RangeMeasure const &rangeMeasure, Op rangeToN)
BigO(char const *bigOName, RangeMeasure const &scaledRangeMeasure)
Result(Config benchmarkConfig)
void add(Clock::duration totalElapsed, uint64_t iters, detail::PerformanceCounters const &pc)
Result(Result &&other) noexcept
ANKERL_NANOBENCH(NODISCARD) Config const &config() const noexcept
Result & operator=(Result const &other)
Result(Result const &other)
Result & operator=(Result &&other) noexcept(ANKERL_NANOBENCH(NOEXCEPT_STRING_MOVE))
An extremely fast random generator.
Rng(Rng const &)=delete
As a safety precaution, we don't allow copying.
Rng(Rng &&) noexcept=default
Rng & operator=(Rng const &)=delete
Same as Rng(Rng const&), we don't allow assignment.
uint64_t result_type
This RNG provides 64bit randomness.
IterationLogic(IterationLogic &&)=delete
IterationLogic & operator=(IterationLogic const &)=delete
ANKERL_NANOBENCH(NODISCARD) uint64_t numIters() const noexcept
IterationLogic(IterationLogic const &)=delete
IterationLogic(Bench const &bench)
IterationLogic & operator=(IterationLogic &&)=delete
SetupRunner(SetupOp setupOp, Bench &bench)
static const PrecomputedData data
Precomputed COutPoint and CCoins values.
static OutputStream & operator<<(OutputStream &os, const Array &array)
#define T(expected, seed, data)
PerformanceCounters & performanceCounters()
void doNotOptimizeAway(T const &val)
char const * json() noexcept
Template to generate JSON data.
char const * csv() noexcept
CSV data for the benchmark results.
char const * pyperf() noexcept
Output in pyperf compatible JSON format, which can be used for more analyzation.
char const * htmlBoxplot() noexcept
HTML output that uses plotly to generate an interactive boxplot chart. See the tutorial for an exampl...
void render(char const *mustacheTemplate, Bench const &bench, std::ostream &out)
Renders output from a mustache-like template and benchmark results.
std::conditional< std::chrono::high_resolution_clock::is_steady, std::chrono::high_resolution_clock, std::chrono::steady_clock >::type Clock
std::ostream & operator<<(std::ostream &os, BigO const &bigO)
void doNotOptimizeAway(Arg &&arg)
Makes sure none of the given arguments are optimized away by the compiler.
#define ANKERL_NANOBENCH_LOG(x)
#define ANKERL_NANOBENCH_NO_SANITIZE(...)
#define ANKERL_NANOBENCH(x)
bool operator==(const CNetAddr &a, const CNetAddr &b)
Config & operator=(Config const &other)
Config(Config const &other)
Config & operator=(Config &&other) noexcept(ANKERL_NANOBENCH(NOEXCEPT_STRING_MOVE))
Config(Config &&other) noexcept
static SECP256K1_INLINE uint64_t rotl(const uint64_t x, int k)
constexpr auto Ticks(Dur2 d)
Helper to count the seconds of a duration/time_point.