31 #include <boost/regex.hpp> 42 DEFINE_bool(json_verbose,
false,
"Output in verbose JSON format.");
47 "Only benchmarks whose names match this regex will be run.");
52 "Minimum # of microseconds we'll accept for each benchmark.");
57 "Minimum # of iterations we'll try for each benchmark.");
62 "Maximum # of iterations we'll try for each benchmark.");
67 "Maximum # of seconds we'll spend on each benchmark.");
71 std::chrono::high_resolution_clock::duration BenchmarkSuspender::timeSpent;
76 static vector<detail::BenchmarkRegistration> _benchmarks;
80 #define FB_FOLLY_GLOBAL_BENCHMARK_BASELINE fbFollyGlobalBenchmarkBaseline 81 #define FB_STRINGIZE_X2(x) FB_STRINGIZE(x) 94 auto it = std::find_if(
98 return v.
name == global;
104 #undef FB_STRINGIZE_X2 105 #undef FB_FOLLY_GLOBAL_BENCHMARK_BASELINE 122 return *min_element(begin, end);
126 const BenchmarkFun& fun,
127 const double globalBaseline) {
128 using std::chrono::duration_cast;
129 using std::chrono::high_resolution_clock;
130 using std::chrono::microseconds;
131 using std::chrono::nanoseconds;
132 using std::chrono::seconds;
139 "High resolution clock must be nanosecond resolution.");
143 static const auto minNanoseconds = std::max<nanoseconds>(
144 nanoseconds(100000), microseconds(FLAGS_bm_min_usec));
148 static const unsigned int epochs = 1000;
151 const auto timeBudget = seconds(FLAGS_bm_max_secs);
154 double epochResults[epochs] = {0};
155 size_t actualEpochs = 0;
157 for (; actualEpochs < epochs; ++actualEpochs) {
158 const auto maxIters =
uint32_t(FLAGS_bm_max_iters);
159 for (
auto n =
uint32_t(FLAGS_bm_min_iters); n < maxIters; n *= 2) {
160 auto const nsecsAndIter =
fun(static_cast<unsigned int>(n));
161 if (nsecsAndIter.first < minNanoseconds) {
166 auto nsecs = duration_cast<nanoseconds>(nsecsAndIter.first).
count();
167 epochResults[actualEpochs] =
168 max(0.0,
double(nsecs) / nsecsAndIter.second - globalBaseline);
173 if (
now - global >= timeBudget) {
182 return max(0.0,
estimateTime(epochResults, epochResults + actualEpochs));
191 {365.25 * 24 * 3600,
"years"},
228 if (std::isinf(n) || std::isnan(n)) {
229 return folly::to<string>(n);
232 const double absValue = fabs(n);
234 while (absValue < scale[0].boundary && scale[1].
suffix !=
nullptr) {
238 const double scaledValue = n / scale->
boundary;
251 class BenchmarkResultsPrinter {
257 void separator(
char pad) {
258 puts(
string(
columns, pad).c_str());
261 void header(
const string& file) {
263 printf(
"%-*srelative time/iter iters/s\n",
columns - 28, file.c_str());
267 void print(
const vector<detail::BenchmarkResult>&
data) {
268 for (
auto& datum : data) {
269 auto file = datum.file;
270 if (file != lastFile) {
276 string s = datum.name;
290 auto nsPerIter = datum.timeInNs;
291 auto secPerIter = nsPerIter / 1E9;
292 auto itersPerSec = (secPerIter == 0)
293 ? std::numeric_limits<double>::infinity()
299 static_cast<int>(s.size()),
307 "%*s %7.2f%% %9s %7s\n",
308 static_cast<int>(s.size()),
320 const vector<detail::BenchmarkResult>&
data) {
322 for (
auto& datum : data) {
323 d[datum.name] = datum.timeInNs * 1000.;
330 const vector<detail::BenchmarkResult>&
data) {
337 if (FLAGS_json_verbose) {
340 }
else if (FLAGS_json) {
345 CHECK(FLAGS_json_verbose || FLAGS_json) <<
"Cannot print benchmark results";
349 const vector<detail::BenchmarkResult>&
data,
351 out = dynamic::array;
352 for (
auto& datum : data) {
353 out.
push_back(dynamic::array(datum.file, datum.name, datum.timeInNs));
359 vector<detail::BenchmarkResult>& results) {
360 for (
auto& datum : d) {
362 {datum[0].asString(), datum[1].asString(), datum[2].asDouble()});
368 return pair<StringPiece, StringPiece>(result.
file, result.
name);
372 const vector<detail::BenchmarkResult>& base,
373 const vector<detail::BenchmarkResult>&
test) {
374 map<pair<StringPiece, StringPiece>,
double> baselines;
376 for (
auto& baseResult : base) {
377 baselines[
resultKey(baseResult)] = baseResult.timeInNs;
381 static const unsigned int columns = 76;
384 size_t longestName = 0;
385 for (
auto& datum : test) {
386 longestName =
max(longestName, datum.name.size());
390 auto separator = [&](
char pad) { puts(
string(columns, pad).c_str()); };
393 auto header = [&](
const string& file) {
395 printf(
"%-*srelative time/iter iters/s\n", columns - 28, file.c_str());
401 for (
auto& datum : test) {
404 auto file = datum.file;
405 if (file != lastFile) {
411 string s = datum.name;
419 s.resize(columns - 29,
' ');
420 auto nsPerIter = datum.timeInNs;
421 auto secPerIter = nsPerIter / 1E9;
422 auto itersPerSec = (secPerIter == 0)
423 ? std::numeric_limits<double>::infinity()
429 static_cast<int>(s.size()),
435 auto rel = *baseline / nsPerIter * 100.0;
437 "%*s %7.2f%% %9s %7s\n",
438 static_cast<int>(s.size()),
450 std::cerr <<
"WARNING: Benchmark running " 461 vector<detail::BenchmarkResult> results;
464 std::unique_ptr<boost::regex> bmRegex;
465 if (!FLAGS_bm_regex.empty()) {
466 bmRegex = std::make_unique<boost::regex>(FLAGS_bm_regex);
473 auto const globalBaseline =
475 auto printer = BenchmarkResultsPrinter{};
477 if (
i == baselineIndex) {
480 double elapsed = 0.0;
482 if (
bm.name !=
"-") {
483 if (bmRegex && !boost::regex_search(
bm.name, *bmRegex)) {
489 if (!FLAGS_json_verbose && !FLAGS_json) {
490 printer.print({{
bm.file,
bm.name, elapsed}});
492 results.push_back({
bm.file,
bm.name, elapsed});
498 if (FLAGS_json_verbose || FLAGS_json) {
501 printer.separator(
'=');
static const ScaleInfo kTimeSuffixes[]
size_t getGlobalBenchmarkBaselineIndex()
BENCHMARK(constantFuture)
static void printBenchmarkResults(const vector< detail::BenchmarkResult > &data)
DEFINE_bool(benchmark, false,"Run benchmarks.")
static string humanReadable(double n, unsigned int decimals, const ScaleInfo *scales)
static void printBenchmarkResultsAsVerboseJson(const vector< detail::BenchmarkResult > &data)
vector< detail::BenchmarkRegistration > & benchmarks()
std::chrono::steady_clock::time_point now()
constexpr detail::Map< Move > move
#define FB_STRINGIZE_X2(x)
void addBenchmarkImpl(const char *file, const char *name, std::function< TimeIterPair(unsigned int)>)
auto begin(TestAdlIterable &instance)
function< detail::TimeIterPair(unsigned int)> BenchmarkFun
std::string stringPrintf(const char *format,...)
void benchmarkResultsFromDynamic(const dynamic &d, vector< detail::BenchmarkResult > &results)
—— Concurrent Priority Queue Implementation ——
DEFINE_int32(bm_min_iters, 1,"Minimum # of iterations we'll try for each benchmark.")
DEFINE_string(bm_regex,"","Only benchmarks whose names match this regex will be run.")
static string readableTime(double n, unsigned int decimals)
#define FOR_EACH_RANGE(i, begin, end)
void benchmarkResultsToDynamic(const vector< detail::BenchmarkResult > &data, dynamic &out)
constexpr auto size(C const &c) -> decltype(c.size())
constexpr auto empty(C const &c) -> decltype(c.empty())
static string metricReadable(double n, unsigned int decimals)
constexpr bool kIsSanitize
auto end(TestAdlIterable &instance)
std::string toPrettyJson(dynamic const &dyn)
folly::Optional< typename Map::mapped_type > get_optional(const Map &map, const Key &key)
static double estimateTime(double *begin, double *end)
void push_back(dynamic const &)
static const char *const value
static constexpr unsigned int columns
DEFINE_int64(bm_min_usec, 100,"Minimum # of microseconds we'll accept for each benchmark.")
static const ScaleInfo kMetricSuffixes[]
#define FB_FOLLY_GLOBAL_BENCHMARK_BASELINE
void printResultComparison(const vector< detail::BenchmarkResult > &base, const vector< detail::BenchmarkResult > &test)
static void printBenchmarkResultsAsJson(const vector< detail::BenchmarkResult > &data)
static constexpr uint64_t data[1]
static double runBenchmarkGetNSPerIteration(const BenchmarkFun &fun, const double globalBaseline)
static pair< StringPiece, StringPiece > resultKey(const detail::BenchmarkResult &result)