This commit is contained in:
Gregory Burd 2024-05-20 16:31:26 -04:00
parent e0c79c9348
commit 5afb6c6f0d

View file

@ -17,8 +17,6 @@
#define INITIAL_AMOUNT 1024 * 2
bool recording = true;
typedef size_t pgno_t;
typedef enum { SM, ML, RB } container_impl_t;
@ -133,6 +131,8 @@ typedef struct container {
} container_t;
#define digest(name) containers[type].name##_stats.td
char *
bytes_as(double bytes, char *s, size_t size)
{
@ -316,6 +316,8 @@ b64_decode(const char *in, unsigned char *out, size_t outlen)
/* recording ------------------------------------------------------------- */
bool recording = false;
static void
record_set_mutation(FILE *out, pgno_t pg)
{
@ -871,7 +873,9 @@ __roar_validate(void *handle)
return true;
}
/* histogram ------------------------------------------------------------- */
/* statistics ------------------------------------------------------------ */
bool statistics = false;
typedef struct sw {
struct timespec t1; /* start time */
@ -892,7 +896,7 @@ elapsed(struct timespec *s, struct timespec *e)
long sec, nanos;
sec = e->tv_sec - s->tv_sec;
nanos = e->tv_nsec - e->tv_nsec;
nanos = e->tv_nsec - s->tv_nsec;
if (nanos < 0) {
nanos += 1e9;
sec--;
@ -1102,7 +1106,8 @@ container_t containers[] = {
void *handles[(sizeof((containers)) / sizeof((containers)[0]))];
void *new_handles[(sizeof((containers)) / sizeof((containers)[0]))];
FILE *fp;
FILE *record_fp;
FILE *stats_fp;
#define alloc(type, size) containers[type].alloc(size);
#define cast(type, fn, ...) \
@ -1110,15 +1115,15 @@ FILE *fp;
containers[type].fn(handles[type], ##__VA_ARGS__)
#define invoke(type, fn, ...) __stats_##fn(containers[type].fn##_stats.td, containers[type].fn##_stats.fn, handles[type], __VA_ARGS__)
#define mutate(type, fn, ...) \
(type == 0) ? record_##fn##_mutation(fp, __VA_ARGS__) : (void)0, \
#define mutate(type, fn, ...) \
(type == 0) ? record_##fn##_mutation(record_fp, __VA_ARGS__) : (void)0, \
__stats_##fn(containers[type].fn##_stats.td, containers[type].fn##_stats.fn, &handles[type], __VA_ARGS__)
#define foreach(set) for (unsigned type = 0; type < (sizeof((set)) / sizeof((set)[0])); type++)
#define checkpoint(set) \
for (unsigned type = 1; type < (sizeof((set)) / sizeof((set)[0])); type++) { \
verify_eq(0, handles[0], type, handles[type]); \
} \
record_checkpoint(fp, handles[0])
record_checkpoint(record_fp, handles[0])
bool
verify_sm_eq_rb(sparsemap_t *map, roaring_bitmap_t *rbm)
@ -1186,36 +1191,39 @@ verify_eq(unsigned a, void *ad, unsigned b, void *bd)
return ret;
}
#define SHORT_OPT "r:fa:bh"
#define LONG_OPT "record:,force,amount:,buffer,help"
void
print_usage(const char *program_name)
{
printf("Usage: %s [OPTIONS]\n", program_name);
printf(" -r, --record <file> Path to the file for recording (optional)\n");
printf(" -f, --force Force overwrite of existing file (optional)\n");
printf(" -b, --buffer Disable buffering writes to stdout/err (optional)\n");
printf(" -a, --amount <number> Specify the number of entries to record (must be positive, optional)\n");
printf(" -h, --help Print this help message\n");
printf(" -r <file> Path to the file for recording (optional)\n");
printf(" -s <file> Path to the file for statistics (optional)\n");
printf(" -f Force overwrite of existing file (optional)\n");
printf(" -b Disable buffering writes to stdout/err (optional)\n");
printf(" -a <number> Specify the number of entries to record (must be positive, optional)\n");
printf(" -h Print this help message\n");
}
#define SHORT_OPT "r:s:fa:bh"
int
main(int argc, char *argv[])
{
int opt;
const char *record_file = NULL;
const char *stats_file = NULL;
int force_flag = 0;
size_t left, iteration = 0, amt = INITIAL_AMOUNT;
bool buffer = true;
fp = stdout;
while ((opt = getopt(argc, argv, SHORT_OPT LONG_OPT)) != -1) {
while ((opt = getopt(argc, argv, SHORT_OPT)) != -1) {
switch (opt) {
case 'r':
recording = true;
record_file = optarg;
break;
case 's':
statistics = true;
stats_file = optarg;
break;
case 'f':
force_flag = 1;
break;
@ -1240,10 +1248,9 @@ main(int argc, char *argv[])
}
}
// Check if record file is specified
if (record_file == NULL) {
recording = false;
} else {
if (recording) {
record_fp = stdout;
// Check for existing file without force flag
if (access(record_file, F_OK) == 0 && !force_flag) {
fprintf(stderr, "Warning: File '%s' already exists. Use -f or --force to overwrite.\n", record_file);
@ -1251,17 +1258,38 @@ main(int argc, char *argv[])
}
// Open the file for writing (truncate if force flag is set)
fp = fopen(record_file, force_flag ? "w" : "a");
if (fp == NULL) {
record_fp = fopen(record_file, force_flag ? "w" : "a");
if (record_fp == NULL) {
perror("Error opening file");
return 1;
}
}
// Check if statistics file is specified
if (statistics) {
if (stats_file[0] == '-') {
stats_fp = stdout;
setvbuf(stdout, NULL, _IONBF, 0);
} else {
// Check for existing file without force flag
if (access(stats_file, F_OK) == 0 && !force_flag) {
fprintf(stderr, "Warning: File '%s' already exists. Use -f or --force to overwrite.\n", stats_file);
return 1;
}
// Open the file for writing (truncate if force flag is set)
stats_fp = fopen(stats_file, force_flag ? "w" : "a");
if (stats_fp == NULL) {
perror("Error opening file");
return 1;
}
}
}
// disable buffering
if (!buffer) {
setvbuf(stdout, NULL, _IONBF, 0);
setvbuf(fp, NULL, _IONBF, 0);
setvbuf(record_fp, NULL, _IONBF, 0);
setvbuf(stats_fp, NULL, _IONBF, 0);
}
unsigned types[] = { SM, ML, RB };
unsigned num_types = (sizeof((types)) / sizeof((types)[0]));
@ -1280,9 +1308,6 @@ main(int argc, char *argv[])
containers[type].is_empty_stats.td = NULL;
containers[type].is_first_stats.td = NULL;
containers[type].merge_stats.td = td_new(100);
containers[type].size = NULL;
containers[type].count = NULL;
containers[type].validate = NULL;
}
/* Setup: add an amt of bits to each container. */
@ -1299,6 +1324,77 @@ main(int argc, char *argv[])
checkpoint(types);
left = amt;
if (statistics) {
const char *names[] = { "sm", "ml", "rb" };
const char *dists[] = { "p50", "p75", "p90", "p99", "p999" };
fprintf(stats_fp, "timestamp,iterations,");
foreach(types)
{
fprintf(stats_fp, "%s_size,%s_bytes,", names[type], names[type]);
if (digest(alloc) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_alloc_%s,", names[type], dists[i]);
}
}
if (digest(free) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_free_%s,", names[type], dists[i]);
}
}
if (digest(set) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_set_%s,", names[type], dists[i]);
}
}
if (digest(is_set) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_is_set_%s,", names[type], dists[i]);
}
}
if (digest(clear) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_clear_%s,", names[type], dists[i]);
}
}
if (digest(find_span) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_find_span_%s,", names[type], dists[i]);
}
}
if (digest(take_span) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_take_span_%s,", names[type], dists[i]);
}
}
if (digest(release_span) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_release_span_%s,", names[type], dists[i]);
}
}
if (digest(is_span) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_is_span_%s,", names[type], dists[i]);
}
}
if (digest(is_empty) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_is_empty_%s,", names[type], dists[i]);
}
}
if (digest(is_first) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_is_first_%s,", names[type], dists[i]);
}
}
if (digest(merge) != NULL) {
for (int i = 0; i < 5; i++) {
fprintf(stats_fp, "%s_merge_%s,", names[type], dists[i]);
}
}
}
fprintf(stats_fp, "\n");
}
while (true) {
iteration++;
// the an amount [1, 16] of pages to find preferring smaller sizes
@ -1395,7 +1491,39 @@ main(int argc, char *argv[])
containers[type].free(new_handles[type]);
}
}
if (statistics) {
const float dists[] = { 0.5, 0.75, 0.90, 0.99, 0.999 };
fprintf(stats_fp, "%f,%zu,", nsts(), iteration);
foreach(types)
{
fprintf(stats_fp, "%zu,%zu,", containers[type].count(handles[type]), containers[type].size(handles[type]));
// clang-format off
td_histogram_t *td[] = {
digest(alloc),
digest(free),
digest(set),
digest(is_set),
digest(clear),
digest(find_span),
digest(take_span),
digest(release_span),
digest(is_span),
digest(is_empty),
digest(is_first),
digest(merge)
};
// clang-format on
for (int i = 0; i < 12; i++) {
if (td[i] != NULL) {
td_compress(td[i]);
for (int j = 0; j < 5; j++) {
fprintf(stats_fp, "%.10f,", td_quantile(td[i], dists[j]));
}
}
}
}
fprintf(stats_fp, "\n");
}
}
return 0;
}