benchmarks: add option --min-duration=SECONDS

- with this option test_runner() will be called repeatedly (doing the
  given number of --iterations each time) until SECONDS have elapsed

- added output column showing average time per iteration, should help
  when comparing different runs

- shrinked the # of iterations column using k/M suffixes
This commit is contained in:
Mickey Rose 2016-02-17 01:30:32 +01:00
parent 2f303e4da4
commit 8762e1d034

View file

@ -39,6 +39,10 @@ public:
{
return iterations_;
}
mapnik::parameters const& params() const
{
return params_;
}
virtual bool validate() const = 0;
virtual bool operator()() const = 0;
virtual ~test_case() {}
@ -138,52 +142,82 @@ int run(T const& test_runner, std::string const& name)
if (!test_runner.validate())
{
std::clog << "test did not validate: " << name << "\n";
return -1;
return 1;
}
// run test once before timing
// if it returns false then we'll abort timing
if (test_runner())
if (!test_runner())
{
std::chrono::high_resolution_clock::time_point start;
std::chrono::high_resolution_clock::duration elapsed;
return 2;
}
if (test_runner.threads() > 0)
std::chrono::high_resolution_clock::time_point start;
std::chrono::high_resolution_clock::duration elapsed;
auto opt_min_duration = test_runner.params().template get<double>("min-duration", 0.0);
std::chrono::duration<double> min_seconds(*opt_min_duration);
auto min_duration = std::chrono::duration_cast<decltype(elapsed)>(min_seconds);
std::size_t loops = 0;
if (test_runner.threads() > 0)
{
using thread_group = std::vector<std::unique_ptr<std::thread> >;
using value_type = thread_group::value_type;
thread_group tg;
for (std::size_t i=0;i<test_runner.threads();++i)
{
using thread_group = std::vector<std::unique_ptr<std::thread> >;
using value_type = thread_group::value_type;
thread_group tg;
for (std::size_t i=0;i<test_runner.threads();++i)
{
tg.emplace_back(new std::thread(test_runner));
}
start = std::chrono::high_resolution_clock::now();
std::for_each(tg.begin(), tg.end(), [](value_type & t) {if (t->joinable()) t->join();});
elapsed = std::chrono::high_resolution_clock::now() - start;
tg.emplace_back(new std::thread(test_runner));
}
else
{
start = std::chrono::high_resolution_clock::now();
start = std::chrono::high_resolution_clock::now();
std::for_each(tg.begin(), tg.end(), [](value_type & t) {if (t->joinable()) t->join();});
elapsed = std::chrono::high_resolution_clock::now() - start;
loops = 1;
}
else
{
start = std::chrono::high_resolution_clock::now();
do {
test_runner();
elapsed = std::chrono::high_resolution_clock::now() - start;
}
char msg[200];
std::snprintf(msg, sizeof(msg),
"%-43s %3zu threads %9zu iters %6.0f milliseconds",
name.c_str(),
test_runner.threads(),
test_runner.iterations(),
std::chrono::duration<double, std::milli>(elapsed).count());
std::clog << msg << "\n";
++loops;
} while (elapsed < min_duration);
}
double iters = loops * test_runner.iterations();
double dur_total = std::chrono::duration<double, std::milli>(elapsed).count();
double dur_avg = dur_total / iters;
char iters_unit = ' ';
char msg[200];
if (iters >= 1e7) iters *= 1e-6, iters_unit = 'M';
else if (iters >= 1e4) iters *= 1e-3, iters_unit = 'k';
std::snprintf(msg, sizeof(msg),
"%-43s %3zu threads %4.0f%c iters %6.0f milliseconds",
name.c_str(),
test_runner.threads(),
iters, iters_unit,
dur_total);
std::clog << msg;
// log average time per iteration, currently only for non-threaded runs
if (test_runner.threads() == 0)
{
char unit = 'm';
if (dur_avg < 1e-5) dur_avg *= 1e+9, unit = 'p';
else if (dur_avg < 1e-2) dur_avg *= 1e+6, unit = 'n';
else if (dur_avg < 1e+1) dur_avg *= 1e+3, unit = 'u';
std::snprintf(msg, sizeof(msg), " %4.0f%cs/iter", dur_avg, unit);
std::clog << msg;
}
std::clog << "\n";
return 0;
}
catch (std::exception const& ex)
{
std::clog << "test runner did not complete: " << ex.what() << "\n";
return -1;
return 4;
}
return 0;
}
struct sequencer