2024-05-02 20:00:04 -07:00
|
|
|
#include <inttypes.h>
|
|
|
|
#include <string.h>
|
|
|
|
#include <stdlib.h>
|
|
|
|
#include <time.h>
|
|
|
|
#include <sys/time.h>
|
|
|
|
#include <unistd.h>
|
|
|
|
|
|
|
|
#include "test_help.h"
|
|
|
|
#include "../zmalloc.h"
|
|
|
|
#include "../crc64.h"
|
|
|
|
#include "../crcspeed.h"
|
|
|
|
#include "../crccombine.h"
|
|
|
|
|
|
|
|
static void genBenchmarkRandomData(char *data, int count);
|
|
|
|
static int bench_crc64(unsigned char *data, uint64_t size, long long passes, uint64_t check, char *name, int csv);
|
|
|
|
static void bench_combine(char *label, uint64_t size, uint64_t expect, int csv);
|
|
|
|
|
|
|
|
long long _ustime(void) {
|
|
|
|
struct timeval tv;
|
|
|
|
long long ust;
|
|
|
|
|
|
|
|
gettimeofday(&tv, NULL);
|
2024-05-28 09:27:51 -07:00
|
|
|
ust = ((long long)tv.tv_sec) * 1000000;
|
2024-05-02 20:00:04 -07:00
|
|
|
ust += tv.tv_usec;
|
|
|
|
return ust;
|
|
|
|
}
|
|
|
|
|
|
|
|
static int bench_crc64(unsigned char *data, uint64_t size, long long passes, uint64_t check, char *name, int csv) {
|
Fix unit test issues on address sanitizer and fortify (#437)
This commit does four things:
1. On various images, the linker was not able to correctly load the flto
optimizations from the archive generated for unit tests, and was
throwing errors. I was able to solve this by updating the plugin for the
fortify test, but was unable to reproduce it on the ASAN tests or find a
solution. So I decided to go with a single solution for now, which was
to just disable the linker optimizations for those tests. This shouldn't
weaken the protections provided by ASAN.
2. The change to remove flto for some reason caused some odd inlining
behavior in the intset test, that I wasn't really able to understand.
The error was basically that we were doing a 4 byte write, starting at
byte offset 8, for the first addition to listpack that was of size 10.
Practically this has no effect, since I'm not aware of any allocator
that would give us a 10 byte block as opposed to 12 (or more likely 16)
bytes. The isn't the correct behavior, since an uninitialized listpack
defaults to 16bit encoding, which should only be writing 2 bytes. I
rabbit holed like 2 hours into this, and gave up and just ignored the
warning on the file.
3. Now that address sanitizer was correctly running, it picked up two
issues. A memory leak and uninitialized value, so those were easy to
fix.
4. There is also a small change to the fortify to build the test up
front instead of later, this is just to be consistent with other tests
and has no functional change.
Signed-off-by: Madelyn Olson <madelyneolson@gmail.com>
2024-05-05 22:00:08 -07:00
|
|
|
uint64_t min = size, hash = 0;
|
2024-05-02 20:00:04 -07:00
|
|
|
long long original_start = _ustime(), original_end;
|
2024-05-28 09:27:51 -07:00
|
|
|
for (long long i = passes; i > 0; i--) {
|
2024-05-02 20:00:04 -07:00
|
|
|
hash = crc64(0, data, size);
|
|
|
|
}
|
|
|
|
original_end = _ustime();
|
|
|
|
min = (original_end - original_start) * 1000 / passes;
|
|
|
|
/* approximate nanoseconds without nstime */
|
|
|
|
if (csv) {
|
2024-05-28 09:27:51 -07:00
|
|
|
printf("%s,%" PRIu64 ",%" PRIu64 ",%d\n", name, size, (1000 * size) / min, hash == check);
|
2024-05-02 20:00:04 -07:00
|
|
|
} else {
|
2024-05-28 09:27:51 -07:00
|
|
|
TEST_PRINT_INFO("test size=%" PRIu64 " algorithm=%s %" PRIu64 " M/sec matches=%d", size, name,
|
|
|
|
(1000 * size) / min, hash == check);
|
2024-05-02 20:00:04 -07:00
|
|
|
}
|
|
|
|
return hash != check;
|
|
|
|
}
|
|
|
|
|
|
|
|
const uint64_t BENCH_RPOLY = UINT64_C(0x95ac9329ac4bc9b5);
|
|
|
|
|
|
|
|
static void bench_combine(char *label, uint64_t size, uint64_t expect, int csv) {
|
|
|
|
uint64_t min = size, start = expect, thash = expect ^ (expect >> 17);
|
|
|
|
long long original_start = _ustime(), original_end;
|
2024-05-28 09:27:51 -07:00
|
|
|
for (int i = 0; i < 1000; i++) {
|
2024-05-02 20:00:04 -07:00
|
|
|
crc64_combine(thash, start, size, BENCH_RPOLY, 64);
|
|
|
|
}
|
|
|
|
original_end = _ustime();
|
|
|
|
/* ran 1000 times, want ns per, counted us per 1000 ... */
|
|
|
|
min = original_end - original_start;
|
|
|
|
if (csv) {
|
|
|
|
printf("%s,%" PRIu64 ",%" PRIu64 "\n", label, size, min);
|
|
|
|
} else {
|
|
|
|
printf("%s size=%" PRIu64 " in %" PRIu64 " nsec\n", label, size, min);
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
static void genBenchmarkRandomData(char *data, int count) {
|
|
|
|
static uint32_t state = 1234;
|
|
|
|
int i = 0;
|
|
|
|
|
|
|
|
while (count--) {
|
2024-05-28 09:27:51 -07:00
|
|
|
state = (state * 1103515245 + 12345);
|
|
|
|
data[i++] = '0' + ((state >> 16) & 63);
|
2024-05-02 20:00:04 -07:00
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
/* This is a special unit test useful for benchmarking crc64combine performance. The
|
|
|
|
* benchmarking is only done when the tests are invoked with a single test target,
|
|
|
|
* like 'valkey-unit-tests --single test_crc64combine.c --crc 16384'. */
|
|
|
|
int test_crc64combine(int argc, char **argv, int flags) {
|
|
|
|
if (!(flags & UNIT_TEST_SINGLE)) {
|
|
|
|
return 0;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t crc64_test_size = 0;
|
|
|
|
int i, lastarg, csv = 0, loop = 0, combine = 0;
|
|
|
|
again:
|
|
|
|
for (i = 3; i < argc; i++) {
|
2024-05-28 09:27:51 -07:00
|
|
|
lastarg = (i == (argc - 1));
|
|
|
|
if (!strcmp(argv[i], "--help")) {
|
2024-05-02 20:00:04 -07:00
|
|
|
goto usage;
|
2024-05-28 09:27:51 -07:00
|
|
|
} else if (!strcmp(argv[i], "--csv")) {
|
2024-05-02 20:00:04 -07:00
|
|
|
csv = 1;
|
2024-05-28 09:27:51 -07:00
|
|
|
} else if (!strcmp(argv[i], "-l")) {
|
2024-05-02 20:00:04 -07:00
|
|
|
loop = 1;
|
2024-05-28 09:27:51 -07:00
|
|
|
} else if (!strcmp(argv[i], "--crc")) {
|
2024-05-02 20:00:04 -07:00
|
|
|
if (lastarg) goto invalid;
|
|
|
|
crc64_test_size = atoll(argv[++i]);
|
2024-05-28 09:27:51 -07:00
|
|
|
} else if (!strcmp(argv[i], "--combine")) {
|
2024-05-02 20:00:04 -07:00
|
|
|
combine = 1;
|
|
|
|
} else {
|
2024-05-28 09:27:51 -07:00
|
|
|
invalid:
|
|
|
|
printf("Invalid option \"%s\" or option argument missing\n\n", argv[i]);
|
|
|
|
usage:
|
|
|
|
printf("Usage: --single test_crc64combine.c [OPTIONS]\n\n"
|
|
|
|
" --csv Output in CSV format\n"
|
|
|
|
" -l Loop. Run the tests forever\n"
|
|
|
|
" --crc <bytes> Benchmark crc64 faster options, using a buffer this big, and quit when done.\n"
|
|
|
|
" --combine Benchmark crc64 combine value ranges and timings.\n");
|
2024-05-02 20:00:04 -07:00
|
|
|
return 1;
|
|
|
|
}
|
|
|
|
}
|
|
|
|
|
|
|
|
int init_this_loop = 1;
|
|
|
|
long long init_start, init_end;
|
|
|
|
|
|
|
|
do {
|
2024-05-28 09:27:51 -07:00
|
|
|
unsigned char *data = NULL;
|
2024-05-02 20:00:04 -07:00
|
|
|
uint64_t passes = 0;
|
|
|
|
if (crc64_test_size) {
|
|
|
|
data = zmalloc(crc64_test_size);
|
2024-05-28 09:27:51 -07:00
|
|
|
genBenchmarkRandomData((char *)data, crc64_test_size);
|
2024-05-02 20:00:04 -07:00
|
|
|
/* We want to hash about 1 gig of data in total, looped, to get a good
|
|
|
|
* idea of our performance.
|
|
|
|
*/
|
|
|
|
passes = (UINT64_C(0x100000000) / crc64_test_size);
|
|
|
|
passes = passes >= 2 ? passes : 2;
|
|
|
|
passes = passes <= 1000 ? passes : 1000;
|
|
|
|
}
|
|
|
|
|
|
|
|
crc64_init();
|
|
|
|
/* warm up the cache */
|
2024-05-28 09:27:51 -07:00
|
|
|
set_crc64_cutoffs(crc64_test_size + 1, crc64_test_size + 1);
|
2024-05-02 20:00:04 -07:00
|
|
|
uint64_t expect = crc64(0, data, crc64_test_size);
|
|
|
|
|
|
|
|
if (!combine && crc64_test_size) {
|
|
|
|
if (csv && init_this_loop) printf("algorithm,buffer,performance,crc64_matches\n");
|
|
|
|
|
|
|
|
/* get the single-character version for single-byte Redis behavior */
|
2024-05-28 09:27:51 -07:00
|
|
|
set_crc64_cutoffs(0, crc64_test_size + 1);
|
2024-05-28 13:36:54 -04:00
|
|
|
if (bench_crc64(data, crc64_test_size, passes, expect, "crc_1byte", csv)) {
|
|
|
|
zfree(data);
|
|
|
|
data = NULL;
|
|
|
|
return 1;
|
|
|
|
}
|
2024-05-02 20:00:04 -07:00
|
|
|
|
2024-05-28 09:27:51 -07:00
|
|
|
set_crc64_cutoffs(crc64_test_size + 1, crc64_test_size + 1);
|
2024-05-02 20:00:04 -07:00
|
|
|
/* run with 8-byte "single" path, crcfaster */
|
2024-05-28 13:36:54 -04:00
|
|
|
if (bench_crc64(data, crc64_test_size, passes, expect, "crcspeed", csv)) {
|
|
|
|
zfree(data);
|
|
|
|
data = NULL;
|
|
|
|
return 1;
|
|
|
|
}
|
2024-05-02 20:00:04 -07:00
|
|
|
|
|
|
|
/* run with dual 8-byte paths */
|
2024-05-28 09:27:51 -07:00
|
|
|
set_crc64_cutoffs(1, crc64_test_size + 1);
|
2024-05-28 13:36:54 -04:00
|
|
|
if (bench_crc64(data, crc64_test_size, passes, expect, "crcdual", csv)) {
|
|
|
|
zfree(data);
|
|
|
|
data = NULL;
|
|
|
|
return 1;
|
|
|
|
}
|
2024-05-02 20:00:04 -07:00
|
|
|
|
|
|
|
/* run with tri 8-byte paths */
|
|
|
|
set_crc64_cutoffs(1, 1);
|
2024-05-28 13:36:54 -04:00
|
|
|
if (bench_crc64(data, crc64_test_size, passes, expect, "crctri", csv)) {
|
|
|
|
zfree(data);
|
|
|
|
data = NULL;
|
|
|
|
return 1;
|
|
|
|
}
|
2024-05-02 20:00:04 -07:00
|
|
|
|
|
|
|
/* Be free memory region, be free. */
|
|
|
|
zfree(data);
|
|
|
|
data = NULL;
|
|
|
|
}
|
|
|
|
|
|
|
|
uint64_t INIT_SIZE = UINT64_C(0xffffffffffffffff);
|
|
|
|
if (combine) {
|
|
|
|
if (init_this_loop) {
|
|
|
|
init_start = _ustime();
|
2024-05-28 09:27:51 -07:00
|
|
|
crc64_combine(UINT64_C(0xdeadbeefdeadbeef), UINT64_C(0xfeebdaedfeebdaed), INIT_SIZE, BENCH_RPOLY, 64);
|
2024-05-02 20:00:04 -07:00
|
|
|
init_end = _ustime();
|
|
|
|
|
|
|
|
init_end -= init_start;
|
|
|
|
init_end *= 1000;
|
|
|
|
if (csv) {
|
|
|
|
printf("operation,size,nanoseconds\n");
|
|
|
|
printf("init_64,%" PRIu64 ",%" PRIu64 "\n", INIT_SIZE, (uint64_t)init_end);
|
|
|
|
} else {
|
|
|
|
TEST_PRINT_INFO("init_64 size=%" PRIu64 " in %" PRIu64 " nsec", INIT_SIZE, (uint64_t)init_end);
|
|
|
|
}
|
|
|
|
/* use the hash itself as the size (unpredictable) */
|
|
|
|
bench_combine("hash_as_size_combine", crc64_test_size, expect, csv);
|
|
|
|
|
|
|
|
/* let's do something big (predictable, so fast) */
|
|
|
|
bench_combine("largest_combine", INIT_SIZE, expect, csv);
|
|
|
|
}
|
|
|
|
bench_combine("combine", crc64_test_size, expect, csv);
|
|
|
|
}
|
|
|
|
init_this_loop = 0;
|
|
|
|
/* step down by ~1.641 for a range of test sizes */
|
|
|
|
crc64_test_size -= (crc64_test_size >> 2) + (crc64_test_size >> 3) + (crc64_test_size >> 6);
|
|
|
|
} while (crc64_test_size > 3);
|
|
|
|
if (loop) goto again;
|
|
|
|
return 0;
|
|
|
|
}
|