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