hashTimeLibCrypt.cpp   [plain text]


/*
 * hashTimeLibCrypt.cpp - measure performance of libcrypt digest ops.
 * 
 * Thjis is obsolete; hashTime does this a lot better,a dn it also measures raw
 * CommonCrypto and CryptKit versions. 
 */
 
#include <openssl/sha.h>
#include <openssl/md5.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "cputime.h"
#include "cspwrap.h"
#include "common.h"
#include "pbkdDigest.h"

/* enumerate digest algorithms our way */
typedef int HT_Alg;
enum {
	HA_MD5 = 0,
	HA_SHA1
};

#define FIRST_ALG	HA_MD5
#define LAST_ALG	HA_SHA1

static void usage(char **argv)
{
    printf("Usage: %s [option ...]\n", argv[0]);
    printf("Options:\n");
	printf("   t=testspec; default=all\n");
	printf("     test specs: c digest context setup/teardown\n");
	printf("                 b basic single block digest\n");
	printf("                 d digest lots of data\n");
	printf("   l=loops (only valid if testspec is given)\n");
	exit(1);
}

/* passed to each test */	
typedef struct {
	unsigned			loops;
	bool				isSha;
} TestParams;

/* just digest context setup/teardown */
/* returns nonzero on error */
static int hashContext(
	TestParams	*params)
{
	unsigned 		loop;
	CPUTime 		startTime;
	double			timeSpentMs;
	DigestCtx		ctx;
	int				rtn;
	
	startTime = CPUTimeRead();
	for(loop=0; loop<params->loops; loop++) {
		rtn = DigestCtxInit(&ctx, params->isSha);
		if(!rtn) {
			return -1;
		}
	}
	timeSpentMs = CPUTimeDeltaMs(startTime, CPUTimeRead());

	printf("   context setup/delete : %u ops in %.2f ms; %f ms/op\n",
		params->loops, timeSpentMs, timeSpentMs / (double)params->loops);
	return 0;
}

/* Minimal init/digest/final */
#define BASIC_BLOCK_SIZE	64		// to digest in bytes
#define MAX_DIGEST_SIZE		20		// we provide, no malloc below CSSM

static int hashBasic(
	TestParams	*params)
{
	unsigned 		loop;
	CPUTime 		startTime;
	double			timeSpentMs;
	uint8			ptext[BASIC_BLOCK_SIZE];
	uint8			digest[MAX_DIGEST_SIZE];
	DigestCtx		ctx;
	int 			rtn;
	
	/* random data, const thru the loops */
	appGetRandomBytes(ptext, BASIC_BLOCK_SIZE);
	
	/* start critical timing loop */
	startTime = CPUTimeRead();
	for(loop=0; loop<params->loops; loop++) {
		rtn = DigestCtxInit(&ctx, params->isSha);
		if(!rtn) {
			return -1;
		}
		rtn = DigestCtxUpdate(&ctx, ptext, BASIC_BLOCK_SIZE);
		if(!rtn) {
			return -1;
		}
		rtn = DigestCtxFinal(&ctx, digest);
		if(!rtn) {
			return -1;
		}
	}
	DigestCtxFree(&ctx);
	timeSpentMs = CPUTimeDeltaMs(startTime, CPUTimeRead());
	printf("   Digest one %u byte block : %u ops in %.2f ms; %f ms/op\n",
		BASIC_BLOCK_SIZE, params->loops, 
		timeSpentMs, timeSpentMs / (double)params->loops);
	return 0;
}

/* Lots of data */
#define PTEXT_SIZE			1000		// to digest in bytes
#define INNER_LOOPS			1000

