void rate_sleep(struct rate *r) { struct timespec now, til; /* what clock to use depends on whether clock_nanosleep() is available */ #if HAVE_CLOCK_NANOSLEEP static const clockid_t rate_clock = CLOCK_MONOTONIC; #else static const clockid_t rate_clock = CLOCK_REALTIME; #endif if (r == NULL) return; /* update the event counter */ r->count += 1; /* fetch the current time */ clock_gettime(rate_clock, &now); /* special case: if this is the first call to rate_sleep(), * calculate when the next tick will be. this is a little bit more * accurate than calculating it in rate_init(). */ if (r->count == 1) { r->start = now; r->next_tick = calc_next_tick(&now, &r->period); } /* adjust the rate and period every 'freq' events. * skip the first window of 'freq' events. * disabled if 'freq' is 0. */ if (r->freq != 0 && (r->count % r->freq) == 0 && r->count > r->freq) adjust_rate(r, &now); /* 'til', amount of time remaining until the next tick */ til = r->next_tick; my_timespec_sub(&now, &til); /* if 'til' is in the past, don't bother sleeping */ if (ts_nanos(&til) > 0) { /* do the sleep */ #if HAVE_CLOCK_NANOSLEEP clock_nanosleep(rate_clock, TIMER_ABSTIME, &r->next_tick, NULL); #else struct timespec rel; rel = r->next_tick; my_timespec_sub(&now, &rel); my_nanosleep(&rel); #endif /* re-fetch the current time */ clock_gettime(rate_clock, &now); } /* calculate the next tick */ r->next_tick = calc_next_tick(&now, &r->period); }
static inline void adjust_rate(struct rate *r, struct timespec *now) { struct timespec elapsed; double ratio; unsigned actual_rate; /* amount of time elapsed since first sleep */ elapsed = *now; my_timespec_sub(&r->start, &elapsed); /* the average event rate that has been maintained over the * lifespan of this rate-limiter. */ actual_rate = r->count / (ts_nanos(&elapsed) / (1000000000 + 0.0)); /* simple ratio of nominal event rate and average event rate */ ratio = r->rate / (actual_rate + 0.0); /* clamp this ratio to a small interval */ if (ratio < 0.99) ratio = 0.99; if (ratio > 1.01) ratio = 1.01; /* calculate a new, adjusted rate based on this ratio */ r->adj_rate *= ratio; /* calculate a new tick period based on the adjusted rate */ const double period = 1.0 / (r->adj_rate + 0.0); my_timespec_from_double(period, &r->period); }
static void print_stats(void) { struct timespec dur; double t_dur; my_timespec_get(&dur); my_timespec_sub(&start_time, &dur); t_dur = my_timespec_to_double(&dur); fprintf(stderr, "%s: wrote %'" PRIu64 " entries (%'" PRIu64 " merged) " "in %'.2f sec, %'d ent/sec, %'d merge/sec\n", program_name, count, count_merged, t_dur, (int) (count / t_dur), (int) (count_merged / t_dur) ); }
int main(int argc, char **argv) { struct timespec ts_a, ts_b; double elapsed; char *file_path; char *queue_model_str; unsigned num_messages; unsigned num_threads; fstrm_iothr_queue_model queue_model; if (argc != 5) { fprintf(stderr, "Usage: %s <FILE> <QUEUE MODEL> <NUM THREADS> <NUM MESSAGES>\n", argv[0]); fprintf(stderr, "\n"); fprintf(stderr, "FILE is a filesystem path.\n"); fprintf(stderr, "QUEUE MODEL is the string 'SPSC' or 'MPSC'.\n"); fprintf(stderr, "NUM THREADS is an integer.\n"); fprintf(stderr, "NUM MESSAGES is an integer.\n"); return EXIT_FAILURE; } file_path = argv[1]; queue_model_str = argv[2]; num_threads = atoi(argv[3]); num_messages = atoi(argv[4]); if (num_threads < 1) { fprintf(stderr, "%s: Error: invalid number of threads\n", argv[0]); return EXIT_FAILURE; } if (num_messages < 1) { fprintf(stderr, "%s: Error: invalid number of messages\n", argv[0]); return EXIT_FAILURE; } if (strcasecmp(queue_model_str, "SPSC") == 0) { queue_model = FSTRM_IOTHR_QUEUE_MODEL_SPSC; } else if (strcasecmp(queue_model_str, "MPSC") == 0) { queue_model = FSTRM_IOTHR_QUEUE_MODEL_MPSC; } else { fprintf(stderr, "%s: Error: invalid queue model\n", argv[0]); return EXIT_FAILURE; } printf("testing fstrm_iothr with file= %s " "queue_model= %s " "num_threads= %u " "num_messages= %u\n", file_path, queue_model_str, num_threads, num_messages); struct fstrm_file_options *fopt; fopt = fstrm_file_options_init(); fstrm_file_options_set_file_path(fopt, file_path); struct fstrm_writer *w = fstrm_file_writer_init(fopt, NULL); assert(w != NULL); fstrm_file_options_destroy(&fopt); struct fstrm_iothr_options *iothr_opt; iothr_opt = fstrm_iothr_options_init(); if (queue_model == FSTRM_IOTHR_QUEUE_MODEL_SPSC) { fstrm_iothr_options_set_num_input_queues(iothr_opt, num_threads); } else if (queue_model == FSTRM_IOTHR_QUEUE_MODEL_MPSC) { fstrm_iothr_options_set_num_input_queues(iothr_opt, 1); } else { assert(0); /* not reached */ } fstrm_iothr_options_set_queue_model(iothr_opt, queue_model); struct fstrm_iothr *iothr = fstrm_iothr_init(iothr_opt, &w); assert(iothr != NULL); fstrm_iothr_options_destroy(&iothr_opt); struct consumer test_consumer; struct producer test_producers[num_threads]; for (unsigned i = 0; i < num_threads; i++) { test_producers[i].iothr = iothr; test_producers[i].num_messages = num_messages; } if (queue_model == FSTRM_IOTHR_QUEUE_MODEL_SPSC) { for (unsigned i = 0; i < num_threads; i++) { test_producers[i].ioq = fstrm_iothr_get_input_queue(iothr); assert(test_producers[i].ioq != NULL); } } else if (queue_model == FSTRM_IOTHR_QUEUE_MODEL_MPSC) { struct fstrm_iothr_queue *ioq = fstrm_iothr_get_input_queue(iothr); assert(ioq != NULL); for (unsigned i = 0; i < num_threads; i++) test_producers[i].ioq = ioq; } else { assert(0); /* not reached */ } my_gettime(CLOCK_MONOTONIC, &ts_a); printf("creating %u producer threads\n", num_threads); for (unsigned i = 0; i < num_threads; i++) pthread_create(&test_producers[i].thr, NULL, thr_producer, &test_producers[i]); printf("joining %u producer threads\n", num_threads); for (unsigned i = 0; i < num_threads; i++) pthread_join(test_producers[i].thr, (void **) NULL); printf("destroying fstrm_iothr object\n"); fstrm_iothr_destroy(&iothr); my_gettime(CLOCK_MONOTONIC, &ts_b); my_timespec_sub(&ts_a, &ts_b); elapsed = my_timespec_to_double(&ts_b); printf("completed in %.2f seconds\n", elapsed); int res = consume_input(&test_consumer, file_path); if (res != EXIT_SUCCESS) return res; struct producer_stats pstat_sum; memset(&pstat_sum, 0, sizeof(pstat_sum)); for (unsigned i = 0; i < num_threads; i++) { pstat_sum.count_generated += test_producers[i].pstat.count_generated; pstat_sum.count_submitted += test_producers[i].pstat.count_submitted; pstat_sum.bytes_generated += test_producers[i].pstat.bytes_generated; pstat_sum.bytes_submitted += test_producers[i].pstat.bytes_submitted; } printf("count_generated= %" PRIu64 "\n", pstat_sum.count_generated); printf("bytes_generated= %" PRIu64 "\n", pstat_sum.bytes_generated); printf("count_submitted= %" PRIu64 "\n", pstat_sum.count_submitted); printf("bytes_submitted= %" PRIu64 "\n", pstat_sum.bytes_submitted); printf("count_received= %" PRIu64 " (%.3f)\n", test_consumer.cstat.count_received, (test_consumer.cstat.count_received + 0.0) / (pstat_sum.count_generated + 0.0) ); printf("bytes_received= %" PRIu64 " (%.3f)\n", test_consumer.cstat.bytes_received, (test_consumer.cstat.bytes_received + 0.0) / (pstat_sum.bytes_generated + 0.0) ); assert(pstat_sum.count_submitted == test_consumer.cstat.count_received); assert(pstat_sum.bytes_submitted == test_consumer.cstat.bytes_received); putchar('\n'); return EXIT_SUCCESS; }