summaryrefslogtreecommitdiff
path: root/test_utils
diff options
context:
space:
mode:
authorAlex Richardson <Alexander.Richardson@cl.cam.ac.uk>2020-09-17 18:28:17 +0100
committerAlex Richardson <Alexander.Richardson@cl.cam.ac.uk>2022-01-15 13:47:21 +0000
commit10a9214ac65e171f08589ef0ec94ffcbccbfcc49 (patch)
tree69acfc0e0ab9faaa856f692a49fc0390d8c835dc /test_utils
parentb9675888c288fb8b293a69783712bbc2a4573773 (diff)
downloadlibarchive-10a9214ac65e171f08589ef0ec94ffcbccbfcc49.tar.gz
Avoid millions of rand() calls() when running tests
Many tests use a loop calling rand() to fill buffers with test data. As these calls cannot be inlined, this adds up to noticeable overhead: For example, running on QEMU RISC-V the test_write_format_7zip_large_copy test took ~22 seconds before and with this change it's ~17 seconds. This change uses a simpler xorshift64 random number generator that can be inlined into the loop filling the data buffer. By default the seed for this RNG is rand(), but it can be overwritten by setting the TEST_RANDOM_SEED environment variable. For a native build the difference is much less noticeable, but it's still measurable: test_write_format_7zip_large_copy takes 314.9 ms ± 3.9 ms before and 227.8 ms ± 5.8 ms after (i.e. 38% faster for that test).
Diffstat (limited to 'test_utils')
-rw-r--r--test_utils/test_utils.c81
-rw-r--r--test_utils/test_utils.h5
2 files changed, 86 insertions, 0 deletions
diff --git a/test_utils/test_utils.c b/test_utils/test_utils.c
index 8ea3d3c4..db6c31b2 100644
--- a/test_utils/test_utils.c
+++ b/test_utils/test_utils.c
@@ -26,8 +26,11 @@
#include "test_utils.h"
+#include <errno.h>
#include <stdlib.h>
+#include <stdio.h>
#include <string.h>
+#include <assert.h>
/* Filter tests against a glob pattern. Returns non-zero if test matches
* pattern, zero otherwise. A '^' at the beginning of the pattern negates
@@ -122,3 +125,81 @@ int get_test_set(int *test_set, int limit, const char *test,
}
return ((idx == 0)?-1:idx);
}
+
+static inline uint64_t
+xorshift64(uint64_t *state)
+{
+ uint64_t x = *state;
+ x ^= x << 13;
+ x ^= x >> 7;
+ x ^= x << 17;
+ *state = x;
+ return (x);
+}
+
+/*
+ * Fill a buffer with reproducible pseudo-random data using a simple xorshift
+ * algorithm. Originally, most tests filled buffers with a loop that calls
+ * rand() once for each byte. However, this initialization can be extremely
+ * slow when running on emulated platforms such as QEMU where 16M calls to
+ * rand() take a long time: Before the test_write_format_7zip_large_copy test
+ * took ~22 seconds, whereas using a xorshift random number generator (that can
+ * be inlined) reduces it to ~17 seconds on QEMU RISC-V.
+ */
+void
+fill_with_pseudorandom_data_seed(uint64_t seed, void *buffer, size_t size)
+{
+ uint64_t *aligned_buffer;
+ size_t num_values;
+ size_t i;
+ size_t unaligned_suffix;
+ size_t unaligned_prefix = 0;
+ /*
+ * To avoid unaligned stores we only fill the aligned part of the buffer
+ * with pseudo-random data and fill the unaligned prefix with 0xab and
+ * the suffix with 0xcd.
+ */
+ if ((uintptr_t)buffer % sizeof(uint64_t)) {
+ unaligned_prefix =
+ sizeof(uint64_t) - (uintptr_t)buffer % sizeof(uint64_t);
+ aligned_buffer =
+ (uint64_t *)((char *)buffer + unaligned_prefix);
+ memset(buffer, 0xab, unaligned_prefix);
+ } else {
+ aligned_buffer = (uint64_t *)buffer;
+ }
+ assert((uintptr_t)aligned_buffer % sizeof(uint64_t) == 0);
+ num_values = (size - unaligned_prefix) / sizeof(uint64_t);
+ unaligned_suffix =
+ size - unaligned_prefix - num_values * sizeof(uint64_t);
+ for (i = 0; i < num_values; i++) {
+ aligned_buffer[i] = xorshift64(&seed);
+ }
+ if (unaligned_suffix) {
+ memset((char *)buffer + size - unaligned_suffix, 0xcd,
+ unaligned_suffix);
+ }
+}
+
+void
+fill_with_pseudorandom_data(void *buffer, size_t size)
+{
+ uint64_t seed;
+ const char* seed_str;
+ /*
+ * Check if a seed has been specified in the environment, otherwise fall
+ * back to using rand() as a seed.
+ */
+ if ((seed_str = getenv("TEST_RANDOM_SEED")) != NULL) {
+ errno = 0;
+ seed = strtoull(seed_str, NULL, 10);
+ if (errno != 0) {
+ fprintf(stderr, "strtoull(%s) failed: %s", seed_str,
+ strerror(errno));
+ seed = rand();
+ }
+ } else {
+ seed = rand();
+ }
+ fill_with_pseudorandom_data_seed(seed, buffer, size);
+}
diff --git a/test_utils/test_utils.h b/test_utils/test_utils.h
index 164c528f..3f61f6b2 100644
--- a/test_utils/test_utils.h
+++ b/test_utils/test_utils.h
@@ -27,6 +27,9 @@
#ifndef TEST_UTILS_H
#define TEST_UTILS_H
+#include <stddef.h>
+#include <stdint.h>
+
struct test_list_t
{
void (*func)(void);
@@ -35,5 +38,7 @@ struct test_list_t
};
int get_test_set(int *, int, const char *, struct test_list_t *);
+void fill_with_pseudorandom_data(void* buffer, size_t size);
+void fill_with_pseudorandom_data_seed(uint64_t seed, void* buffer, size_t size);
#endif /* TEST_UTILS_H */