2025-04-27 07:49:33 -04:00

132 lines
3.0 KiB
C++

// Measurement harness based on Andrew Koenig JOOP article
// it repeats timing loops until it is (statistically) convinced
// that a result is meaningful
#include <vector>
#include <assert.h>
#include <algorithm>
#include <ctime>
template <typename F>
class perf_measure
{
F f_;
std::vector<double> mv;
double ovhd;
// We'd like the odds to be factor:1 that the result is
// within percent% of the median
const int factor;// = 10;
const int percent;// = 20;
public:
perf_measure(F f):f_(f),factor(10),percent(20),ovhd(clock_overhead()){};
// How many clock units does it take to interrogate the clock?
double clock_overhead()
{
clock_t k = clock(), start, limit;
// Wait for the clock to tick
do start = clock();
while (start == k);
// interrogate the clock until it has advanced at least a second
// (for reasonable accuracy)
limit = start + CLOCKS_PER_SEC;
unsigned long r = 0;
while ((k = clock()) < limit)
++r;
return double(k - start) / r;
}
// Measure a function (object) factor*2 times,
// appending the measurements to the second argument
void measure_aux()
{
// Ensure we don't reallocate in mid-measurement
mv.reserve(mv.size() + factor*2);
// Wait for the clock to tick
clock_t k = clock();
clock_t start;
do start = clock();
while (start == k);
// Do 2*factor measurements
for (int i = 2*factor; i; --i) {
unsigned long count = 0, limit = 1, tcount = 0;
const clock_t clocklimit = start + CLOCKS_PER_SEC/100;
clock_t t;
do {
while (count < limit) {
f_();
++count;
}
limit *= 2;
++tcount;
} while ((t = clock()) < clocklimit);
// Wait for the clock to tick again;
clock_t t2;
do ++tcount;
while ((t2 = clock()) == t);
// Append the measurement to the vector
mv.push_back(((t2 - start) - (tcount * ovhd)) / count);
// Establish a new starting point
start = t2;
}
}
// Returns the number of clock units per iteration
// With odds of factor:1, the measurement is within percent% of
// the value returned, which is also the median of all measurements.
double measure()
{
int n = 0; // iteration counter
double median = 0;
do {
++n;
// Try 2*factor measurements
measure_aux();
assert(mv.size() == 2*n*factor);
// Compute the median. We know the size is even, so we cheat.
std::sort(mv.begin(), mv.end());
double median = (mv[n*factor] + mv[n*factor-1])/2;
//double median = 0;
// for(std::vector<double>::const_iterator it = mv.begin();it!=mv.end();++it)
// median+=*it;
// median = median/mv.size();
// If the extrema are within threshold of the median, we're done
if (mv[n] > (median * (100-percent))/100 &&
mv[mv.size() - n - 1] < (median * (100+percent))/100)
return median;
} while (mv.size() < factor * 200);
return -median;
}
};
template <typename F>
double measure(F f)
{
perf_measure<F> t(f);
return t.measure();
};