diff --git a/src/h2load.cc b/src/h2load.cc index 9f265eba..1afadf3d 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -85,7 +85,7 @@ Config::~Config() { } } -bool Config::is_rate_mode() { return (this->rate != 0); } +bool Config::is_rate_mode() const { return (this->rate != 0); } Config config; @@ -156,13 +156,13 @@ void readcb(struct ev_loop *loop, ev_io *w, int revents) { namespace { // Called every second when rate mode is being used -void second_timeout_w_cb(EV_P_ ev_timer *w, int revents) { +void second_timeout_w_cb(struct ev_loop *loop, ev_timer *w, int revents) { auto worker = static_cast(w->data); auto nclients_per_second = worker->rate; auto conns_remaining = worker->nclients - worker->nconns_made; auto nclients = std::min(nclients_per_second, conns_remaining); - for (ssize_t i = 0; i < nclients; ++i) { + for (size_t i = 0; i < nclients; ++i) { auto req_todo = worker->config->max_concurrent_streams; worker->clients.push_back(make_unique(worker, req_todo)); auto &client = worker->clients.back(); @@ -175,7 +175,6 @@ void second_timeout_w_cb(EV_P_ ev_timer *w, int revents) { if (worker->nconns_made >= worker->nclients) { ev_timer_stop(worker->loop, w); } - ++worker->current_second; } } // namespace @@ -729,20 +728,19 @@ void Client::record_ttfb(Stats *stat) { void Client::signal_write() { ev_io_start(worker->loop, &wev); } Worker::Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t req_todo, size_t nclients, - ssize_t rate, Config *config) + size_t rate, Config *config) : stats(req_todo), loop(ev_loop_new(0)), ssl_ctx(ssl_ctx), config(config), - id(id), tls_info_report_done(false), current_second(0), nconns_made(0), - nclients(nclients), rate(rate) { + id(id), tls_info_report_done(false), nconns_made(0), nclients(nclients), + rate(rate) { stats.req_todo = req_todo; - progress_interval = std::max((size_t)1, req_todo / 10); + progress_interval = std::max(static_cast(1), req_todo / 10); auto nreqs_per_client = req_todo / nclients; auto nreqs_rem = req_todo % nclients; if (config->is_rate_mode()) { // create timer that will go off every second + ev_timer_init(&timeout_watcher, second_timeout_w_cb, 0., 1.); timeout_watcher.data = this; - ev_init(&timeout_watcher, second_timeout_w_cb); - timeout_watcher.repeat = 1.; } else { for (size_t i = 0; i < nclients; ++i) { auto req_todo = nreqs_per_client; @@ -1233,7 +1231,7 @@ int main(int argc, char **argv) { break; case 'r': config.rate = strtoul(optarg, nullptr, 10); - if (config.rate <= 0) { + if (config.rate == 0) { std::cerr << "-r: the rate at which connections are made " << "must be positive." << std::endl; exit(EXIT_FAILURE); @@ -1241,7 +1239,7 @@ int main(int argc, char **argv) { break; case 'C': config.nconns = strtoul(optarg, nullptr, 10); - if (config.nconns <= 0) { + if (config.nconns == 0) { std::cerr << "-C: the total number of connections made " << "must be positive." << std::endl; exit(EXIT_FAILURE); @@ -1298,47 +1296,37 @@ int main(int argc, char **argv) { exit(EXIT_FAILURE); } - if (!config.is_rate_mode() && config.nreqs < config.nclients) { - std::cerr << "-n, -c: the number of requests must be greater than or " - << "equal to the concurrent clients." << std::endl; - exit(EXIT_FAILURE); - } - - if (!config.is_rate_mode() && config.nclients < config.nthreads) { - std::cerr << "-c, -t: the number of client must be greater than or equal " - "to the number of threads." << std::endl; - exit(EXIT_FAILURE); - } - if (config.nthreads > std::thread::hardware_concurrency()) { std::cerr << "-t: warning: the number of threads is greater than hardware " << "cores." << std::endl; } - if (config.nconns < 0) { - std::cerr << "-C: the total number of connections made " - << "cannot be negative." << std::endl; - exit(EXIT_FAILURE); - } + if (!config.is_rate_mode()) { + if (config.nreqs < config.nclients) { + std::cerr << "-n, -c: the number of requests must be greater than or " + << "equal to the concurrent clients." << std::endl; + exit(EXIT_FAILURE); + } - if (config.rate < 0) { - std::cerr << "-r: the rate at which connections are made " - << "cannot be negative." << std::endl; - exit(EXIT_FAILURE); - } + if (config.nclients < config.nthreads) { + std::cerr << "-c, -t: the number of client must be greater than or equal " + "to the number of threads." << std::endl; + exit(EXIT_FAILURE); + } + } else { + if (config.rate < config.nthreads) { + std::cerr << "-r, -t: the connection rate must be greater than or equal " + << "to the number of threads." << std::endl; + exit(EXIT_FAILURE); + } - if (config.is_rate_mode() && config.rate < (ssize_t)config.nthreads) { - std::cerr << "-r, -t: the connection rate must be greater than or equal " - << "to the number of threads." << std::endl; - exit(EXIT_FAILURE); - } - - if (config.is_rate_mode() && config.nconns != 0 && - config.nconns < (ssize_t)config.nthreads) { - std::cerr << "-C, -t: the total number of connections must be greater than " - "or equal " - << "to the number of threads." << std::endl; - exit(EXIT_FAILURE); + if (config.nconns != 0 && config.nconns < config.nthreads) { + std::cerr + << "-C, -t: the total number of connections must be greater than " + "or equal " + << "to the number of threads." << std::endl; + exit(EXIT_FAILURE); + } } if (!datafile.empty()) { @@ -1424,51 +1412,58 @@ int main(int argc, char **argv) { reqlines = parse_uris(std::begin(uris), std::end(uris)); } + if (reqlines.empty()) { + std::cerr << "No URI given" << std::endl; + exit(EXIT_FAILURE); + } + if (config.max_concurrent_streams == -1) { config.max_concurrent_streams = reqlines.size(); } + assert(config.max_concurrent_streams > 0); + // if not in rate mode and -C is set, warn that we are ignoring it if (!config.is_rate_mode() && config.nconns != 0) { std::cerr << "-C: warning: This option can only be used with -r, and" << " will be ignored otherwise." << std::endl; } - ssize_t n_time = 0; - ssize_t c_time = 0; + size_t n_time = 0; + size_t c_time = 0; size_t actual_nreqs = config.nreqs; // only care about n_time and c_time in rate mode - if (config.is_rate_mode() && config.max_concurrent_streams != 0) { - n_time = (int)config.nreqs / - ((int)config.rate * (int)config.max_concurrent_streams); - c_time = (int)config.nconns / (int)config.rate; - } - // check to see if the two ways of determining test time conflict - if (config.is_rate_mode() && (int)config.max_concurrent_streams != 0 && - (n_time != c_time) && config.nreqs != 1 && config.nconns != 0) { - if ((int)config.nreqs < config.nconns) { - std::cerr << "-C, -n: warning: number of requests conflict. " - << std::endl; - std::cerr << "The test will create " - << (config.max_concurrent_streams * config.nconns) - << " total requests." << std::endl; - actual_nreqs = config.max_concurrent_streams * config.nconns; - } else { - std::cout << "-C, -n: warning: number of requests conflict. " - << std::endl; - std::cout << "The smaller of the two will be chosen and the test will " - << "create " - << std::min( - config.nreqs, - (size_t)(config.max_concurrent_streams * config.nconns)) - << " total requests." << std::endl; - actual_nreqs = std::min( - config.nreqs, (size_t)(config.max_concurrent_streams * config.nreqs)); - } - } else { - if (config.is_rate_mode() && config.max_concurrent_streams != 0 && - (n_time != c_time) && config.nreqs == 1 && config.nconns != 0) { - actual_nreqs = config.max_concurrent_streams * config.nconns; + if (config.is_rate_mode()) { + n_time = config.nreqs / (config.rate * config.max_concurrent_streams); + c_time = config.nconns / config.rate; + + // check to see if the two ways of determining test time conflict + if (n_time != c_time && config.nconns != 0) { + if (config.nreqs != 1) { + if (config.nreqs < config.nconns) { + std::cerr << "-C, -n: warning: number of requests conflict. " + << std::endl; + std::cerr << "The test will create " + << (config.max_concurrent_streams * config.nconns) + << " total requests." << std::endl; + actual_nreqs = config.max_concurrent_streams * config.nconns; + } else { + std::cout << "-C, -n: warning: number of requests conflict. " + << std::endl; + std::cout + << "The smaller of the two will be chosen and the test will " + << "create " + << std::min(config.nreqs, + static_cast(config.max_concurrent_streams * + config.nconns)) + << " total requests." << std::endl; + actual_nreqs = std::min( + config.nreqs, static_cast(config.max_concurrent_streams * + config.nreqs)); + } + } else { + actual_nreqs = config.max_concurrent_streams * config.nconns; + } } } @@ -1545,7 +1540,7 @@ int main(int argc, char **argv) { if (config.is_rate_mode()) { // set various config values - if ((int)config.nreqs < config.nconns) { + if (config.nreqs < config.nconns) { seconds = c_time; } else if (config.nconns == 0) { seconds = n_time; @@ -1561,19 +1556,17 @@ int main(int argc, char **argv) { size_t nclients_per_thread = config.nclients / config.nthreads; ssize_t nclients_rem = config.nclients % config.nthreads; - size_t rate_per_thread = config.rate / (ssize_t)config.nthreads; - ssize_t rate_per_thread_rem = config.rate % (ssize_t)config.nthreads; + size_t rate_per_thread = config.rate / config.nthreads; + ssize_t rate_per_thread_rem = config.rate % config.nthreads; - auto nclients_extra = 0; - auto nclients_extra_per_thread = 0; - auto nclients_extra_rem_per_thread = 0; + size_t nclients_extra_per_thread = 0; + ssize_t nclients_extra_per_thread_rem = 0; // In rate mode, we want each Worker to create a total of // C/t connections. if (config.is_rate_mode() && config.nconns > seconds * config.rate) { - nclients_extra = config.nconns - (seconds * config.rate); - nclients_extra_per_thread = nclients_extra / (ssize_t)config.nthreads; - nclients_extra_rem_per_thread = - (ssize_t)nclients_extra % (ssize_t)config.nthreads; + auto nclients_extra = config.nconns - (seconds * config.rate); + nclients_extra_per_thread = nclients_extra / config.nthreads; + nclients_extra_per_thread_rem = nclients_extra % config.nthreads; } std::cout << "starting benchmark..." << std::endl; @@ -1586,12 +1579,15 @@ int main(int argc, char **argv) { #ifndef NOTHREADS std::vector> futures; for (size_t i = 0; i < config.nthreads - 1; ++i) { - auto nreqs = nreqs_per_thread + (nreqs_rem-- > 0); auto rate = rate_per_thread + (rate_per_thread_rem-- > 0); - auto nclients = nclients_per_thread + (nclients_rem-- > 0); - if (config.is_rate_mode()) { + size_t nreqs; + size_t nclients; + if (!config.is_rate_mode()) { + nclients = nclients_per_thread + (nclients_rem-- > 0); + nreqs = nreqs_per_thread + (nreqs_rem-- > 0); + } else { nclients = rate * seconds + nclients_extra_per_thread + - (nclients_extra_rem_per_thread-- > 0); + (nclients_extra_per_thread_rem-- > 0); nreqs = nclients * config.max_concurrent_streams; } std::cout << "spawning thread #" << i << ": " << nclients @@ -1605,12 +1601,15 @@ int main(int argc, char **argv) { } #endif // NOTHREADS - auto nreqs_last = nreqs_per_thread + (nreqs_rem-- > 0); - auto nclients_last = nclients_per_thread + (nclients_rem-- > 0); auto rate_last = rate_per_thread + (rate_per_thread_rem-- > 0); - if (config.is_rate_mode()) { + size_t nclients_last; + size_t nreqs_last; + if (!config.is_rate_mode()) { + nclients_last = nclients_per_thread + (nclients_rem-- > 0); + nreqs_last = nreqs_per_thread + (nreqs_rem-- > 0); + } else { nclients_last = rate_last * seconds + nclients_extra_per_thread + - (nclients_extra_rem_per_thread-- > 0); + (nclients_extra_per_thread_rem-- > 0); nreqs_last = nclients_last * config.max_concurrent_streams; } std::cout << "spawning thread #" << (config.nthreads - 1) << ": " diff --git a/src/h2load.h b/src/h2load.h index 20d323fe..28aec5d4 100644 --- a/src/h2load.h +++ b/src/h2load.h @@ -78,9 +78,9 @@ struct Config { size_t window_bits; size_t connection_window_bits; // rate at which connections should be made - ssize_t rate; + size_t rate; // number of connections made - ssize_t nconns; + size_t nconns; enum { PROTO_HTTP2, PROTO_SPDY2, PROTO_SPDY3, PROTO_SPDY3_1 } no_tls_proto; // file descriptor for upload data int data_fd; @@ -91,7 +91,7 @@ struct Config { Config(); ~Config(); - bool is_rate_mode(); + bool is_rate_mode() const; }; struct RequestStat { @@ -177,14 +177,13 @@ struct Worker { size_t progress_interval; uint32_t id; bool tls_info_report_done; - ssize_t current_second; - ssize_t nconns_made; - ssize_t nclients; + size_t nconns_made; + size_t nclients; + size_t rate; ev_timer timeout_watcher; - ssize_t rate; Worker(uint32_t id, SSL_CTX *ssl_ctx, size_t nreq_todo, size_t nclients, - ssize_t rate, Config *config); + size_t rate, Config *config); ~Worker(); Worker(Worker &&o) = default; void run();