panthema / 2008 / 0714-cryptography-speedtest-comparison / crypto-speedtest-0.1 / src / speedtest.h (Download File)
// $Id: speedtest.h 223 2008-04-07 16:56:58Z tb $
// Not really an include file, more a base file for the speed tests.

#include <assert.h>
#include <stdint.h>
#include <math.h>
#include <time.h>

#include <vector>
#include <map>
#include <fstream>
#include <iostream>
#include <iomanip>
#include <numeric>
#include <cmath>
#include <algorithm>
#include <limits>

// *** Speedtest Parameters ***

// speed test different buffer sizes in this range
const unsigned int buffermin = 16;
const unsigned int buffermax = 16 * 65536;
const unsigned int baserepeatsize = 65536;
const unsigned int minrepeats = 4;
const unsigned int measureruns = 16;

#ifndef _MSC_VER
#include <sys/time.h>
#else
#include <windows.h>
#endif

/// Time is measured using gettimeofday() or GetTickCount()
inline double timestamp()
{
#ifndef _MSC_VER
    struct timeval tv;
    gettimeofday(&tv, NULL);
    return tv.tv_sec + tv.tv_usec * 0.000001;
#else
    return timeGetTime() / 1000.0;
#endif
}

// *** Global Buffers and Settings for the Speedtest Functions ***

unsigned char	enckey[32];	/// 256 bit encryption key
unsigned char	enciv[16];	/// 16 byte initialization vector if needed.

unsigned char	buffer[buffermax];	/// encryption buffer
unsigned int	bufferlen;		/// currently tested buffer length

// *** run_test() ***

/**
 * This function will run a test routine multiple times with different buffer
 * sizes configured. It measures the time required to encrypt a number of
 * bytes. The average time and standard deviation are calculated and written to
 * a log file for gnuplot.
 */

template <void (*testfunc)()>
void run_test(const char* logfile)
{
    std::cout << "Speed testing for " << logfile << "\n";

    // Save the time required for each run.
    std::map<unsigned int, std::vector<double> > timelog;
	
    for(unsigned int fullrun = 0; fullrun < measureruns; ++fullrun)
    {
        unsigned int repeatsize = baserepeatsize;

	for(unsigned int bufflen = buffermin; bufflen <= buffermax; bufflen *= 2)
	{
REDO:
	    // because small time measurements are inaccurate, repeat very fast
	    // tests until the same amount of data is encrypted as in the large
	    // tests.
	    unsigned int repeat = repeatsize / bufflen;
	    if (repeat < minrepeats) repeat = minrepeats;

	    // std::cout << "Test: bufflen " << bufflen << " repeat " << repeat << "\n";

	    bufferlen = bufflen;

	    // fill buffer
	    for(unsigned int i = 0; i < bufferlen; ++i)
		buffer[i] = (uint8_t)i;

	    double ts1 = timestamp();

	    for(unsigned int testrun = 0; testrun < repeat; ++testrun)
	    {
		testfunc();
	    }

	    double ts2 = timestamp();

	    double tsdelta = ts2 - ts1;

	    // Adapt number of repetitions to the clock's accuracy.
	    if (tsdelta < 0.5) {
       		printf("Run %d bufferlen %d repeat %d took only %.2f sec. Increasing repetitons.\n",
		       fullrun, bufferlen, repeat, tsdelta);
		repeatsize *= 2;
		goto REDO;
	    }
	    else {
       		printf("Run %d bufferlen %d repeat %d took %.2f sec.\n",
		       fullrun, bufferlen, repeat, tsdelta);
	    }

	    // check buffer status after repeated en/decryption
	    for(unsigned int i = 0; i < bufferlen; ++i)
		assert(buffer[i] == (uint8_t)i);

	    timelog[bufferlen].push_back( tsdelta / (double)repeat );
	}
    }

    // Calculate and output statistics.
    std::ofstream of (logfile);

    // First output time absolute measurements
    for(std::map<unsigned int, std::vector<double> >::const_iterator ti = timelog.begin();
	ti != timelog.end(); ++ti)
    {
	const std::vector<double>& timelist = ti->second;

	double average = std::accumulate(timelist.begin(), timelist.end(), 0.0) / timelist.size();

	double variance = 0.0;
	for(unsigned int i = 0; i < timelist.size(); ++i)
	{
	    variance += (timelist[i] - average) * (timelist[i] - average);
	}
	variance = variance / (timelist.size() - 1);

	double stddev = std::sqrt(variance);

	if (timelist.size() == 1) { // only one run -> no variance or stddev
	    variance = stddev = 0.0;
	}

	double vmin = *std::min_element(timelist.begin(), timelist.end());
	double vmax = *std::max_element(timelist.begin(), timelist.end());

	of << std::setprecision(16);
	of << ti->first << " " << average << " " << stddev << " " << vmin << " " << vmax << "\n";
    }
    of << "\n\n";

    // Second output speed measurements
    for(std::map<unsigned int, std::vector<double> >::const_iterator ti = timelog.begin();
	ti != timelog.end(); ++ti)
    {
	const std::vector<double>& timelist = ti->second;

	double average = 0.0;
	double vmin = std::numeric_limits<double>::infinity();
	double vmax = 0;

	for(unsigned int i = 0; i < timelist.size(); ++i)
	{
	    average += (double)ti->first / timelist[i];
	    vmin = std::min<double>(vmin, (double)ti->first / timelist[i]);
	    vmax = std::max<double>(vmax, (double)ti->first / timelist[i]);
	}
	average /= timelist.size();

	double variance = 0.0;
	for(unsigned int i = 0; i < timelist.size(); ++i)
	{
	    double delta = ((double)ti->first / timelist[i]) - average;
	    variance += delta * delta;
	}
	variance = variance / (timelist.size() - 1);

	double stddev = std::sqrt(variance);

	if (timelist.size() == 1) { // only one run -> no variance or stddev
	    variance = stddev = 0.0;
	}

	of << std::setprecision(16);
	of << ti->first << " " << average << " " << stddev << " " << vmin << " " << vmax << "\n";
    }
    of.close();
}