diff --git a/benchmarks/benchmark.c b/benchmarks/benchmark.c index f8293ae..32c8fac 100644 --- a/benchmarks/benchmark.c +++ b/benchmarks/benchmark.c @@ -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 #include #include #include -#include "macro_collections.h" -#include "timer.h" +#include + +#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; } diff --git a/benchmarks/util/twister.c b/benchmarks/util/twister.c new file mode 100644 index 0000000..bfd3943 --- /dev/null +++ b/benchmarks/util/twister.c @@ -0,0 +1,102 @@ +/** + * twister.c + * + * Creation Date: 09/05/2019 + * + * Authors: + * Leonardo Vencovsky (https://github.com/LeoVen) + * + * Mersenne Twister PRNG utility + * + */ +#include +#include +#include + +#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); +}