static int hashDataRate(
	TestParams	*params)
{
	unsigned 		loop;
	unsigned		iloop;
	CPUTime 		startTime;
	double			timeSpent, timeSpentMs;
	uint8			ptext[PTEXT_SIZE];
	uint8			digest[MAX_DIGEST_SIZE];
	DigestCtx		ctx;
	int				rtn;
	
	/* random data, const thru the loops */
	appGetRandomBytes(ptext, PTEXT_SIZE);
	
	/* start critical timing loop */
	startTime = CPUTimeRead();
	for(loop=0; loop<params->loops; loop++) {
		rtn = DigestCtxInit(&ctx, params->isSha);
		if(!rtn) {
			return -1;
		}
		for(iloop=0; iloop<INNER_LOOPS; iloop++) {
			rtn = DigestCtxUpdate(&ctx, ptext, PTEXT_SIZE);
			if(!rtn) {
				return -1;
			}
		}
		rtn = DigestCtxFinal(&ctx, digest);
		if(!rtn) {
			return -1;
		}
	}
	timeSpentMs = CPUTimeDeltaMs(startTime, CPUTimeRead());
	timeSpent = timeSpentMs / 1000.0;
	
	float bytesPerLoop = INNER_LOOPS * PTEXT_SIZE;
	float totalBytes   = params->loops * bytesPerLoop;
	
	/* careful, KByte = 1024, ms = 1/1000 */
	printf("   Digest %.0f bytes : %u ops in %.2f ms; %f ms/op, %.0f KBytes/s\n",
		bytesPerLoop, params->loops, 
		timeSpentMs, timeSpentMs / (double)params->loops,
		((float)totalBytes / 1024.0) / timeSpent);
	return 0;
}


typedef int (*testRunFcn)(TestParams *testParams);

/*
 * Static declaration of a test
 */
typedef struct {
	const char 			*testName;
	unsigned			loops;
	testRunFcn			run;
	char				testSpec;		// for t=xxx cmd line opt
} TestDefs;

static TestDefs testDefs[] = 
{
	{ 	"Digest context setup/teardown",
		100000,
		hashContext,
		'c',
	},
	{ 	"Basic single block digest",
		100000,
		hashBasic,
		'b',
	},
	{ 	"Large data digest",
		1000,
		hashDataRate,
		'd',
	},
};

static void algToAlgId(
	HT_Alg			alg,
	bool			*isSha,
	const char		**algStr)
{
	switch(alg) {
		case HA_MD5:
			*isSha = false;
			*algStr = "MD5";
			break;
		case HA_SHA1:
			*isSha = true;
			*algStr = "SHA1";
			break;
		default:
			printf("***algToAlgId screwup\n");
			exit(1);
	}
}

#define NUM_TESTS	(sizeof(testDefs) / sizeof(testDefs[0]))

int main(int argc, char **argv)
{
	TestParams 		testParams;
	TestDefs		*testDef;
	int				rtn;
	int 			arg;
	char			*argp;
	unsigned		cmdLoops = 0;		// can be specified in cmd line
										// if not, use TestDefs.loops
	char			testSpec = '\0';	// allows specification of one test
										// otherwise run all
	HT_Alg			alg;
	const char		*algStr;
	
	for(arg=1; arg<argc; arg++) {
		argp = argv[arg];
		switch(argp[0]) {
			case 't':
				testSpec = argp[2];
				break;
			case 'l':
				cmdLoops = atoi(&argp[2]);
				break;
			default:
				usage(argv);
		}
	}

	for(unsigned testNum=0; testNum<NUM_TESTS; testNum++) {
		testDef = &testDefs[testNum];
		
		if(testSpec && (testDef->testSpec != testSpec)) {
			continue;
		}
		printf("%s:\n", testDef->testName);
		if(cmdLoops) {
			/* user specified */
			testParams.loops = cmdLoops;
		}	
		else {
			/* default */
			testParams.loops = testDef->loops;
		}
		for(alg=FIRST_ALG; alg<=LAST_ALG; alg++) {
			algToAlgId(alg, &testParams.isSha, &algStr);
			printf("   === %s ===\n", algStr);
			rtn = testDef->run(&testParams);
			if(rtn) {
				printf("Test returned error\n");
				exit(1);
			}
		}
	}
	return 0;
}