git-commit-vandalism/t/helper/test-mergesort.c
René Scharfe b378c2ff1e test-mergesort: use DEFINE_LIST_SORT
Build a typed sort function for the mergesort performance test tool
using DEFINE_LIST_SORT instead of calling llist_mergesort().  This gets
rid of the next pointer accessor functions and improves the performance
at the cost of a slightly higher object text size.

Before:
0071.12: llist_mergesort() unsorted    0.24(0.22+0.01)
0071.14: llist_mergesort() sorted      0.12(0.10+0.01)
0071.16: llist_mergesort() reversed    0.12(0.10+0.01)

__TEXT	__DATA	__OBJC	others	dec	hex
6407	276	0	24701	31384	7a98	t/helper/test-mergesort.o

With this patch:
0071.12: DEFINE_LIST_SORT unsorted     0.22(0.21+0.01)
0071.14: DEFINE_LIST_SORT sorted       0.11(0.10+0.01)
0071.16: DEFINE_LIST_SORT reversed     0.11(0.10+0.01)

__TEXT	__DATA	__OBJC	others	dec	hex
6615	276	0	25832	32723	7fd3	t/helper/test-mergesort.o

Signed-off-by: René Scharfe <l.s.r@web.de>
Signed-off-by: Junio C Hamano <gitster@pobox.com>
2022-07-17 15:20:38 -07:00

