I have this mergesort implementation that has exactly the same API as qsort
:
mergesort.h:
#ifndef MERGESORT_H
#define MERGESORT_H
#include <stdlib.h>
#ifdef __cplusplus
extern "C" {
#endif
void my_mergesort(void* base,
size_t num,
size_t size,
int (*compar)(const void*, const void*));
#ifdef __cplusplus
}
#endif
#endif /* MERGESORT_H */
mergesort.c:
#include "mergesort.h"
#include <stdlib.h>
#include <string.h>
static void mergesort_impl(void* source,
void* target,
void* tmp,
size_t size,
size_t offset,
size_t range_length,
int (*compar)(const void*, const void*))
{
if (range_length < 2)
{
return;
}
size_t half_range_length = range_length >> 1;
mergesort_impl(target,
source,
tmp,
size,
offset,
half_range_length,
compar);
mergesort_impl(target,
source,
tmp,
size,
offset + half_range_length,
range_length - half_range_length,
compar);
void* left_subarray_pointer = source + offset * size;
void* left_subarray_pointer_bound = left_subarray_pointer +
half_range_length * size;
void* right_subarray_pointer = left_subarray_pointer_bound;
void* right_subarray_pointer_bound = source + (offset + range_length)
* size;
void* target_array_pointer = target + offset * size;
while (left_subarray_pointer < left_subarray_pointer_bound
&& right_subarray_pointer < right_subarray_pointer_bound)
{
if (compar(right_subarray_pointer, left_subarray_pointer) < 0)
{
memcpy(target_array_pointer, right_subarray_pointer, size);
target_array_pointer += size;
right_subarray_pointer += size;
}
else
{
memcpy(target_array_pointer, left_subarray_pointer, size);
target_array_pointer += size;
left_subarray_pointer += size;
}
}
memcpy(target_array_pointer,
left_subarray_pointer,
left_subarray_pointer_bound - left_subarray_pointer);
memcpy(target_array_pointer,
right_subarray_pointer,
right_subarray_pointer_bound - right_subarray_pointer);
}
void my_mergesort(void* base,
size_t num,
size_t size,
int (*compar)(const void*, const void*))
{
void* tmp = malloc(size);
void* aux = malloc(num * size);
memcpy(aux, base, num * size);
mergesort_impl(aux, base, tmp, size, 0, num, compar);
free(tmp);
free(aux);
}
main.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <time.h>
#include <sys/time.h>
#include "mergesort.h"
static int compar(const void* a, const void* b)
{
return (*(int*) a) - (*(int*) b);
}
static int* create_random_int_array(size_t length)
{
int* array = malloc(length * sizeof(int));
srand(time(NULL));
for (size_t i = 0; i < length; ++i)
{
array[i] = rand() % (1000 * 1000);
}
return array;
}
static long get_milliseconds()
{
struct timeval tv;
gettimeofday(&tv, NULL);
return tv.tv_sec * 1000 + tv.tv_usec / 1000;
}
int arrays_are_same(int* arr1, int* arr2, size_t length)
{
for (size_t i = 0; i < length; ++i)
{
if (arr1[i] != arr2[i])
{
return 0;
}
}
return 1;
}
static const size_t ARRAY_SIZE = 10 * 1000 * 1000;
int main() {
int* arr1 = create_random_int_array(ARRAY_SIZE);
int* arr2 = malloc(sizeof(int) * ARRAY_SIZE);
memcpy(arr2, arr1, sizeof(int) * ARRAY_SIZE);
long start_time = get_milliseconds();
my_mergesort(arr1, ARRAY_SIZE, sizeof(int), compar);
long end_time = get_milliseconds();
printf("my_mergesort took %ld milliseconds.\n", end_time - start_time);
start_time = get_milliseconds();
qsort(arr2, ARRAY_SIZE, sizeof(int), compar);
end_time = get_milliseconds();
printf("qsort took %ld milliseconds.\n", end_time - start_time);
printf("Arrays are identical: %d\n", arrays_are_same(arr1,
arr2,
ARRAY_SIZE));
free(arr1);
free(arr2);
}
Any critique is much appreciated. I am especially interested to hear how can I improve the performance of my merge sort, as it runs around 3 times slower than qsort
.
source + offset * size
? AFAIK a compiler needs to know the size of the objects pointed to, so it can scale the addend properly, and the size of data pointed byvoid* source
is undefined. \$\endgroup\$source
is avoid*
, so thatsource + offset * size
means that "moveoffset * size
bytes forward fromsource
. Also, working withvoid*
is the only way to simulate generic programming in C. \$\endgroup\$register
keyword.. \$\endgroup\$