common/measurement.c
///////////////////////////////////////////////////////////////////////////////
// Filename: measurement.c
///////////////////////////////////////////////////////////////////////////////
// Purpose: implements class "measurement" that makes it easier to measure
// performances
///////////////////////////////////////////////////////////////////////////////
// History:
// ========
//
// Date Time Name Description
// -------- -------- -------- ------------------------------------------------
// 96/02/28 22:59:21 muellerg: created
// 96/02/29 11:21:19 muellerg: histogram functions added
//
///////////////////////////////////////////////////////////////////////////////
// Feature test switches ///////////////////////////// Feature test switches //
/* NONE */
// System headers /////////////////////////////////////////// System headers //
#include <stdlib.h>
#include <string.h>
// Local headers ///////////////////////////////////////////// Local headers //
#include "../common.h"
// Macros /////////////////////////////////////////////////////////// Macros //
/* NONE */
// File scope objects /////////////////////////////////// File scope objects //
/* NONE */
// External variables, functions, and classes ///////////// External objects //
/* NONE */
// Signal catching functions ///////////////////// Signal catching functions //
/* NONE */
// Structures, unions, and class definitions /////////////////// Definitions //
/* NONE */
// Functions and class implementation /// Functions and class implementation //
/* NONE */
/*
* create measurement object that can handle up to
* max_number_of_measurements measurements
*
* the log file name is derived from name
*/
measurement::measurement(char *name, char *ext, int max_number_of_measurements)
{
logbasename = name; // set logfile name
logbasenameext = ext; // and extension
max_number = max_number_of_measurements; // maximum number of
// measurements
actual = 0; // next extry to write to
data = new measurement_data[max_number]; // get buffer data
if(!data)
error.panic("measurement::measurement(): out of memory");
}
/*
* destuctor: delete buffer if we have one
*/
measurement::~measurement(void)
{
if(data)
delete[] data;
}
/*
* start a mesurement as fast as possible
*/
void
measurement::start(int buffersize, int how_often, char *comment)
{
if(actual < max_number)
{
data[actual].buffersize = buffersize;
data[actual].how_often = how_often;
data[actual].comment = comment;
cput.start();
clockt.start();
}
else
error.panic("measurement::start(): buffer overflow");
}
/*
* end a measurement and copy the data to an internal buffer as fast as
* possible
*/
void
measurement::end(void)
{
cput.end();
clockt.end();
data[actual].cpu_secs = cput.secs();
data[actual].cpu_usecs = cput.usecs();
data[actual].clock_secs = clockt.secs();
data[actual].clock_usecs = clockt.usecs();
actual++;
}
/*
* reset measurements
*/
void
measurement::reset(void)
{
actual = 0;
}
/*
* write data out to logfile in human readable format
*/
void
measurement::writeout_logfile(bool write_kb_per_sec,
bool write_ops_per_sec,
bool use_cout)
{
// write out human readable logfile
write_logfile(LOGFILE_EXTENSION, true, write_kb_per_sec,
write_ops_per_sec, use_cout);
}
/*
* write data out for use with other programs (e.g. gnuplot)
*/
void
measurement::writeout_plain_logfile(bool write_kb_per_sec,
bool write_ops_per_sec)
{
// write out human readable logfile
write_logfile(COMPLOGFILE_EXTENSION, false, write_kb_per_sec,
write_ops_per_sec, false);
}
/*
* write logfile with extension
*/
void
measurement::write_logfile(char *extension, bool verbal,
bool write_kb_per_sec, bool write_ops_per_sec, bool use_cout)
{
ostream *out=0;
char *namebuffer = NULL;
ofstream out_f;
if(use_cout)
out = &cout;
else
{
int bufferlen = strlen(logbasename) +
strlen(logbasenameext) +
strlen(extension) +2;
namebuffer = new char[bufferlen];
// construct file name
strcpy(namebuffer, logbasename);
strcat(namebuffer, ".");
strcat(namebuffer, logbasenameext);
strcat(namebuffer, extension);
out_f.open(namebuffer);
if(out_f)
out = &out_f;
}
if(out)
{
// file is available
if( (!verbal) && write_kb_per_sec && write_ops_per_sec)
error.panic("measurement: can't define write_ops and write_kb"
" if !verbal");
// write only as much data out as we have
for(int i = 0; i < actual; i++)
{
// compute time
float clockt_usecs = data[i].clock_secs*1000000 +
data[i].clock_usecs;
float cput_usecs = data[i].cpu_secs*1000000 +
data[i].cpu_usecs;
if(verbal)
{
if(data[i].comment)
*out << data[i].comment << endl;
if(data[i].buffersize != -1)
{
*out << "Result for buffersize "
<< data[i].buffersize << " (";
}
*out << "average over "
<< data[i].how_often << " operations";
if(data[i].buffersize != -1)
*out << "):";
*out << endl;
}
if(!verbal)
{
if(data[i].buffersize != -1)
*out << data[i].buffersize << " ";
}
if(write_kb_per_sec)
{
if(verbal)
*out << "kb/s: ";
*out << (float) ( (float)(data[i].buffersize *
data[i].how_often) /
(clockt_usecs/1000000.0)) / 1024.0;
if(write_ops_per_sec)
*out << " ";
else
*out << endl;
}
if(write_ops_per_sec)
{
if(verbal)
*out << "number of operations per second: ";
*out << (float) ( (float)(data[i].how_often) /
(clockt_usecs/1000000.0) ) << endl;
}
if(verbal)
*out << "clock secs:"
<< (clockt_usecs/((float)1000000))/(float)data[i].how_often
<< "(="
<< clockt_usecs/(float)data[i].how_often
<< " usecs)"
<< " cpu secs:"
<< (cput_usecs/((float)1000000))/(float)data[i].how_often
<< "(="
<< cput_usecs/(float)data[i].how_often
<< " usecs)"
<< endl << endl;
} // for
}
else
{
if(use_cout)
error.warning("cout error - can't write output");
else
error.warning("cant open file %s for writing", namebuffer);
exit(EXIT_FAILURE);
}
if(namebuffer)
delete[] namebuffer;
}
/*
* write out histogram (for applications where only the time measurement
* matter) -- verbose version
*/
void
measurement::writeout_histogram(bool use_cout)
{
write_histogram(true, use_cout);
}
/*
* write out histogram (for applications where only the time measurement
* matter) -- data (gnuplot) version
*/
void
measurement::writeout_plain_histogram(void)
{
write_histogram(false, false);
}
/*
* write out histogram (for applications where only the time measurement
* matter) -- generic version
*
* verbose: output min, max, average
* !verbose: output raw data (numerated from 1 to actual)
*/
void
measurement::write_histogram(bool verbal, bool use_cout)
{
ostream *out=0;
char *namebuffer = NULL;
ofstream out_f;
if(use_cout)
out = &cout;
else
{
int bufferlen = strlen(logbasename) +
strlen(logbasenameext) +
max(strlen(COMPLOGFILE_EXTENSION),
strlen(COMPLOGFILE_EXTENSION)) + 2;
namebuffer = new char[bufferlen];
// construct file name
strcpy(namebuffer, logbasename);
strcat(namebuffer, ".");
strcat(namebuffer, logbasenameext);
if(verbal)
strcat(namebuffer, LOGFILE_EXTENSION);
else
strcat(namebuffer, COMPLOGFILE_EXTENSION);
out_f.open(namebuffer);
if(out_f)
out = &out_f;
}
if(out)
{
// file is available
// write only as much data out as we have
float min_clockt, max_clockt;
float min_cput, max_cput;
float sum_clockt, sum_cput;
// initialize
min_clockt = max_clockt = data[0].clock_secs*1000000 +
data[0].clock_usecs;
min_cput = max_cput = data[0].cpu_secs*1000000 +
data[0].cpu_usecs;
sum_clockt = sum_cput = 0;
for(int i = 0; i < actual; i++)
{
// compute time
float clockt_usecs = data[i].clock_secs*1000000 +
data[i].clock_usecs;
float cput_usecs = data[i].cpu_secs*1000000 +
data[i].cpu_usecs;
// update statistics
sum_clockt += clockt_usecs;
sum_cput += cput_usecs;
// maximum numbers
if(clockt_usecs > max_clockt)
max_clockt = clockt_usecs;
if(cput_usecs > max_cput)
max_cput = cput_usecs;
// minimum numbers
if(clockt_usecs < min_clockt)
min_clockt = clockt_usecs;
if(cput_usecs < min_cput)
min_cput = cput_usecs;
// output raw data
if(!verbal)
*out << i+1 << " " << clockt_usecs << endl;
} // for
if(verbal)
{
// display comment of first measurement if we have one
if(data[0].comment)
*out << data[0].comment << endl;
// average time
*out << "Average time: clock secs: "
<< ((sum_clockt/(float)actual)/1000000.0)/(float)actual
<< "(=" << sum_clockt/(float)actual
<< " usecs)"
<< " cpu secs: "
<< ((sum_cput/(float)actual)/1000000.0)/(float)actual
<< "(=" << sum_cput/(float)actual
<< " usecs)"
<< endl;
// minimum time
*out << "Minimum time: clock secs: "
<< min_clockt/1000000.0
<< "(=" << min_clockt
<< " usecs)"
<< " cpu secs: "
<< min_cput/1000000.0
<< "(=" << min_cput
<< " usecs)"
<< endl;
// maximum time
*out << "Maximum time: clock secs: "
<< max_clockt/1000000.0
<< "(=" << max_clockt
<< " usecs)"
<< " cpu secs: "
<< max_cput/1000000.0
<< "(=" << max_cput
<< " usecs)"
<< endl;
} // verbal
}
else
{
if(use_cout)
error.warning("cout error - can't write output");
else
error.warning("cant open file %s for writing", namebuffer);
exit(EXIT_FAILURE);
}
if(namebuffer)
delete[] namebuffer;
}
// Main /////////////////////////////////////////////////////////////// Main //
/* NONE */