|
1 | 1 | #include "streamvbyte.h" |
| 2 | +#include "counters/bench.h" |
| 3 | + |
2 | 4 | #include <assert.h> |
3 | | -#include <math.h> |
| 5 | +#include <stdint.h> |
4 | 6 | #include <stdio.h> |
5 | 7 | #include <stdlib.h> |
6 | | -#include <sys/resource.h> |
7 | | -#include <time.h> |
8 | | - |
9 | 8 | #include <string.h> |
10 | 9 |
|
11 | 10 | #ifdef __clang__ |
12 | 11 | #pragma clang diagnostic ignored "-Wdeclaration-after-statement" |
13 | | -#pragma clang diagnostic ignored "-Wunused-variable" |
14 | 12 | #endif |
15 | 13 |
|
16 | | -static void punt(long long n, char *s) { |
17 | | - int i = 127; |
18 | | - int sign = 0; |
19 | | - if (n < 0) { |
20 | | - sign = 1; |
21 | | - n = -n; |
| 14 | +#define N 500000U |
| 15 | + |
| 16 | +typedef struct { |
| 17 | + const uint32_t *datain; |
| 18 | + uint8_t *compressedbuffer; |
| 19 | + uint32_t *recovdata; |
| 20 | + size_t compsize; |
| 21 | + size_t n; |
| 22 | +} svb_ctx; |
| 23 | + |
| 24 | +static void bench_encode(void *ud) { |
| 25 | + svb_ctx *ctx = (svb_ctx *)ud; |
| 26 | + ctx->compsize = streamvbyte_encode(ctx->datain, (uint32_t)ctx->n, ctx->compressedbuffer); |
| 27 | +} |
| 28 | + |
| 29 | +static void bench_decode(void *ud) { |
| 30 | + svb_ctx *ctx = (svb_ctx *)ud; |
| 31 | + size_t r = streamvbyte_decode(ctx->compressedbuffer, ctx->recovdata, (uint32_t)ctx->n); |
| 32 | + (void)r; |
| 33 | +} |
| 34 | + |
| 35 | +static void print_aggregate(const char *label, size_t bytes_processed, |
| 36 | + const counters_event_aggregate *agg, int have_counters) { |
| 37 | + double ns = counters_event_aggregate_elapsed_ns(agg); |
| 38 | + double gbs = ns > 0.0 ? (double)bytes_processed / ns : 0.0; |
| 39 | + double uints_per_sec = ns > 0.0 ? (double)(bytes_processed / sizeof(uint32_t)) * 1e9 / ns : 0.0; |
| 40 | + |
| 41 | + printf(" %-7s %8.0f ns/op %6.2f GB/s %12.0f uints/s iters=%d", |
| 42 | + label, ns, gbs, uints_per_sec, |
| 43 | + counters_event_aggregate_iteration_count(agg)); |
| 44 | + |
| 45 | + if (have_counters) { |
| 46 | + double instr = counters_event_aggregate_instructions(agg); |
| 47 | + double cycles = counters_event_aggregate_cycles(agg); |
| 48 | + double branches = counters_event_aggregate_branches(agg); |
| 49 | + double branch_misses = counters_event_aggregate_branch_misses(agg); |
| 50 | + double cache_misses = counters_event_aggregate_cache_misses(agg); |
| 51 | + double ipc = cycles > 0.0 ? instr / cycles : 0.0; |
| 52 | + size_t nints = bytes_processed / sizeof(uint32_t); |
| 53 | + double ins_per_int = nints > 0 ? instr / (double)nints : 0.0; |
| 54 | + double cyc_per_int = nints > 0 ? cycles / (double)nints : 0.0; |
| 55 | + printf(" ins=%.0f cyc=%.0f IPC=%.2f ins/int=%.2f cyc/int=%.2f" |
| 56 | + " br=%.0f brmiss=%.0f cmiss=%.0f", |
| 57 | + instr, cycles, ipc, ins_per_int, cyc_per_int, |
| 58 | + branches, branch_misses, cache_misses); |
22 | 59 | } |
23 | | - s[i--] = '\0'; // null terminated |
24 | | - int digits = 0; |
25 | | - do { |
26 | | - s[i--] = n % 10 + '0'; |
27 | | - digits++; |
28 | | - n /= 10; |
29 | | - if (((digits % 3) == 0) && (n != 0)) |
30 | | - s[i--] = ','; |
31 | | - |
32 | | - } while (n); |
33 | | - if (sign) |
34 | | - s[i--] = '-'; |
35 | | - memmove(s, s + i + 1, (size_t)(127 - i)); |
| 60 | + printf("\n"); |
36 | 61 | } |
37 | 62 |
|
38 | | -int main(void) { |
39 | | -#define N 500000U // Avoids VLA |
40 | | - const uint32_t NTrials = 100U; |
41 | | - struct rusage before; |
42 | | - struct rusage after; |
43 | | - double t; |
44 | | - char s[128]; |
45 | | - char s1[128]; |
46 | | - char s2[128]; |
47 | | - |
48 | | - uint32_t datain[N]; |
49 | | - uint8_t compressedbuffer[N * 5]; |
50 | | - uint32_t recovdata[N]; |
51 | | - |
52 | | - for (uint32_t k = 0; k < N; ++k) |
53 | | - datain[k] = (uint32_t)rand() >> ((uint32_t)31 & (uint32_t)rand()); |
54 | | - |
55 | | - size_t compsize = 0; |
56 | | - |
57 | | - getrusage(RUSAGE_SELF, &before); |
58 | | - |
59 | | - for (uint32_t i = 0; i < NTrials; i++) |
60 | | - compsize = streamvbyte_encode(datain, N, compressedbuffer); |
61 | | - |
62 | | - getrusage(RUSAGE_SELF, &after); |
63 | | - |
64 | | - t = (after.ru_utime.tv_usec - before.ru_utime.tv_usec) / 1000000.0; |
65 | | - punt((long long)(round((double)(N * NTrials) / t)), s); |
66 | | - printf("encoding time = %f s, %s uints/sec\n", t, s); |
67 | | - |
68 | | - size_t compsize2; |
69 | | - getrusage(RUSAGE_SELF, &before); |
70 | | - for (uint32_t i = 0; i < NTrials; i++) |
71 | | - compsize2 = streamvbyte_decode(compressedbuffer, recovdata, N); |
72 | | - getrusage(RUSAGE_SELF, &after); |
73 | | - t = (after.ru_utime.tv_usec - before.ru_utime.tv_usec) / 1000000.0; |
74 | | - punt((long long)(round((double)(N * NTrials) / t)), s); |
75 | | - printf("decoding time = %f s, %s uints/sec\n", t, s); |
| 63 | +static void fill_log_uniform(uint32_t *data, size_t n) { |
| 64 | + for (size_t k = 0; k < n; ++k) |
| 65 | + data[k] = (uint32_t)rand() >> ((uint32_t)31 & (uint32_t)rand()); |
| 66 | +} |
| 67 | + |
| 68 | +static void fill_full_range(uint32_t *data, size_t n) { |
| 69 | + for (size_t k = 0; k < n; ++k) { |
| 70 | + /* rand() yields at most 31 bits on most platforms; combine two calls. */ |
| 71 | + uint32_t hi = (uint32_t)rand(); |
| 72 | + uint32_t lo = (uint32_t)rand(); |
| 73 | + data[k] = (hi << 16) ^ lo; |
| 74 | + } |
| 75 | +} |
| 76 | + |
| 77 | +static void fill_small(uint32_t *data, size_t n) { |
| 78 | + for (size_t k = 0; k < n; ++k) |
| 79 | + data[k] = (uint32_t)rand() & 0xFFu; |
| 80 | +} |
| 81 | + |
| 82 | +static void run_benchmark(const char *title, |
| 83 | + void (*fill)(uint32_t *, size_t), |
| 84 | + uint32_t *datain, uint8_t *compressedbuffer, |
| 85 | + uint32_t *recovdata, int have_counters) { |
| 86 | + fill(datain, N); |
| 87 | + |
| 88 | + svb_ctx ctx; |
| 89 | + ctx.datain = datain; |
| 90 | + ctx.compressedbuffer = compressedbuffer; |
| 91 | + ctx.recovdata = recovdata; |
| 92 | + ctx.compsize = 0; |
| 93 | + ctx.n = N; |
| 94 | + |
| 95 | + counters_bench_parameter params = counters_bench_parameter_default(); |
| 96 | + |
| 97 | + printf("== %s ==\n", title); |
| 98 | + |
| 99 | + counters_event_aggregate enc = counters_bench(bench_encode, &ctx, ¶ms); |
| 100 | + print_aggregate("encode", N * sizeof(uint32_t), &enc, have_counters); |
| 101 | + |
| 102 | + size_t compsize = ctx.compsize; |
| 103 | + |
| 104 | + counters_event_aggregate dec = counters_bench(bench_decode, &ctx, ¶ms); |
| 105 | + print_aggregate("decode", N * sizeof(uint32_t), &dec, have_counters); |
| 106 | + |
| 107 | + size_t compsize2 = streamvbyte_decode(compressedbuffer, recovdata, N); |
76 | 108 | if (compsize != compsize2) |
77 | | - printf("compsize=%zu compsize2 = %zu\n", compsize, compsize2); |
| 109 | + printf(" compsize mismatch: %zu vs %zu\n", compsize, compsize2); |
78 | 110 |
|
79 | 111 | uint32_t k; |
80 | 112 | for (k = 0; k < N && datain[k] == recovdata[k]; k++) |
81 | 113 | ; |
82 | | - |
83 | 114 | if (k < N) |
84 | | - printf("mismatch at %d before=%d after=%d\n", k, datain[k], recovdata[k]); |
85 | | - |
| 115 | + printf(" mismatch at %u before=%u after=%u\n", k, datain[k], recovdata[k]); |
86 | 116 | assert(k >= N); |
87 | | - punt(N * sizeof(uint32_t), s1); |
88 | | - punt((long long)compsize, s2); |
89 | | - printf("Compressed %s bytes down to %s bytes.\n", s1, s2); |
90 | | - return 0; |
| 117 | + |
| 118 | + double ratio = (double)compsize / (double)((size_t)N * sizeof(uint32_t)); |
| 119 | + printf(" Compressed %zu bytes down to %zu bytes (ratio = %.3f, %.2f bits/int).\n\n", |
| 120 | + (size_t)N * sizeof(uint32_t), compsize, ratio, |
| 121 | + 8.0 * (double)compsize / (double)N); |
| 122 | +} |
| 123 | + |
| 124 | +int main(void) { |
| 125 | + uint32_t *datain = (uint32_t *)malloc(N * sizeof(uint32_t)); |
| 126 | + uint8_t *compressedbuffer = (uint8_t *)malloc((size_t)N * 5); |
| 127 | + uint32_t *recovdata = (uint32_t *)malloc(N * sizeof(uint32_t)); |
| 128 | + if (!datain || !compressedbuffer || !recovdata) { |
| 129 | + fprintf(stderr, "allocation failure\n"); |
| 130 | + return EXIT_FAILURE; |
| 131 | + } |
| 132 | + |
| 133 | + int have_counters = counters_has_performance_counters() ? 1 : 0; |
| 134 | + if (!have_counters) { |
| 135 | + fprintf(stderr, |
| 136 | + "Warning: hardware performance counters are unavailable.\n" |
| 137 | + "Timing will be reported, but instruction/cycle/branch/cache-miss\n" |
| 138 | + "counts will be omitted. To enable detailed counters:\n" |
| 139 | + " Linux: relax perf_event_paranoid, e.g.\n" |
| 140 | + " sudo sysctl -w kernel.perf_event_paranoid=0\n" |
| 141 | + " (or run this binary with sudo)\n" |
| 142 | + " macOS (Apple Silicon): run this binary with sudo to access kpc.\n\n"); |
| 143 | + } |
| 144 | + |
| 145 | + run_benchmark("log-uniform widths (mixed 1-4 byte)", fill_log_uniform, |
| 146 | + datain, compressedbuffer, recovdata, have_counters); |
| 147 | + run_benchmark("full-range uint32_t (mostly 4-byte)", fill_full_range, |
| 148 | + datain, compressedbuffer, recovdata, have_counters); |
| 149 | + run_benchmark("small values [0,256) (1-byte)", fill_small, |
| 150 | + datain, compressedbuffer, recovdata, have_counters); |
| 151 | + |
| 152 | + free(datain); |
| 153 | + free(compressedbuffer); |
| 154 | + free(recovdata); |
| 155 | + return EXIT_SUCCESS; |
91 | 156 | } |
0 commit comments