Commit 7e7ce9f8 authored by Elmar Ritsch's avatar Elmar Ritsch Committed by Graeme Stewart
Browse files

Adding a 'count' parameter to CustomBenchmarkGuard, this allows to make one...

Adding a 'count' parameter to CustomBenchmarkGuard, this allows to make one time measurement account for multiple events of interest. (PmbCxxUtils-00-00-03)
parent 6850588b
/*
Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
*/
////////////////////////////////////////////////////////////////
// //
// Header file for class CustomBenchmark //
// //
// Description: Helper class for performing custom //
// bench-marking of CPU. This is a simple //
// implementation without support for nested //
// bench-markings (i.e. a call to begin(..) //
// must always be followed by a call to end(), //
// never another call to begin(..)). //
// //
// Author: Thomas H. Kittelmann (Thomas.Kittelmann@cern.ch) //
// Initial version: January 2012 //
// //
////////////////////////////////////////////////////////////////
#ifndef CUSTOMBENCHMARK_H
#define CUSTOMBENCHMARK_H
#include <ctime>
#include "stdint.h"//<cstdint>
#include <cassert>
#include <limits>
namespace PMonUtils {
class CustomBenchmark {
public:
CustomBenchmark(unsigned nmax);
~CustomBenchmark();
//For benchmarking
void begin(unsigned id);
void end(int count);
//For accessing the results:
void getData(unsigned id, uint64_t& count, double& time_ms) const;
unsigned nMax() const { return m_nmax; }
unsigned size() const { return m_nmax; }
private:
struct Data {
Data() {}
void init() { time_spent = 0; time_at_begin = 0; count = 0; }
int64_t time_spent;
int64_t time_at_begin;
unsigned count;
};
const unsigned m_nmax;
Data * m_data;
Data * m_data_current;
// It is illegal to copy/assign a CustomBenchmark:
CustomBenchmark( const CustomBenchmark & );
CustomBenchmark & operator= ( const CustomBenchmark & );
//A non-overflowing "clock()":
static int64_t clock_nooverflow();
};
inline void CustomBenchmark::begin(unsigned id)
{
assert(id<m_nmax);
m_data_current = &(m_data[id]);
m_data_current->time_at_begin = clock_nooverflow();
}
inline void CustomBenchmark::end(int count=1)
{
assert(m_data_current);
m_data_current->count += count;
m_data_current->time_spent += clock_nooverflow() - m_data_current->time_at_begin;
m_data_current=0;
}
inline void CustomBenchmark::getData(unsigned id, uint64_t& count, double& time_ms) const
{
assert(id<m_nmax);
Data * data = &(m_data[id]);
count = data->count;
time_ms = data->time_spent * (1000.0/CLOCKS_PER_SEC);
}
inline int64_t CustomBenchmark::clock_nooverflow() {
//Copied from PerfMonComps/trunk/src/SemiDetMisc.h
//
//In gnu, clock_t is a long and CLOCKS_PER_SECOND 1000000 so clock()
//will overflow in 32bit builds after a few thousands of seconds. To
//avoid this, we have the following method instead which notices when
//overflow occurs and corrects for it (it won't notice if it doesn't
//get called for >4000s, but this should be ok for almost all of our
//use cases):
assert(std::numeric_limits<clock_t>::is_integer);
if (sizeof(clock_t)>=sizeof(int64_t))
return clock();//64bit builds shouldn't have overflow issues.
//not so clean with statics i guess:
static clock_t last=clock();
static int64_t offset=0;
clock_t c=clock();
if (c<last)
offset+=int64_t(std::numeric_limits<unsigned>::max())-int64_t(std::numeric_limits<unsigned>::min());
last=c;
return offset+c;
}
//A class which makes for instance makes it easier to guarantee that
//an exception in the benchmarked code will still result in
//CustomBenchmark::end() will be called properly. Will do nothing if
//called with a null CustomBenchmark*:
class CustomBenchmarkGuard
{
public:
CustomBenchmarkGuard(CustomBenchmark* cb, unsigned id, int count=1) : m_cb(cb), m_count(count) { if (m_cb) m_cb->begin(id); }
~CustomBenchmarkGuard() { if (m_cb) m_cb->end(m_count); }
private:
CustomBenchmark * m_cb;
int m_count;
};
}
#endif
package PmbCxxUtils
author Thomas Kittelmann <thomas.kittelmann@cern.ch>
use AtlasPolicy AtlasPolicy-*
library PmbCxxUtils *.cxx
apply_pattern installed_library
#!/bin/bash
g++ custombenchmark_example.cxx -I.. -lPmbCxxUtils -L../$CMTCONFIG -o custombenchmark_example
/*
Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
*/
#include "PmbCxxUtils/CustomBenchmark.h"
#include <algorithm>
#include <iostream>
#include <cstdlib>
#include <cmath>
//Example: Assume that we are bench-marking some simulation code, and
//we would like to know time spent as a function of pdg (just the
//absolute pdg value, and any |pdg|>99 we wrap to 99).
int main(int,char**)
{
srand(0);
const unsigned maxpdgrecord=99;
PMonUtils::CustomBenchmark bench(maxpdgrecord+1);//Create bench object, allowing to track 100 unique ids (0..99).
double dummy(17.0);
for (unsigned i=0;i<100;++i) {
int pdg = -150+(rand()%30)*10;
int id = std::min<unsigned>(maxpdgrecord,abs(pdg));//map pdg to a number in 0..99
bench.begin(id);
//Fake work begin
for (unsigned j=0;j<3000*(abs(pdg)+1);++j)
dummy = sin(dummy);
//Fake work end
bench.end();
}
//Exactly the same loop once again, to show how to use the Guard mini helper:
for (unsigned i=0;i<100;++i) {
int pdg = -150+(rand()%30)*10;
int id = std::min<unsigned>(maxpdgrecord,abs(pdg));//map pdg to a number in 0..99
{
PMonUtils::CustomBenchmarkGuard cbg(&bench,id);
//Fake work begin
for (unsigned j=0;j<3000*(abs(pdg)+1);++j)
dummy = sin(dummy);//Even if an exception is thrown here, the destructor of the cbg object will invoke bench.end() for us.
//Fake work end
}
}
//Print out the results:
for (unsigned pdg = 0; pdg<=maxpdgrecord; ++pdg) {
uint64_t count; double time_ms;
bench.getData(pdg, count, time_ms);
if (count)//Only report on those encountered at least once
std::cout<< "Work done on particle with |pdg|="<<pdg<<(pdg==maxpdgrecord?"+":"")
<< " was done "<<count<<" times and took a total of "<<time_ms
<<" milliseconds [on average "<<time_ms/count<<" ms each time]"<<std::endl;
}
}
/*
Copyright (C) 2002-2017 CERN for the benefit of the ATLAS collaboration
*/
#include "PmbCxxUtils/CustomBenchmark.h"
namespace PMonUtils {
CustomBenchmark::CustomBenchmark(unsigned nmax)
: m_nmax(nmax),
m_data(new Data[nmax]),
m_data_current(0)
{
Data * itE = &(m_data[m_nmax]);
for (Data * it = m_data; it!=itE; ++it)
it->init();
}
CustomBenchmark::~CustomBenchmark()
{
delete[] m_data;
}
}
Supports Markdown
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