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 */