392 lines
7.9 KiB
C
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "test-tool.h"
#include "cache.h"
#include "mergesort.h"
static uint32_t minstd_rand(uint32_t *state)
{
*state = (uint64_t)*state * 48271 % 2147483647;
return *state;
}
struct line {
char *text;
struct line *next;
};
DEFINE_LIST_SORT(static, sort_lines, struct line, next);
static int compare_strings(const struct line *x, const struct line *y)
{
return strcmp(x->text, y->text);
}
static int sort_stdin(void)
{
struct line *line, *p = NULL, *lines = NULL;
struct strbuf sb = STRBUF_INIT;
while (!strbuf_getline(&sb, stdin)) {
line = xmalloc(sizeof(struct line));
line->text = strbuf_detach(&sb, NULL);
if (p) {
line->next = p->next;
p->next = line;
} else {
line->next = NULL;
lines = line;
}
p = line;
}
sort_lines(&lines, compare_strings);
while (lines) {
puts(lines->text);
lines = lines->next;
}
return 0;
}
static void dist_sawtooth(int *arr, int n, int m)
{
int i;
for (i = 0; i < n; i++)
arr[i] = i % m;
}
static void dist_rand(int *arr, int n, int m)
{
int i;
uint32_t seed = 1;
for (i = 0; i < n; i++)
arr[i] = minstd_rand(&seed) % m;
}
static void dist_stagger(int *arr, int n, int m)
{
int i;
for (i = 0; i < n; i++)
arr[i] = (i * m + i) % n;
}
static void dist_plateau(int *arr, int n, int m)
{
int i;
for (i = 0; i < n; i++)
arr[i] = (i < m) ? i : m;
}
static void dist_shuffle(int *arr, int n, int m)
{
int i, j, k;
uint32_t seed = 1;
for (i = j = 0, k = 1; i < n; i++)
arr[i] = minstd_rand(&seed) % m ? (j += 2) : (k += 2);
}
#define DIST(name) { #name, dist_##name }
static struct dist {
const char *name;
void (*fn)(int *arr, int n, int m);
} dist[] = {
DIST(sawtooth),
DIST(rand),
DIST(stagger),
DIST(plateau),
DIST(shuffle),
};
static const struct dist *get_dist_by_name(const char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(dist); i++) {
if (!strcmp(dist[i].name, name))
return &dist[i];
}
return NULL;
}
static void mode_copy(int *arr, int n)
{
/* nothing */
}
static void mode_reverse(int *arr, int n)
{
int i, j;
for (i = 0, j = n - 1; i < j; i++, j--)
SWAP(arr[i], arr[j]);
}
static void mode_reverse_1st_half(int *arr, int n)
{
mode_reverse(arr, n / 2);
}
static void mode_reverse_2nd_half(int *arr, int n)
{
int half = n / 2;
mode_reverse(arr + half, n - half);
}
static int compare_ints(const void *av, const void *bv)
{
const int *ap = av, *bp = bv;
int a = *ap, b = *bp;
return (a > b) - (a < b);
}
static void mode_sort(int *arr, int n)
{
QSORT(arr, n, compare_ints);
}
static void mode_dither(int *arr, int n)
{
int i;
for (i = 0; i < n; i++)
arr[i] += i % 5;
}
static void unriffle(int *arr, int n, int *tmp)
{
int i, j;
COPY_ARRAY(tmp, arr, n);
for (i = j = 0; i < n; i += 2)
arr[j++] = tmp[i];
for (i = 1; i < n; i += 2)
arr[j++] = tmp[i];
}
static void unriffle_recursively(int *arr, int n, int *tmp)
{
if (n > 1) {
int half = n / 2;
unriffle(arr, n, tmp);
unriffle_recursively(arr, half, tmp);
unriffle_recursively(arr + half, n - half, tmp);
}
}
static void mode_unriffle(int *arr, int n)
{
int *tmp;
ALLOC_ARRAY(tmp, n);
unriffle_recursively(arr, n, tmp);
free(tmp);
}
static unsigned int prev_pow2(unsigned int n)
{
unsigned int pow2 = 1;
while (pow2 * 2 < n)
pow2 *= 2;
return pow2;
}
static void unriffle_recursively_skewed(int *arr, int n, int *tmp)
{
if (n > 1) {
int pow2 = prev_pow2(n);
int rest = n - pow2;
unriffle(arr + pow2 - rest, rest * 2, tmp);
unriffle_recursively_skewed(arr, pow2, tmp);
unriffle_recursively_skewed(arr + pow2, rest, tmp);
}
}
static void mode_unriffle_skewed(int *arr, int n)
{
int *tmp;
ALLOC_ARRAY(tmp, n);
unriffle_recursively_skewed(arr, n, tmp);
free(tmp);
}
#define MODE(name) { #name, mode_##name }
static struct mode {
const char *name;
void (*fn)(int *arr, int n);
} mode[] = {
MODE(copy),
MODE(reverse),
MODE(reverse_1st_half),
MODE(reverse_2nd_half),
MODE(sort),
MODE(dither),
MODE(unriffle),
MODE(unriffle_skewed),
};
static const struct mode *get_mode_by_name(const char *name)
{
int i;
for (i = 0; i < ARRAY_SIZE(mode); i++) {
if (!strcmp(mode[i].name, name))
return &mode[i];
}
return NULL;
}
static int generate(int argc, const char **argv)
{
const struct dist *dist = NULL;
const struct mode *mode = NULL;
int i, n, m, *arr;
if (argc != 4)
return 1;
dist = get_dist_by_name(argv[0]);
mode = get_mode_by_name(argv[1]);
n = strtol(argv[2], NULL, 10);
m = strtol(argv[3], NULL, 10);
if (!dist || !mode)
return 1;
ALLOC_ARRAY(arr, n);
dist->fn(arr, n, m);
mode->fn(arr, n);
for (i = 0; i < n; i++)
printf("%08x\n", arr[i]);
free(arr);
return 0;
}
static struct stats {
int get_next, set_next, compare;
} stats;
struct number {
int value, rank;
struct number *next;
};
DEFINE_LIST_SORT_DEBUG(static, sort_numbers, struct number, next,
stats.get_next++, stats.set_next++);
static int compare_numbers(const struct number *an, const struct number *bn)
{
int a = an->value, b = bn->value;
stats.compare++;
return (a > b) - (a < b);
}
static void clear_numbers(struct number *list)
{
while (list) {
struct number *next = list->next;
free(list);
list = next;
}
}
static int test(const struct dist *dist, const struct mode *mode, int n, int m)
{
int *arr;
size_t i;
struct number *curr, *list, **tail;
int is_sorted = 1;
int is_stable = 1;
const char *verdict;
int result = -1;
ALLOC_ARRAY(arr, n);
dist->fn(arr, n, m);
mode->fn(arr, n);
for (i = 0, tail = &list; i < n; i++) {
curr = xmalloc(sizeof(*curr));
curr->value = arr[i];
curr->rank = i;
*tail = curr;
tail = &curr->next;
}
*tail = NULL;
stats.get_next = stats.set_next = stats.compare = 0;
sort_numbers(&list, compare_numbers);
QSORT(arr, n, compare_ints);
for (i = 0, curr = list; i < n && curr; i++, curr = curr->next) {
if (arr[i] != curr->value)
is_sorted = 0;
if (curr->next && curr->value == curr->next->value &&
curr->rank >= curr->next->rank)
is_stable = 0;
}
if (i < n) {
verdict = "too short";
} else if (curr) {
verdict = "too long";
} else if (!is_sorted) {
verdict = "not sorted";
} else if (!is_stable) {
verdict = "unstable";
} else {
verdict = "OK";
result = 0;
}
printf("%-9s %-16s %8d %8d %8d %8d %8d %s\n",
dist->name, mode->name, n, m, stats.get_next, stats.set_next,
stats.compare, verdict);
clear_numbers(list);
free(arr);
return result;
}
/*
* A version of the qsort certification program from "Engineering a Sort
* Function" by Bentley and McIlroy, Software—Practice and Experience,
* Volume 23, Issue 11, 12491265 (November 1993).
*/
static int run_tests(int argc, const char **argv)
{
const char *argv_default[] = { "100", "1023", "1024", "1025" };
if (!argc)
return run_tests(ARRAY_SIZE(argv_default), argv_default);
printf("%-9s %-16s %8s %8s %8s %8s %8s %s\n",
"distribut", "mode", "n", "m", "get_next", "set_next",
"compare", "verdict");
while (argc--) {
int i, j, m, n = strtol(*argv++, NULL, 10);
for (i = 0; i < ARRAY_SIZE(dist); i++) {
for (j = 0; j < ARRAY_SIZE(mode); j++) {
for (m = 1; m < 2 * n; m *= 2) {
if (test(&dist[i], &mode[j], n, m))
return 1;
}
}
}
}
return 0;
}
int cmd__mergesort(int argc, const char **argv)
{
int i;
const char *sep;
if (argc == 6 && !strcmp(argv[1], "generate"))
return generate(argc - 2, argv + 2);
if (argc == 2 && !strcmp(argv[1], "sort"))
return sort_stdin();
if (argc > 1 && !strcmp(argv[1], "test"))
return run_tests(argc - 2, argv + 2);
fprintf(stderr, "usage: test-tool mergesort generate <distribution> <mode> <n> <m>\n");
fprintf(stderr, " or: test-tool mergesort sort\n");
fprintf(stderr, " or: test-tool mergesort test [<n>...]\n");
fprintf(stderr, "\n");
for (i = 0, sep = "distributions: "; i < ARRAY_SIZE(dist); i++, sep = ", ")
fprintf(stderr, "%s%s", sep, dist[i].name);
fprintf(stderr, "\n");
for (i = 0, sep = "modes: "; i < ARRAY_SIZE(mode); i++, sep = ", ")
fprintf(stderr, "%s%s", sep, mode[i].name);
fprintf(stderr, "\n");
return 129;
}