Files
amiga-rhash/librhash/rhash_timing.c
Carsten Larsen afd26249c2 Release 1.34
2017-04-12 00:33:28 +02:00

254 lines
7.6 KiB
C

/* rhash_timing.c - functions to benchmark hash algorithms,
*
* Copyright: 2010-2012 Aleksey Kravchenko <rhash.admin@gmail.com>
*
* Permission is hereby granted, free of charge, to any person obtaining a
* copy of this software and associated documentation files (the "Software"),
* to deal in the Software without restriction, including without limitation
* the rights to use, copy, modify, merge, publish, distribute, sublicense,
* and/or sell copies of the Software, and to permit persons to whom the
* Software is furnished to do so.
*
* This program is distributed in the hope that it will be useful, but
* WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY
* or FITNESS FOR A PARTICULAR PURPOSE. Use this program at your own risk!
*/
/* modifier for Windows dll */
#if defined(_WIN32) && defined(RHASH_EXPORTS)
# define RHASH_API __declspec(dllexport)
#endif
#include "byte_order.h"
#include "rhash.h"
#include "rhash_timing.h"
/* DEFINE read_tsc() if possible */
#if (defined(CPU_IA32)) || defined(CPU_X64)
#if defined( _MSC_VER ) /* if MS VC */
# include <intrin.h>
# pragma intrinsic( __rdtsc )
# define read_tsc() __rdtsc()
# define HAVE_TSC
#elif defined( __GNUC__ ) /* if GCC */
static uint64_t read_tsc(void) {
unsigned long lo, hi;
__asm volatile("rdtsc" : "=a" (lo), "=d" (hi));
return (((uint64_t)hi) << 32) + lo;
}
# define HAVE_TSC
#endif /* _MSC_VER, __GNUC__ */
#endif /* CPU_IA32, CPU_X64 */
/* TIMER FUNCTIONS */
#ifdef _WIN32
#include <windows.h>
#define get_timedelta(delta) QueryPerformanceCounter((LARGE_INTEGER*)delta)
#else
#define get_timedelta(delta) gettimeofday(delta, NULL)
#endif
/**
* Return real-value representing number of seconds
* stored in the given timeval structure.
* The function is used with timers, when printing time statistics.
*
* @deprecated This function shall be removed soon.
*
* @param delta time delta to be converted
* @return number of seconds
*/
static double fsec(timedelta_t* timer)
{
#ifdef _WIN32
LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);
return (double)*timer / freq.QuadPart;
#else
return ((double)timer->tv_usec / 1000000.0) + timer->tv_sec;
#endif
}
/**
* Start a timer.
*
* @deprecated This function shall be removed soon, since
* it is not related to the hashing library main functionality.
*
* @param timer timer to start
*/
void rhash_timer_start(timedelta_t* timer)
{
get_timedelta(timer);
}
/**
* Stop given timer.
*
* @deprecated This function shall be removed soon, since
* it is not related to the hashing library main functionality.
*
* @param timer the timer to stop
* @return number of seconds timed
*/
double rhash_timer_stop(timedelta_t* timer)
{
timedelta_t end;
get_timedelta(&end);
#ifdef _WIN32
*timer = end - *timer;
#else
timer->tv_sec = end.tv_sec - timer->tv_sec - (end.tv_usec >= timer->tv_usec ? 0 : 1);
timer->tv_usec = end.tv_usec + (end.tv_usec >= timer->tv_usec ? 0 : 1000000 ) - timer->tv_usec;
#endif
return fsec(timer);
}
#ifdef _WIN32
/**
* Set process priority and affinity to use all cpu's but the first one.
* This improves benchmark results on a multi-cpu systems.
*
* @deprecated This function shall be removed soon.
*/
static void benchmark_cpu_init(void)
{
DWORD_PTR dwProcessMask, dwSysMask, dwDesired;
SetPriorityClass(GetCurrentProcess(), HIGH_PRIORITY_CLASS);
SetThreadPriority(GetCurrentThread(), THREAD_PRIORITY_HIGHEST);
if ( GetProcessAffinityMask(GetCurrentProcess(), &dwProcessMask, &dwSysMask) ) {
dwDesired = dwSysMask & (dwProcessMask & ~1); /* remove the first processor */
dwDesired = (dwDesired ? dwDesired : dwSysMask & ~1);
if (dwDesired != 0) {
SetProcessAffinityMask(GetCurrentProcess(), dwDesired);
}
}
}
#endif
/**
* Hash a repeated message chunk by specified hash function.
*
* @deprecated This function shall be removed soon.
*
* @param hash_id hash function identifier
* @param message a message chunk to hash
* @param msg_size message chunk size
* @param count number of chunks
* @param out computed hash
* @return 1 on success, 0 on error
*/
static int hash_in_loop(unsigned hash_id, const unsigned char* message, size_t msg_size, int count, unsigned char* out)
{
int i;
struct rhash_context *context = rhash_init(hash_id);
if (!context) return 0;
/* process the repeated message buffer */
for (i = 0; i < count; i++) rhash_update(context, message, msg_size);
rhash_final(context, out);
rhash_free(context);
return 1;
}
/**
* Benchmark a hash algorithm.
*
* @deprecated This function shall be removed soon, since
* it is not related to the hashing library main functionality.
*
* @param hash_id hash algorithm identifier
* @param flags benchmark flags, can be RHASH_BENCHMARK_QUIET and RHASH_BENCHMARK_CPB
* @param output the stream to print results
*/
void rhash_run_benchmark(unsigned hash_id, unsigned flags, FILE* output)
{
unsigned char ALIGN_ATTR(16) message[8192]; /* 8 KiB */
timedelta_t timer;
int i, j;
size_t sz_mb, msg_size;
double time, total_time = 0;
const int rounds = 4;
const char* hash_name;
unsigned char out[130];
#ifdef HAVE_TSC
double cpb = 0;
#endif /* HAVE_TSC */
#ifdef _WIN32
benchmark_cpu_init(); /* set cpu affinity to improve test results */
#endif
/* set message size for fast and slow hash functions */
msg_size = 1073741824 / 2;
if (hash_id & (RHASH_WHIRLPOOL | RHASH_SNEFRU128 | RHASH_SNEFRU256 | RHASH_SHA3_224 | RHASH_SHA3_256 | RHASH_SHA3_384 | RHASH_SHA3_512)) {
msg_size /= 8;
} else if (hash_id & (RHASH_GOST | RHASH_GOST_CRYPTOPRO | RHASH_SHA384 | RHASH_SHA512)) {
msg_size /= 2;
}
sz_mb = msg_size / (1 << 20); /* size in MiB */
hash_name = rhash_get_name(hash_id);
if (!hash_name) hash_name = ""; /* benchmarking several hashes*/
for (i = 0; i < (int)sizeof(message); i++) message[i] = i & 0xff;
for (j = 0; j < rounds; j++) {
rhash_timer_start(&timer);
hash_in_loop(hash_id, message, sizeof(message), (int)(msg_size / sizeof(message)), out);
time = rhash_timer_stop(&timer);
total_time += time;
if ((flags & (RHASH_BENCHMARK_QUIET | RHASH_BENCHMARK_RAW)) == 0) {
fprintf(output, "%s %u MiB calculated in %.3f sec, %.3f MBps\n", hash_name, (unsigned)sz_mb, time, (double)sz_mb / time);
fflush(output);
}
}
#if defined(HAVE_TSC)
/* measure the CPU "clocks per byte" speed */
if (flags & RHASH_BENCHMARK_CPB) {
unsigned int c1 = -1, c2 = -1;
unsigned volatile long long cy0, cy1, cy2;
int msg_size = 128 * 1024;
/* make 200 tries */
for (i = 0; i < 200; i++) {
cy0 = read_tsc();
hash_in_loop(hash_id, message, sizeof(message), msg_size / sizeof(message), out);
cy1 = read_tsc();
hash_in_loop(hash_id, message, sizeof(message), msg_size / sizeof(message), out);
hash_in_loop(hash_id, message, sizeof(message), msg_size / sizeof(message), out);
cy2 = read_tsc();
cy2 -= cy1;
cy1 -= cy0;
c1 = (unsigned int)(c1 > cy1 ? cy1 : c1);
c2 = (unsigned int)(c2 > cy2 ? cy2 : c2);
}
cpb = ((c2 - c1) + 1) / (double)msg_size;
}
#endif /* HAVE_TSC */
if (flags & RHASH_BENCHMARK_RAW) {
/* output result in a "raw" machine-readable format */
fprintf(output, "%s\t%u\t%.3f\t%.3f", hash_name, ((unsigned)sz_mb * rounds), total_time, (double)(sz_mb * rounds) / total_time);
#if defined(HAVE_TSC)
if (flags & RHASH_BENCHMARK_CPB) fprintf(output, "\t%.2f", cpb);
#endif /* HAVE_TSC */
fprintf(output, "\n");
} else {
fprintf(output, "%s %u MiB total in %.3f sec, %.3f MBps", hash_name, ((unsigned)sz_mb * rounds), total_time, (double)(sz_mb * rounds) / total_time);
#if defined(HAVE_TSC)
if (flags & RHASH_BENCHMARK_CPB) fprintf(output, ", CPB=%.2f", cpb);
#endif /* HAVE_TSC */
fprintf(output, "\n");
}
}