Code:
#include <stdlib.h>
#include <stdint.h>
#include <string.h>
#include <errno.h>
#include <stdio.h>
#include "timing.h"
#include "sort.h"
/* Xorshift random number generator.
*/
static uint32_t xorshift_state[4] = {
123456789U,
362436069U,
521288629U,
88675123U
};
static int xorshift_setseed(const void *const data, const size_t length)
{
uint32_t state[4];
if (length < 1)
return ENOENT;
if (length < sizeof state) {
memset(state, 0, sizeof state);
memcpy(state, data, length);
} else
memcpy(state, data, sizeof state);
/* State cannot be all zeros, as that'd produce only zeros. */
if (state[0] || state[1] || state[2] || state[3]) {
memcpy(xorshift_state, state, sizeof state);
return 0;
}
return EINVAL;
}
static uint32_t xorshift_u32(void)
{
const uint32_t temp = xorshift_state[0] ^ (xorshift_state[0] << 11U);
xorshift_state[0] = xorshift_state[1];
xorshift_state[1] = xorshift_state[2];
xorshift_state[2] = xorshift_state[3];
return xorshift_state[3] ^= (temp >> 8U) ^ temp ^ (xorshift_state[3] >> 19U);
}
static uint64_t xorshift_u64(void)
{
return ((uint64_t)xorshift_u32() << 32)
| (uint64_t)xorshift_u32();
}
static size_t xorshift_range(const size_t min, const size_t max)
{
if (max > min) {
const size_t size = max - min;
size_t mask = ~0;
size_t temp;
while (mask / (size_t)2 > size)
mask /= (size_t)2;
mask--;
if (size < (size_t)4294967295.0) {
do {
temp = xorshift_u32() & mask;
} while (temp > size);
return temp + min;
} else {
do {
temp = xorshift_u64() & mask;
} while (temp > size);
return temp + min;
}
} else
return min;
}
/* Given an array of keys,
* dynamically allocate a single block, and fill with nodes as
* the corresponding linked list.
*/
node_t *linked_list(const int *const key, const size_t len)
{
node_t *array;
size_t i;
if (len < 1) {
errno = 0;
return (node_t *)0;
}
array = malloc(len * sizeof array[0]);
if (!array) {
errno = ENOMEM;
return (node_t *)0;
}
for (i = 0; i < len; i++) {
array[i].next = &array[i+1];
array[i].key = key[i];
}
array[len-1].next = (node_t *)0;
return array;
}
size_t sorted_length(const node_t *node)
{
size_t len = 1;
if (!node)
return 0;
while (node->next) {
if (node->key > node->next->key)
return len;
node = node->next;
len++;
}
return len;
}
int parse_range(const char *const arg, size_t *const min, size_t *const max)
{
long val1, val2;
char dummy;
if (!arg || !*arg)
return EINVAL;
if (sscanf(arg, " %ld - %ld %c", &val1, &val2, &dummy) == 2 ||
sscanf(arg, " %ld : %ld %c", &val1, &val2, &dummy) == 2 ||
sscanf(arg, " %ld .. %ld %c", &val1, &val2, &dummy) == 2) {
if (val1 <= val2) {
if (min) *min = val1;
if (max) *max = val2;
} else {
if (min) *min = val2;
if (max) *max = val1;
}
return 0;
}
if (sscanf(arg, " %ld + %ld %c", &val1, &val2, &dummy) == 2) {
if (val2 >= 0L) {
if (min) *min = val1;
if (max) *max = val1 + val2;
} else {
if (min) *min = val1 + val2;
if (max) *max = val1;
}
return 0;
}
if (sscanf(arg, " %ld %c", &val1, &dummy) == 1) {
if (min) *min = val1;
if (max) *max = val1;
return 0;
}
return EINVAL;
}
int compare_doubles(const void *ptr1, const void *ptr2)
{
const double val1 = *(const double *)ptr1;
const double val2 = *(const double *)ptr2;
if (val1 < val2)
return -1;
else
if (val1 > val2)
return +1;
else
return 0;
}
int main(int argc, char *argv[])
{
size_t sublists, sublists_min, sublists_max;
size_t length, length_min, length_max;
size_t sprinkles, sprinkles_min, sprinkles_max;
size_t repeats;
size_t len, max_len;
int *key;
double *topdown_wall, *nominal_wall, *bottomup_wall;
double *topdown_cpu, *nominal_cpu, *bottomup_cpu;
double *topdown_cycles, *nominal_cycles, *bottomup_cycles;
size_t topdown, nominal, bottomup;
node_t *list, *sorted;
size_t i, n;
int one;
if (argc < 5 || argc > 6 || !strcmp(argv[1], "-h") || !strcmp(argv[1], "--help")) {
fprintf(stderr, "\n");
fprintf(stderr, "Usage: %s [ -h | --help ]\n", argv[0]);
fprintf(stderr, " %s REPEATS SUBLISTS LENGTH SPRINKLES [ SEED ]\n", argv[0]);
fprintf(stderr, "Where:\n");
fprintf(stderr, " REPEATS Number of operations measured\n");
fprintf(stderr, " SUBLISTS Number of sorted sublists\n");
fprintf(stderr, " LENGTH Length of each sublist\n");
fprintf(stderr, " SPRINKLES Number of added random elements\n");
fprintf(stderr, " SEED String used to seed the Xorshift PRNG\n");
fprintf(stderr, "\n");
fprintf(stderr, "This program generates a linked list with the above\n");
fprintf(stderr, "randomness properties, then measures the time taken\n");
fprintf(stderr, "to sort it using different sorting approaches.\n");
fprintf(stderr, "\n");
return 1;
}
if (argc >= 5)
if (xorshift_setseed(argv[4], strlen(argv[4]))) {
fprintf(stderr, "%s: Cannot use string as a random number seed.\n", argv[4]);
return 1;
}
{
long value;
char dummy;
if (sscanf(argv[1], " %ld %c", &value, &dummy) == 1 && value > 0L)
repeats = value;
else {
fprintf(stderr, "%s: Invalid number of sort operations to measure.\n", argv[1]);
return 1;
}
}
topdown_wall = malloc(repeats * sizeof topdown_wall[0]);
topdown_cpu = malloc(repeats * sizeof topdown_cpu[0]);
topdown_cycles = malloc(repeats * sizeof topdown_cycles[0]);
nominal_wall = malloc(repeats * sizeof nominal_wall[0]);
nominal_cpu = malloc(repeats * sizeof nominal_cpu[0]);
nominal_cycles = malloc(repeats * sizeof nominal_cycles[0]);
bottomup_wall = malloc(repeats * sizeof bottomup_wall[0]);
bottomup_cpu = malloc(repeats * sizeof bottomup_cpu[0]);
bottomup_cycles = malloc(repeats * sizeof bottomup_cycles[0]);
if (!topdown_wall || !topdown_cpu || !topdown_cycles ||
!nominal_wall || !nominal_cpu || !nominal_cycles ||
!bottomup_wall || !bottomup_cpu || !bottomup_cycles) {
fprintf(stderr, "Not enough memory: too many repeats (%lu).\n", (unsigned long)repeats);
return 1;
}
if (parse_range(argv[2], &sublists_min, &sublists_max) || sublists_max < sublists_min) {
fprintf(stderr, "%s: Invalid number of sublists.\n", argv[2]);
return 1;
}
if (parse_range(argv[3], &length_min, &length_max) || length_max < length_min) {
fprintf(stderr, "%s: Invalid sublist length.\n", argv[3]);
return 1;
}
if (parse_range(argv[4], &sprinkles_min, &sprinkles_max) || sprinkles_max < sprinkles_min) {
fprintf(stderr, "%s: Invalid number of added random elements.\n", argv[4]);
return 1;
}
sublists = xorshift_range(sublists_min, sublists_max);
sprinkles = xorshift_range(sprinkles_min, sprinkles_max);
max_len = (size_t)sublists * (size_t)length_max + (size_t)sprinkles;
len = 0;
key = malloc(max_len * sizeof key[0]);
if (!key) {
fprintf(stderr, "Not enough memory (for %lu sublists and %lu sprinkles).\n",
(unsigned long)sublists, (unsigned long)sprinkles);
return 1;
}
for (n = 0; n < sublists; n++) {
length = xorshift_range(length_min, length_max);
for (i = 0; i < length; i++)
key[len++] = 256*i + (xorshift_u32() & 255);
}
for (n = 0; n < sprinkles; n++) {
i = xorshift_range(0, len);
if (i < len)
memmove(key + i + 1, key + i, (len - i) * sizeof key[0]);
key[i] = xorshift_u32();
len++;
}
if (len < 2) {
fprintf(stderr, "Nothing to sort.\n");
return 1;
}
topdown = 0;
nominal = 0;
bottomup = 0;
list = NULL;
while (topdown < repeats || nominal < repeats || bottomup < repeats) {
one = (xorshift_u32() >> 16) & 3;
if (!one)
continue;
if (!list) {
list = linked_list(key, len);
if (!list) {
fprintf(stderr, "Out of memory!\n");
return 1;
}
}
if (topdown < repeats && one == 1) {
timing_start();
sorted = topdown_sort(list);
timing_stop(&topdown_wall[topdown], &topdown_cpu[topdown], &topdown_cycles[topdown]);
if (sorted_length(sorted) != len) {
fprintf(stderr, "topdown_sort() failed.\n");
return 1;
}
topdown++;
free(list);
list = NULL;
} else
if (nominal < repeats && one == 2) {
timing_start();
sorted = nominal_sort(list);
timing_stop(&nominal_wall[nominal], &nominal_cpu[nominal], &nominal_cycles[nominal]);
if (sorted_length(sorted) != len) {
fprintf(stderr, "nominal_sort() failed.\n");
return 1;
}
nominal++;
free(list);
list = NULL;
} else
if (bottomup < repeats && one == 3) {
timing_start();
sorted = bottomup_sort(list);
timing_stop(&bottomup_wall[bottomup], &bottomup_cpu[bottomup], &bottomup_cycles[bottomup]);
if (sorted_length(sorted) != len) {
fprintf(stderr, "bottomup_sort() failed.\n");
return 1;
}
bottomup++;
free(list);
list = NULL;
}
}
qsort(topdown_wall, repeats, sizeof topdown_wall[0], compare_doubles);
qsort(topdown_cpu, repeats, sizeof topdown_cpu[0], compare_doubles);
qsort(topdown_cycles, repeats, sizeof topdown_cycles[0], compare_doubles);
qsort(nominal_wall, repeats, sizeof nominal_wall[0], compare_doubles);
qsort(nominal_cpu, repeats, sizeof nominal_cpu[0], compare_doubles);
qsort(nominal_cycles, repeats, sizeof nominal_cycles[0], compare_doubles);
qsort(bottomup_wall, repeats, sizeof bottomup_wall[0], compare_doubles);
qsort(bottomup_cpu, repeats, sizeof bottomup_cpu[0], compare_doubles);
qsort(bottomup_cycles, repeats, sizeof bottomup_cycles[0], compare_doubles);
fprintf(stderr, "List length: %lu nodes\n", (unsigned long)len);
fprintf(stderr, "Sublists: %lu\n", (unsigned long)sublists);
fprintf(stderr, "Random nodes: %lu\n", (unsigned long)sprinkles);
fprintf(stderr, "\n");
fprintf(stderr, "Traditional top-down merge sort:\n");
fprintf(stderr, "\t%15.9f seconds wall time minimum\n", topdown_wall[0]);
fprintf(stderr, "\t%15.9f seconds CPU time minimum\n", topdown_cpu[0]);
if (topdown_cycles[0])
fprintf(stderr, "\t%15.0f CPU cycles minimum\n", topdown_cycles[0]);
fprintf(stderr, "\t%15.9f seconds wall time median\n", topdown_wall[repeats/2]);
fprintf(stderr, "\t%15.9f seconds CPU time median\n", topdown_cpu[repeats/2]);
if (topdown_cycles[repeats/2])
fprintf(stderr, "\t%15.0f CPU cycles median\n", topdown_cycles[repeats/2]);
fprintf(stderr, "\n");
fprintf(stderr, "Top-down sorted-scanning merge sort:\n");
fprintf(stderr, "\t%15.9f seconds wall time minimum\n", nominal_wall[0]);
fprintf(stderr, "\t%15.9f seconds CPU time minimum\n", nominal_cpu[0]);
if (nominal_cycles[0])
fprintf(stderr, "\t%15.0f CPU cycles minimum\n", nominal_cycles[0]);
fprintf(stderr, "\t%15.9f seconds wall time median\n", nominal_wall[repeats/2]);
fprintf(stderr, "\t%15.9f seconds CPU time median\n", nominal_cpu[repeats/2]);
if (nominal_cycles[repeats/2])
fprintf(stderr, "\t%15.0f CPU cycles median\n", nominal_cycles[repeats/2]);
fprintf(stderr, "\n");
fprintf(stderr, "Bottom up merge sort:\n");
fprintf(stderr, "\t%15.9f seconds wall time minimum\n", bottomup_wall[0]);
fprintf(stderr, "\t%15.9f seconds CPU time minimum\n", bottomup_cpu[0]);
if (bottomup_cycles[0])
fprintf(stderr, "\t%15.0f CPU cycles minimum\n", bottomup_cycles[0]);
fprintf(stderr, "\t%15.9f seconds wall time median\n", bottomup_wall[repeats/2]);
fprintf(stderr, "\t%15.9f seconds CPU time median\n", bottomup_cpu[repeats/2]);
if (bottomup_cycles[repeats/2])
fprintf(stderr, "\t%15.0f CPU cycles median\n", bottomup_cycles[repeats/2]);
fprintf(stderr, "\n");
fflush(stderr);
printf("%lu %lu %lu ", (unsigned long)len, (unsigned long)sublists, (unsigned long)sprinkles);
printf("%.0f %.0f %.0f ", topdown_wall[0], topdown_cpu[0], topdown_cycles[0]);
printf("%.0f %.0f %.0f ", topdown_wall[repeats/2], topdown_cpu[repeats/2], topdown_cycles[repeats/2]);
printf("%.0f %.0f %.0f ", nominal_wall[0], nominal_cpu[0], nominal_cycles[0]);
printf("%.0f %.0f %.0f ", nominal_wall[repeats/2], nominal_cpu[repeats/2], nominal_cycles[repeats/2]);
printf("%.0f %.0f %.0f ", bottomup_wall[0], bottomup_cpu[0], bottomup_cycles[0]);
printf("%.0f %.0f %.0f\n", bottomup_wall[repeats/2], bottomup_cpu[repeats/2], bottomup_cycles[repeats/2]);
return 0;
}
Finally, here is the