Update benchmark
Added Mersenne Twister utility as the function rand() was not random enough
This commit is contained in:
1 parent
c61ac2e347
commit
bc350611fc
2 files changed
+256
-114
No files matched your search
+154
-114
@@ -8,16 +8,24 @@
|
||||
*
|
||||
* Benchmarks:
|
||||
* - Input
|
||||
* - Output
|
||||
* - Search
|
||||
* - Iteration
|
||||
* - Search (to be done)
|
||||
* - Output
|
||||
*/
|
||||
#include "macro_collections.h"
|
||||
#include "timer.h"
|
||||
#include "util/twister.c"
|
||||
#include <stdio.h>
|
||||
#include <stdint.h>
|
||||
#include <assert.h>
|
||||
#include <math.h>
|
||||
#include "macro_collections.h"
|
||||
#include "timer.h"
|
||||
#include <inttypes.h>
|
||||
|
||||
#define NTOTAL UINT64_C(10000000)
|
||||
#define NMIN UINT64_C(100)
|
||||
|
||||
// Percentage % macro
|
||||
#define PC(x) x * 100.0
|
||||
|
||||
static int intcmp(int a, int b)
|
||||
{
|
||||
@@ -36,21 +44,16 @@ static size_t inthash(int t)
|
||||
return a;
|
||||
}
|
||||
|
||||
double rrandom()
|
||||
size_t randsize(size_t min, size_t max, mt_state_ptr st)
|
||||
{
|
||||
return (double)rand() / ((double)RAND_MAX + 1.0);
|
||||
return (size_t)floor(twist_real(st) * ((double)max - (double)min + 1.0) + (double)min);
|
||||
}
|
||||
|
||||
size_t randsize(size_t min, size_t max)
|
||||
void shuffle(int *array, mt_state_ptr st)
|
||||
{
|
||||
return (size_t)floor(rrandom() * ((double)max - (double)min + 1.0) + (double)min);
|
||||
}
|
||||
|
||||
void shuffle(int *array)
|
||||
{
|
||||
for (size_t i = 0; i < 10000000; i++)
|
||||
for (size_t i = 0; i < NTOTAL; i++)
|
||||
{
|
||||
size_t j = randsize(0, 10000000);
|
||||
size_t j = randsize(0, NTOTAL - 1, st);
|
||||
int tmp = array[j];
|
||||
array[j] = array[i];
|
||||
array[i] = tmp;
|
||||
@@ -68,100 +71,130 @@ COLLECTION_GENERATE(TREEMAP, PUBLIC, tm, tmap, /* FMOD */, int, int)
|
||||
COLLECTION_GENERATE(HASHSET, PUBLIC, hs, hset, /* FMOD */, /* K */, int)
|
||||
COLLECTION_GENERATE(HASHMAP, PUBLIC, hm, hmap, /* FMOD */, int, int)
|
||||
|
||||
#define BENCHMARK(NAME, PFX, sname, initfunc, insertbody, removebody, iternext, searchbody) \
|
||||
void NAME##_io_benchmark(int *array, size_t s) \
|
||||
{ \
|
||||
printf("+------------------------------------------------------------ %10s\n", #NAME); \
|
||||
timer_t timer; \
|
||||
int r, k, v; \
|
||||
size_t j; \
|
||||
\
|
||||
sname *collection = initfunc; \
|
||||
\
|
||||
TIMER_START(timer) \
|
||||
\
|
||||
for (size_t i = 0; i < 10000000; i++) \
|
||||
{ \
|
||||
insertbody; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
\
|
||||
size_t s1 = PFX##_count(collection); \
|
||||
\
|
||||
printf(" INPUT %10s TOOK %8.0lf milliseconds for %8lu elements\n", #NAME, timer.result, s1); \
|
||||
\
|
||||
TIMER_START(timer) \
|
||||
\
|
||||
for (size_t i = 0; i < s; i++) \
|
||||
{ \
|
||||
searchbody; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
\
|
||||
printf(" SEARCH %10s TOOK %8.0lf milliseconds for %8lu elements\n", #NAME, timer.result, s); \
|
||||
\
|
||||
sname##_iter iter; \
|
||||
\
|
||||
TIMER_START(timer) \
|
||||
\
|
||||
for (PFX##_iter_new(&iter, collection); !PFX##_iter_end(&iter);) \
|
||||
{ \
|
||||
iternext; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
\
|
||||
printf("ITERATION %10s TOOK %8.0lf milliseconds for %8lu elements\n", #NAME, timer.result, s1); \
|
||||
\
|
||||
TIMER_START(timer); \
|
||||
\
|
||||
for (size_t i = 0; i < 10000000; i++) \
|
||||
{ \
|
||||
removebody; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
\
|
||||
size_t s2 = PFX##_count(collection); \
|
||||
\
|
||||
printf(" OUTPUT %10s TOOK %8.0lf milliseconds for %8lu elements\n", #NAME, timer.result, s1); \
|
||||
\
|
||||
PFX##_free(collection); \
|
||||
\
|
||||
assert(s1 == 10000000 && s2 == 0); \
|
||||
\
|
||||
printf("+------------------------------------------------------------ %10s\n\n", #NAME); \
|
||||
#define BENCHMARK(NAME, PFX, sname, initfunc, insertbody, removebody, iternext, searchbody) \
|
||||
void NAME##_io_benchmark(int *array, int *sarray, size_t s) \
|
||||
{ \
|
||||
printf("+------------------------------------------------------------ %10s\n", #NAME); \
|
||||
printf("+------------------------------------------------------------ \n"); \
|
||||
timer_t timer, total; \
|
||||
int r, k, v; \
|
||||
size_t j; \
|
||||
\
|
||||
sname *coll = initfunc; \
|
||||
\
|
||||
TIMER_START(total) \
|
||||
TIMER_START(timer) \
|
||||
\
|
||||
for (size_t i = 0; i < NTOTAL; i++) \
|
||||
{ \
|
||||
insertbody; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
\
|
||||
size_t s1 = PFX##_count(coll); \
|
||||
\
|
||||
printf(" INPUT %10s TOOK %8.0lf milliseconds for %8" PRIuMAX " elements\n", #NAME, timer.result, s1); \
|
||||
\
|
||||
double input_time = timer.result; \
|
||||
\
|
||||
TIMER_START(timer) \
|
||||
\
|
||||
for (size_t i = 0; i < s; i++) \
|
||||
{ \
|
||||
searchbody; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
\
|
||||
printf(" SEARCH %10s TOOK %8.0lf milliseconds for %8" PRIuMAX " elements\n", #NAME, timer.result, s); \
|
||||
\
|
||||
double search_time = timer.result; \
|
||||
\
|
||||
sname##_iter iter; \
|
||||
\
|
||||
TIMER_START(timer) \
|
||||
\
|
||||
for (PFX##_iter_init(&iter, coll); !PFX##_iter_end(&iter);) \
|
||||
{ \
|
||||
iternext; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
\
|
||||
printf("ITERATION %10s TOOK %8.0lf milliseconds for %8" PRIuMAX " elements\n", #NAME, timer.result, s1); \
|
||||
\
|
||||
double iter_time = timer.result; \
|
||||
\
|
||||
TIMER_START(timer); \
|
||||
\
|
||||
for (size_t i = 0; i < NTOTAL; i++) \
|
||||
{ \
|
||||
removebody; \
|
||||
} \
|
||||
\
|
||||
TIMER_STOP(timer) \
|
||||
\
|
||||
TIMER_CALC(timer) \
|
||||
\
|
||||
size_t s2 = PFX##_count(coll); \
|
||||
\
|
||||
printf(" OUTPUT %10s TOOK %8.0lf milliseconds for %8" PRIuMAX " elements\n", #NAME, timer.result, s1); \
|
||||
\
|
||||
double output_time = timer.result; \
|
||||
\
|
||||
TIMER_STOP(total) \
|
||||
TIMER_CALC(total) \
|
||||
\
|
||||
PFX##_free(coll); \
|
||||
\
|
||||
double adj_search_time = (total.result - search_time) + (search_time * (double)NTOTAL) / (double)s; \
|
||||
double no_search = input_time + iter_time + output_time; \
|
||||
\
|
||||
printf("+------------------------------------------------------------ \n"); \
|
||||
printf("TOTAL TIME : %.0lf milliseconds\n", total.result); \
|
||||
printf("ADJUSTED TIME : %.0lf milliseconds\n", adj_search_time); \
|
||||
\
|
||||
printf("+------------------------------------------------------------ \n"); \
|
||||
printf(" INPUT : %5.2lf%% (%5.2f%%)\n", PC(input_time / total.result), PC(input_time / no_search)); \
|
||||
printf(" SEARCH : %5.2lf%%\n", PC(search_time / total.result)); \
|
||||
printf("ITERATION : %5.2lf%% (%5.2f%%)\n", PC(iter_time / total.result), PC(iter_time / no_search)); \
|
||||
printf(" OUTPUT : %5.2lf%% (%5.2f%%)\n", PC(output_time / total.result), PC(output_time / no_search)); \
|
||||
printf("+------------------------------------------------------------ \n"); \
|
||||
\
|
||||
assert(s1 == NTOTAL && s2 == 0); \
|
||||
\
|
||||
printf("+------------------------------------------------------------ %10s\n\n", #NAME); \
|
||||
}
|
||||
|
||||
BENCHMARK(DEQUE, d, deque, d_new(10000000), d_push_back(collection, array[i]), d_pop_back(collection), d_iter_next(&iter, &k, &j), d_contains(collection, array[i], intcmp))
|
||||
BENCHMARK(HASHMAP, hm, hmap, hm_new(10000000, 0.8, intcmp, inthash), hm_insert(collection, array[i], array[i]), hm_remove(collection, array[i], &r), hm_iter_next(&iter, &k, &v, &j), hm_contains(collection, array[i]))
|
||||
BENCHMARK(HASHSET, hs, hset, hs_new(10000000, 0.8, intcmp, inthash), hs_insert(collection, array[i]), hs_remove(collection, array[i]), hs_iter_next(&iter, &v, &j), hs_contains(collection, array[i]))
|
||||
BENCHMARK(HEAP, h, heap, h_new(10000000, MinHeap, intcmp), h_insert(collection, array[i]), h_remove(collection, &r), h_iter_next(&iter, &v, &j), h_contains(collection, array[i]))
|
||||
BENCHMARK(LINKEDLIST, ll, linked, ll_new(), ll_push_back(collection, array[i]), ll_pop_back(collection), ll_iter_next(&iter, &v, &j), ll_contains(collection, array[i], intcmp))
|
||||
BENCHMARK(LIST, l, list, l_new(10000000), l_push_back(collection, array[i]), l_pop_back(collection), l_iter_next(&iter, &v, &j), l_contains(collection, array[i], intcmp))
|
||||
BENCHMARK(QUEUE, q, queue, q_new(10000000), q_enqueue(collection, array[i]), q_dequeue(collection), q_iter_next(&iter, &v, &j), q_contains(collection, array[i], intcmp))
|
||||
BENCHMARK(STACK, s, stack, s_new(10000000), s_push(collection, array[i]), s_pop(collection), s_iter_next(&iter, &v, &j), s_contains(collection, array[i], intcmp))
|
||||
BENCHMARK(TREEMAP, tm, tmap, tm_new(intcmp), tm_insert(collection, array[i], array[i]), tm_remove(collection, array[i], &r), tm_iter_next(&iter, &k, &v, &j), tm_contains(collection, array[i]))
|
||||
BENCHMARK(TREESET, ts, tset, ts_new(intcmp), ts_insert(collection, array[i]), ts_remove(collection, array[i]), ts_iter_next(&iter, &v, &j), ts_contains(collection, array[i]))
|
||||
BENCHMARK(DEQUE, d, deque, d_new(NTOTAL), d_push_back(coll, array[i]), d_pop_back(coll), d_iter_next(&iter, &k, &j), d_contains(coll, sarray[i], intcmp))
|
||||
BENCHMARK(HASHMAP, hm, hmap, hm_new(NTOTAL, 0.8, intcmp, inthash), hm_insert(coll, array[i], array[i]), hm_remove(coll, array[i], &r), hm_iter_next(&iter, &k, &v, &j), hm_contains(coll, sarray[i]))
|
||||
BENCHMARK(HASHSET, hs, hset, hs_new(NTOTAL, 0.8, intcmp, inthash), hs_insert(coll, array[i]), hs_remove(coll, array[i]), hs_iter_next(&iter, &v, &j), hs_contains(coll, sarray[i]))
|
||||
BENCHMARK(HEAP, h, heap, h_new(NTOTAL, MinHeap, intcmp), h_insert(coll, array[i]), h_remove(coll, &r), h_iter_next(&iter, &v, &j), h_contains(coll, sarray[i]))
|
||||
BENCHMARK(LINKEDLIST, ll, linked, ll_new(), ll_push_back(coll, array[i]), ll_pop_back(coll), ll_iter_next(&iter, &v, &j), ll_contains(coll, sarray[i], intcmp))
|
||||
BENCHMARK(LIST, l, list, l_new(NTOTAL), l_push_back(coll, array[i]), l_pop_back(coll), l_iter_next(&iter, &v, &j), l_contains(coll, sarray[i], intcmp))
|
||||
BENCHMARK(QUEUE, q, queue, q_new(NTOTAL), q_enqueue(coll, array[i]), q_dequeue(coll), q_iter_next(&iter, &v, &j), q_contains(coll, sarray[i], intcmp))
|
||||
BENCHMARK(STACK, s, stack, s_new(NTOTAL), s_push(coll, array[i]), s_pop(coll), s_iter_next(&iter, &v, &j), s_contains(coll, sarray[i], intcmp))
|
||||
BENCHMARK(TREEMAP, tm, tmap, tm_new(intcmp), tm_insert(coll, array[i], array[i]), tm_remove(coll, array[i], &r), tm_iter_next(&iter, &k, &v, &j), tm_contains(coll, sarray[i]))
|
||||
BENCHMARK(TREESET, ts, tset, ts_new(intcmp), ts_insert(coll, array[i]), ts_remove(coll, array[i]), ts_iter_next(&iter, &v, &j), ts_contains(coll, sarray[i]))
|
||||
|
||||
int main(void)
|
||||
{
|
||||
// If you run this benchmark more than once in the same computer (as long
|
||||
// as the rand() implementation is the same) the contents of the array will
|
||||
// always be shuffled the same way.
|
||||
srand(3111221);
|
||||
// Using the Mersenne Twister to generate random numbers. This seed
|
||||
// guarantees that everytime this program runs it will generate the same
|
||||
// random numbers. The random numbers influence on the shuffling of the
|
||||
// array.
|
||||
mt_state st;
|
||||
twist_init(&st, UINT64_C(12604951237));
|
||||
|
||||
int *array = malloc(10000000 * sizeof(int));
|
||||
int *array = malloc(NTOTAL * sizeof(int));
|
||||
int *sarray = malloc(NTOTAL * sizeof(int));
|
||||
|
||||
if (!array)
|
||||
{
|
||||
@@ -172,22 +205,29 @@ int main(void)
|
||||
for (int i = -5000000; i < 5000000; i++)
|
||||
array[i + 5000000] = i;
|
||||
|
||||
shuffle(array);
|
||||
shuffle(array, &st);
|
||||
|
||||
// Linear containers have to search for 10000 elements and non-linear (hash
|
||||
memcpy(sarray, array, NTOTAL * sizeof(int));
|
||||
|
||||
shuffle(sarray, &st);
|
||||
|
||||
printf("\nNote: Adjusted Time refers to if the container had done a search over all %" PRIuMAX " elements.\n\n", NTOTAL);
|
||||
|
||||
// Linear containers have to search for NMIN elements and non-linear (hash
|
||||
// and tree) have to search for all elements.
|
||||
DEQUE_io_benchmark(array, 10000);
|
||||
HASHMAP_io_benchmark(array, 10000000);
|
||||
HASHSET_io_benchmark(array, 10000000);
|
||||
HEAP_io_benchmark(array, 10000);
|
||||
LINKEDLIST_io_benchmark(array, 10000);
|
||||
LIST_io_benchmark(array, 10000);
|
||||
QUEUE_io_benchmark(array, 10000);
|
||||
STACK_io_benchmark(array, 10000);
|
||||
TREEMAP_io_benchmark(array, 10000000);
|
||||
TREESET_io_benchmark(array, 10000000);
|
||||
DEQUE_io_benchmark(array, sarray, NMIN);
|
||||
HASHMAP_io_benchmark(array, sarray, NTOTAL);
|
||||
HASHSET_io_benchmark(array, sarray, NTOTAL);
|
||||
HEAP_io_benchmark(array, sarray, NMIN);
|
||||
LINKEDLIST_io_benchmark(array, sarray, NMIN);
|
||||
LIST_io_benchmark(array, sarray, NMIN);
|
||||
QUEUE_io_benchmark(array, sarray, NMIN);
|
||||
STACK_io_benchmark(array, sarray, NMIN);
|
||||
TREEMAP_io_benchmark(array, sarray, NTOTAL);
|
||||
TREESET_io_benchmark(array, sarray, NTOTAL);
|
||||
|
||||
free(array);
|
||||
free(sarray);
|
||||
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
/**
|
||||
* twister.c
|
||||
*
|
||||
* Creation Date: 09/05/2019
|
||||
*
|
||||
* Authors:
|
||||
* Leonardo Vencovsky (https://github.com/LeoVen)
|
||||
*
|
||||
* Mersenne Twister PRNG utility
|
||||
*
|
||||
*/
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
#include <stdint.h>
|
||||
|
||||
#define NN 312
|
||||
#define MM 156
|
||||
#define MATRIX_A UINT64_C(0xB5026F5AA96619E9)
|
||||
#define UM UINT64_C(0xFFFFFFFF80000000) // Most significant 33 bits
|
||||
#define LM UINT64_C(0x7FFFFFFF) // Least significant 31 bits
|
||||
|
||||
// State vector
|
||||
typedef struct mt_state_s
|
||||
{
|
||||
uint64_t mt[NN];
|
||||
int mti;
|
||||
} mt_state, *mt_state_ptr;
|
||||
|
||||
// Initializes mt[NN] with a seed
|
||||
void twist_init(mt_state_ptr st, uint64_t seed);
|
||||
|
||||
// Generates a random uint64_t in [0, 2^64-1] interval
|
||||
uint64_t twist_uint64(mt_state_ptr st);
|
||||
|
||||
// Generates a double in [0, 1) interval
|
||||
double twist_real(mt_state_ptr st);
|
||||
|
||||
//
|
||||
// Implementation
|
||||
//
|
||||
|
||||
// Initialize a state mt[NN] with a seed
|
||||
void twist_init(mt_state_ptr st, uint64_t seed)
|
||||
{
|
||||
st->mti = NN + 1;
|
||||
|
||||
st->mt[0] = seed;
|
||||
|
||||
for (st->mti = 1; st->mti < NN; st->mti++)
|
||||
{
|
||||
st->mt[st->mti] = (UINT64_C(6364136223846793005) * (st->mt[st->mti - 1] ^ (st->mt[st->mti - 1] >> 62)) + st->mti);
|
||||
}
|
||||
}
|
||||
|
||||
// Generates a random uint64_t in [0, 2^64-1] interval
|
||||
uint64_t twist_uint64(mt_state_ptr st)
|
||||
{
|
||||
int i;
|
||||
uint64_t x;
|
||||
static uint64_t mag01[2] = {UINT64_C(0), MATRIX_A};
|
||||
|
||||
if (st->mti >= NN)
|
||||
{
|
||||
// Generate NN words at one time if twist_init() has not been called
|
||||
// A default initial seed is used
|
||||
if (st->mti == NN + 1)
|
||||
twist_init(st, UINT64_C(5489));
|
||||
|
||||
for (i = 0; i < NN - MM; i++)
|
||||
{
|
||||
x = (st->mt[i] & UM) | (st->mt[i + 1] & LM);
|
||||
st->mt[i] = st->mt[i + MM] ^ (x >> 1) ^ mag01[(int)(x & UINT64_C(1))];
|
||||
}
|
||||
|
||||
for (; i < NN - 1; i++)
|
||||
{
|
||||
x = (st->mt[i] & UM) | (st->mt[i + 1] & LM);
|
||||
st->mt[i] = st->mt[i + (MM - NN)] ^ (x >> 1) ^ mag01[(int)(x & UINT64_C(1))];
|
||||
}
|
||||
|
||||
x = (st->mt[NN - 1] & UM) | (st->mt[0] & LM);
|
||||
|
||||
st->mt[NN - 1] = st->mt[MM - 1] ^ (x >> 1) ^ mag01[(int)(x & UINT64_C(1))];
|
||||
|
||||
st->mti = 0;
|
||||
}
|
||||
|
||||
x = st->mt[st->mti++];
|
||||
|
||||
x ^= (x >> 29) & UINT64_C(0x5555555555555555);
|
||||
x ^= (x << 17) & UINT64_C(0x71D67FFFEDA60000);
|
||||
x ^= (x << 37) & UINT64_C(0xFFF7EEE000000000);
|
||||
x ^= (x >> 43);
|
||||
|
||||
return x;
|
||||
}
|
||||
|
||||
// Generates a double in [0, 1) interval
|
||||
double twist_real(mt_state_ptr st)
|
||||
{
|
||||
return (twist_uint64(st) >> 11) * (1.0 / 9007199254740992.0);
|
||||
}
|
||||
Reference in new issue
Block a user