Commit 867f9145 by Ismael

added lambdas to complexity report

parent 74a278e2
......@@ -152,6 +152,7 @@ BENCHMARK(BM_test)->Unit(benchmark::kMillisecond);
#include <assert.h>
#include <stddef.h>
#include <stdint.h>
#include <functional>
#include "macros.h"
......@@ -247,9 +248,14 @@ enum BigO {
oNCubed,
oLogN,
oNLogN,
oAuto
oAuto,
oLambda
};
// BigOFunc is passed to a benchmark in order to specify the asymptotic
// computational complexity for the benchmark.
typedef double(BigOFunc)(size_t);
// State is passed to a running Benchmark and contains state for the
// benchmark to use.
class State {
......@@ -538,6 +544,10 @@ public:
// the asymptotic computational complexity will be shown on the output.
Benchmark* Complexity(BigO complexity = benchmark::oAuto);
// Set the asymptotic computational complexity for the benchmark. If called
// the asymptotic computational complexity will be shown on the output.
Benchmark* Complexity(BigOFunc* complexity);
// Support for running multiple copies of the same benchmark concurrently
// in multiple threads. This may be useful when measuring the scaling
// of some piece of code.
......
......@@ -86,7 +86,8 @@ class BenchmarkReporter {
// Keep track of arguments to compute asymptotic complexity
BigO complexity;
int complexity_n;
BigOFunc* complexity_lambda;
size_t complexity_n;
// Inform print function whether the current run is a complexity report
bool report_big_o;
......@@ -159,7 +160,7 @@ class ConsoleReporter : public BenchmarkReporter {
virtual bool ReportContext(const Context& context);
virtual void ReportRuns(const std::vector<Run>& reports);
protected:
protected:
virtual void PrintRunData(const Run& report);
size_t name_field_width_;
......
......@@ -124,7 +124,7 @@ struct ThreadStats {
ThreadStats() : bytes_processed(0), items_processed(0), complexity_n(0) {}
int64_t bytes_processed;
int64_t items_processed;
int complexity_n;
size_t complexity_n;
};
// Timer management class
......@@ -311,6 +311,7 @@ struct Benchmark::Instance {
bool use_real_time;
bool use_manual_time;
BigO complexity;
BigOFunc* complexity_lambda;
bool last_benchmark_instance;
int repetitions;
double min_time;
......@@ -356,6 +357,7 @@ public:
void UseRealTime();
void UseManualTime();
void Complexity(BigO complexity);
void ComplexityLambda(BigOFunc* complexity);
void Threads(int t);
void ThreadRange(int min_threads, int max_threads);
void ThreadPerCpu();
......@@ -376,6 +378,7 @@ private:
bool use_real_time_;
bool use_manual_time_;
BigO complexity_;
BigOFunc* complexity_lambda_;
std::vector<int> thread_counts_;
BenchmarkImp& operator=(BenchmarkImp const&);
......@@ -440,6 +443,7 @@ bool BenchmarkFamilies::FindBenchmarks(
instance.use_real_time = family->use_real_time_;
instance.use_manual_time = family->use_manual_time_;
instance.complexity = family->complexity_;
instance.complexity_lambda = family->complexity_lambda_;
instance.threads = num_threads;
instance.multithreaded = !(family->thread_counts_.empty());
......@@ -567,6 +571,10 @@ void BenchmarkImp::Complexity(BigO complexity){
complexity_ = complexity;
}
void BenchmarkImp::ComplexityLambda(BigOFunc* complexity) {
complexity_lambda_ = complexity;
}
void BenchmarkImp::Threads(int t) {
CHECK_GT(t, 0);
thread_counts_.push_back(t);
......@@ -691,6 +699,12 @@ Benchmark* Benchmark::Complexity(BigO complexity) {
return this;
}
Benchmark* Benchmark::Complexity(BigOFunc* complexity) {
imp_->Complexity(oLambda);
imp_->ComplexityLambda(complexity);
return this;
}
Benchmark* Benchmark::Threads(int t) {
imp_->Threads(t);
return this;
......@@ -849,6 +863,7 @@ void RunBenchmark(const benchmark::internal::Benchmark::Instance& b,
report.items_per_second = items_per_second;
report.complexity_n = total.complexity_n;
report.complexity = b.complexity;
report.complexity_lambda = b.complexity_lambda;
if(report.complexity != oNone)
complexity_reports.push_back(report);
}
......@@ -949,9 +964,9 @@ void State::SetLabel(const char* label) {
}
namespace internal {
namespace {
namespace {
void RunMatchingBenchmarks(const std::vector<Benchmark::Instance>& benchmarks,
void RunMatchingBenchmarks(const std::vector<Benchmark::Instance>& benchmarks,
BenchmarkReporter* reporter) {
CHECK(reporter != nullptr);
......@@ -982,9 +997,9 @@ void RunMatchingBenchmarks(const std::vector<Benchmark::Instance>& benchmarks,
RunBenchmark(benchmark, reporter, complexity_reports);
}
}
}
}
std::unique_ptr<BenchmarkReporter> GetDefaultReporter() {
std::unique_ptr<BenchmarkReporter> GetDefaultReporter() {
typedef std::unique_ptr<BenchmarkReporter> PtrType;
if (FLAGS_benchmark_format == "console") {
return PtrType(new ConsoleReporter);
......@@ -996,9 +1011,9 @@ std::unique_ptr<BenchmarkReporter> GetDefaultReporter() {
std::cerr << "Unexpected format: '" << FLAGS_benchmark_format << "'\n";
std::exit(1);
}
}
}
} // end namespace
} // end namespace
} // end namespace internal
size_t RunSpecifiedBenchmarks() {
......
......@@ -27,21 +27,21 @@
namespace benchmark {
// Internal function to calculate the different scalability forms
std::function<double(int)> FittingCurve(BigO complexity) {
BigOFunc* FittingCurve(BigO complexity) {
switch (complexity) {
case oN:
return [](int n) {return n; };
return [](size_t n) -> double {return n; };
case oNSquared:
return [](int n) {return n*n; };
return [](size_t n) -> double {return n * n; };
case oNCubed:
return [](int n) {return n*n*n; };
return [](size_t n) -> double {return n * n * n; };
case oLogN:
return [](int n) {return log2(n); };
return [](size_t n) {return log2(n); };
case oNLogN:
return [](int n) {return n * log2(n); };
return [](size_t n) {return n * log2(n); };
case o1:
default:
return [](int) {return 1; };
return [](size_t) {return 1.0; };
}
}
......@@ -49,19 +49,19 @@ std::function<double(int)> FittingCurve(BigO complexity) {
std::string GetBigOString(BigO complexity) {
switch (complexity) {
case oN:
return "* N";
return "N";
case oNSquared:
return "* N**2";
return "N^2";
case oNCubed:
return "* N**3";
return "N^3";
case oLogN:
return "* lgN";
return "lgN";
case oNLogN:
return "* NlgN";
return "NlgN";
case o1:
return "* 1";
return "(1)";
default:
return "";
return "f(N)";
}
}
......@@ -75,21 +75,9 @@ std::string GetBigOString(BigO complexity) {
// For a deeper explanation on the algorithm logic, look the README file at
// http://github.com/ismaelJimenez/Minimal-Cpp-Least-Squared-Fit
// This interface is currently not used from the oustide, but it has been
// provided for future upgrades. If in the future it is not needed to support
// Cxx03, then all the calculations could be upgraded to use lambdas because
// they are more powerful and provide a cleaner inferface than enumerators,
// but complete implementation with lambdas will not work for Cxx03
// (e.g. lack of std::function).
// In case lambdas are implemented, the interface would be like :
// -> Complexity([](int n) {return n;};)
// and any arbitrary and valid equation would be allowed, but the option to
// calculate the best fit to the most common scalability curves will still
// be kept.
LeastSq CalculateLeastSq(const std::vector<int>& n,
LeastSq MinimalLeastSq(const std::vector<int>& n,
const std::vector<double>& time,
std::function<double(int)> fitting_curve) {
BigOFunc* fitting_curve) {
double sigma_gn = 0.0;
double sigma_gn_squared = 0.0;
double sigma_time = 0.0;
......@@ -105,6 +93,7 @@ LeastSq CalculateLeastSq(const std::vector<int>& n,
}
LeastSq result;
result.complexity = oLambda;
// Calculate complexity.
result.coef = sigma_time_gn / sigma_gn_squared;
......@@ -144,19 +133,19 @@ LeastSq MinimalLeastSq(const std::vector<int>& n,
oLogN, oN, oNLogN, oNSquared, oNCubed };
// Take o1 as default best fitting curve
best_fit = CalculateLeastSq(n, time, FittingCurve(o1));
best_fit = MinimalLeastSq(n, time, FittingCurve(o1));
best_fit.complexity = o1;
// Compute all possible fitting curves and stick to the best one
for (const auto& fit : fit_curves) {
LeastSq current_fit = CalculateLeastSq(n, time, FittingCurve(fit));
LeastSq current_fit = MinimalLeastSq(n, time, FittingCurve(fit));
if (current_fit.rms < best_fit.rms) {
best_fit = current_fit;
best_fit.complexity = fit;
}
}
} else {
best_fit = CalculateLeastSq(n, time, FittingCurve(complexity));
best_fit = MinimalLeastSq(n, time, FittingCurve(complexity));
best_fit.complexity = complexity;
}
......@@ -256,14 +245,16 @@ std::vector<BenchmarkReporter::Run> ComputeBigO(
cpu_time.push_back(run.cpu_accumulated_time/run.iterations);
}
LeastSq result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity);
// result_cpu.complexity is passed as parameter to result_real because in case
// reports[0].complexity is oAuto, the noise on the measured data could make
// the best fit function of Cpu and Real differ. In order to solve this, we
// take the best fitting function for the Cpu, and apply it to Real data.
LeastSq result_real = MinimalLeastSq(n, real_time, result_cpu.complexity);
LeastSq result_cpu;
LeastSq result_real;
if (reports[0].complexity != oLambda) {
result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity);
result_real = MinimalLeastSq(n, real_time, result_cpu.complexity);
} else {
result_cpu = MinimalLeastSq(n, cpu_time, reports[0].complexity_lambda);
result_real = MinimalLeastSq(n, real_time, reports[0].complexity_lambda);
}
std::string benchmark_name = reports[0].benchmark_name.substr(0, reports[0].benchmark_name.find('/'));
// Get the data from the accumulator to BenchmarkReporter::Run's.
......
......@@ -26,15 +26,15 @@
namespace benchmark {
// Return a vector containing the mean and standard devation information for
// the specified list of reports. If 'reports' contains less than two
// non-errored runs an empty vector is returned
std::vector<BenchmarkReporter::Run> ComputeStats(
// Return a vector containing the mean and standard devation information for
// the specified list of reports. If 'reports' contains less than two
// non-errored runs an empty vector is returned
std::vector<BenchmarkReporter::Run> ComputeStats(
const std::vector<BenchmarkReporter::Run>& reports);
// Return a vector containing the bigO and RMS information for the specified
// list of reports. If 'reports.size() < 2' an empty vector is returned.
std::vector<BenchmarkReporter::Run> ComputeBigO(
// Return a vector containing the bigO and RMS information for the specified
// list of reports. If 'reports.size() < 2' an empty vector is returned.
std::vector<BenchmarkReporter::Run> ComputeBigO(
const std::vector<BenchmarkReporter::Run>& reports);
// This data structure will contain the result returned by MinimalLeastSq
......@@ -60,11 +60,5 @@ struct LeastSq {
// Function to return an string for the calculated complexity
std::string GetBigOString(BigO complexity);
// Find the coefficient for the high-order term in the running time, by
// minimizing the sum of squares of relative error.
LeastSq MinimalLeastSq(const std::vector<int>& n,
const std::vector<double>& time,
const BigO complexity = oAuto);
} // end namespace benchmark
#endif // COMPLEXITY_H_
......@@ -90,8 +90,8 @@ void ConsoleReporter::PrintRunData(const Run& result) {
const double cpu_time = result.GetAdjustedCPUTime();
if(result.report_big_o) {
std::string big_o = result.report_big_o ? GetBigOString(result.complexity) : "";
ColorPrintf(Out, COLOR_YELLOW, "%10.4f %s %10.4f %s ",
std::string big_o = GetBigOString(result.complexity);
ColorPrintf(Out, COLOR_YELLOW, "%10.2f %s %10.2f %s ",
real_time, big_o.c_str(), cpu_time, big_o.c_str());
} else if(result.report_rms) {
ColorPrintf(Out, COLOR_YELLOW, "%10.0f %% %10.0f %% ",
......
......@@ -13,6 +13,7 @@
// limitations under the License.
#include "benchmark/reporter.h"
#include "complexity.h"
#include <cstdint>
#include <algorithm>
......@@ -87,8 +88,10 @@ void CSVReporter::PrintRunData(const Run & run) {
Out << run.GetAdjustedRealTime() << ",";
Out << run.GetAdjustedCPUTime() << ",";
// Do not print timeLabel on RMS report
if(!run.report_rms) {
// Do not print timeLabel on bigO and RMS report
if(run.report_big_o) {
Out << GetBigOString(run.complexity);
} else if(!run.report_rms){
Out << GetTimeUnitString(run.time_unit);
}
Out << ",";
......
......@@ -13,6 +13,7 @@
// limitations under the License.
#include "benchmark/reporter.h"
#include "complexity.h"
#include <cstdint>
#include <algorithm>
......@@ -132,15 +133,29 @@ void JSONReporter::PrintRunData(Run const& run) {
out << indent
<< FormatKV("iterations", run.iterations)
<< ",\n";
}
out << indent
<< FormatKV("real_time", RoundDouble(run.GetAdjustedRealTime()))
<< ",\n";
out << indent
<< FormatKV("cpu_time", RoundDouble(run.GetAdjustedCPUTime()));
if(!run.report_rms) {
out << ",\n" << indent
<< FormatKV("time_unit", GetTimeUnitString(run.time_unit));
} else if(run.report_big_o) {
out << indent
<< FormatKV("cpu_coefficient", RoundDouble(run.GetAdjustedCPUTime()))
<< ",\n";
out << indent
<< FormatKV("real_coefficient", RoundDouble(run.GetAdjustedRealTime()))
<< ",\n";
out << indent
<< FormatKV("big_o", GetBigOString(run.complexity))
<< ",\n";
out << indent
<< FormatKV("time_unit", GetTimeUnitString(run.time_unit));
} else if(run.report_rms) {
out << indent
<< FormatKV("rms", RoundDouble(run.GetAdjustedCPUTime()*100))
<< "%";
}
if (run.bytes_per_second > 0.0) {
out << ",\n" << indent
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment