1
0
mirror of https://github.com/wg/wrk synced 2025-02-14 07:02:55 +08:00

switch to time as basis of benchmark duration

This commit is contained in:
Will 2013-04-10 20:53:30 +09:00
parent 86a23c6beb
commit 8225945f32
5 changed files with 46 additions and 44 deletions

8
README
View File

@ -6,14 +6,14 @@ wrk - a HTTP benchmarking tool
Basic Usage Basic Usage
wrk -t8 -c400 -r10m http://localhost:8080/index.html wrk -t8 -c400 -d30 http://localhost:8080/index.html
This runs wrk with 8 threads, keeping 400 connections open, and making a This runs a benchmark for 30 seconds, using 8 threads, and keeping
total of 10 million HTTP GET requests to http://localhost:8080/index.html 400 HTTP connections open.
Output: Output:
Making 10000000 requests to http://localhost:8080/index.html Running 30s test @ http://localhost:8080/index.html
8 threads and 400 connections 8 threads and 400 connections
Thread Stats Avg Stdev Max +/- Stdev Thread Stats Avg Stdev Max +/- Stdev
Latency 439.75us 350.49us 7.60ms 92.88% Latency 439.75us 350.49us 7.60ms 92.88%

View File

@ -62,10 +62,10 @@ static int scan_units(char *s, uint64_t *n, units *m) {
if ((c = sscanf(s, "%"SCNu64"%2s", &base, unit)) < 1) return -1; if ((c = sscanf(s, "%"SCNu64"%2s", &base, unit)) < 1) return -1;
if (c == 2) { if (c == 2 && strncasecmp(unit, m->base, 3)) {
for (i = 0; m->units[i] != NULL; i++) { for (i = 0; m->units[i] != NULL; i++) {
scale *= m->scale; scale *= m->scale;
if (!strncasecmp(unit, m->units[i], sizeof(unit))) break; if (!strncasecmp(unit, m->units[i], 3)) break;
} }
if (m->units[i] == NULL) return -1; if (m->units[i] == NULL) return -1;
} }
@ -91,6 +91,14 @@ char *format_time_us(long double n) {
return format_units(n, units, 2); return format_units(n, units, 2);
} }
char *format_time_s(long double n) {
return format_units(n, &time_units_s, 0);
}
int scan_metric(char *s, uint64_t *n) { int scan_metric(char *s, uint64_t *n) {
return scan_units(s, n, &metric_units); return scan_units(s, n, &metric_units);
} }
int scan_time(char *s, uint64_t *n) {
return scan_units(s, n, &time_units_s);
}

View File

@ -4,7 +4,9 @@
char *format_binary(long double); char *format_binary(long double);
char *format_metric(long double); char *format_metric(long double);
char *format_time_us(long double); char *format_time_us(long double);
char *format_time_s(long double);
int scan_metric(char *, uint64_t *); int scan_metric(char *, uint64_t *);
int scan_time(char *, uint64_t *);
#endif /* UNITS_H */ #endif /* UNITS_H */

View File

@ -31,9 +31,8 @@ static struct config {
struct addrinfo addr; struct addrinfo addr;
uint64_t threads; uint64_t threads;
uint64_t connections; uint64_t connections;
uint64_t requests; uint64_t duration;
uint64_t timeout; uint64_t timeout;
uint64_t errors;
} cfg; } cfg;
static struct { static struct {
@ -60,15 +59,15 @@ static void handler(int sig) {
static void usage() { static void usage() {
printf("Usage: wrk <options> <url> \n" printf("Usage: wrk <options> <url> \n"
" Options: \n" " Options: \n"
" -c, --connections <n> Connections to keep open \n" " -c, --connections <N> Connections to keep open \n"
" -r, --requests <n> Total requests to make \n" " -d, --duration <T> Duration of test \n"
" -t, --threads <n> Number of threads to use \n" " -t, --threads <N> Number of threads to use \n"
" \n" " \n"
" -H, --header <h> Add header to request \n" " -H, --header <H> Add header to request \n"
" -v, --version Print version details \n" " -v, --version Print version details \n"
" --errors <n> Abort after N errors \n"
" \n" " \n"
" Numeric arguments may include a SI unit (2k, 2M, 2G)\n"); " Numeric arguments may include a SI unit (1k, 1M, 1G)\n"
" Time arguments may include a time unit (2s, 2m, 2h)\n");
} }
int main(int argc, char **argv) { int main(int argc, char **argv) {
@ -135,12 +134,12 @@ int main(int argc, char **argv) {
thread *threads = zcalloc(cfg.threads * sizeof(thread)); thread *threads = zcalloc(cfg.threads * sizeof(thread));
uint64_t connections = cfg.connections / cfg.threads; uint64_t connections = cfg.connections / cfg.threads;
uint64_t requests = cfg.requests / cfg.threads; uint64_t stop_at = time_us() + (cfg.duration * 1000000);
for (uint64_t i = 0; i < cfg.threads; i++) { for (uint64_t i = 0; i < cfg.threads; i++) {
thread *t = &threads[i]; thread *t = &threads[i];
t->connections = connections; t->connections = connections;
t->requests = requests; t->stop_at = stop_at;
if (pthread_create(&t->thread, NULL, &thread_main, t)) { if (pthread_create(&t->thread, NULL, &thread_main, t)) {
char *msg = strerror(errno); char *msg = strerror(errno);
@ -156,7 +155,8 @@ int main(int argc, char **argv) {
sigfillset(&sa.sa_mask); sigfillset(&sa.sa_mask);
sigaction(SIGINT, &sa, NULL); sigaction(SIGINT, &sa, NULL);
printf("Making %"PRIu64" requests to %s\n", cfg.requests, url); char *time = format_time_s(cfg.duration);
printf("Running %s test @ %s\n", time, url);
printf(" %"PRIu64" threads and %"PRIu64" connections\n", cfg.threads, cfg.connections); printf(" %"PRIu64" threads and %"PRIu64" connections\n", cfg.threads, cfg.connections);
uint64_t start = time_us(); uint64_t start = time_us();
@ -293,17 +293,20 @@ static int sample_rate(aeEventLoop *loop, long long id, void *data) {
static int request_complete(http_parser *parser) { static int request_complete(http_parser *parser) {
connection *c = parser->data; connection *c = parser->data;
thread *thread = c->thread; thread *thread = c->thread;
uint64_t now = time_us();
thread->complete++;
c->latency = now - c->start;
if (parser->status_code > 399) { if (parser->status_code > 399) {
thread->errors.status++; thread->errors.status++;
} }
if (++thread->complete >= thread->requests) { if (now >= thread->stop_at) {
aeStop(thread->loop); aeStop(thread->loop);
goto done; goto done;
} }
c->latency = time_us() - c->start;
if (!http_should_keep_alive(parser)) goto reconnect; if (!http_should_keep_alive(parser)) goto reconnect;
http_parser_init(parser, HTTP_RESPONSE); http_parser_init(parser, HTTP_RESPONSE);
@ -322,8 +325,9 @@ static int request_complete(http_parser *parser) {
static int check_timeouts(aeEventLoop *loop, long long id, void *data) { static int check_timeouts(aeEventLoop *loop, long long id, void *data) {
thread *thread = data; thread *thread = data;
connection *c = thread->cs; connection *c = thread->cs;
uint64_t now = time_us();
uint64_t maxAge = time_us() - (cfg.timeout * 1000); uint64_t maxAge = now - (cfg.timeout * 1000);
for (uint64_t i = 0; i < thread->connections; i++, c++) { for (uint64_t i = 0; i < thread->connections; i++, c++) {
if (maxAge > c->start) { if (maxAge > c->start) {
@ -331,13 +335,7 @@ static int check_timeouts(aeEventLoop *loop, long long id, void *data) {
} }
} }
uint64_t errors = 0; if (stop || now >= thread->stop_at) {
errors += thread->errors.connect;
errors += thread->errors.read;
errors += thread->errors.write;
errors += thread->errors.timeout;
if (stop || errors >= cfg.errors) {
aeStop(loop); aeStop(loop);
} }
@ -426,10 +424,9 @@ static char *format_request(char *host, char *port, char *path, char **headers)
static struct option longopts[] = { static struct option longopts[] = {
{ "connections", required_argument, NULL, 'c' }, { "connections", required_argument, NULL, 'c' },
{ "requests", required_argument, NULL, 'r' }, { "duration", required_argument, NULL, 'd' },
{ "threads", required_argument, NULL, 't' }, { "threads", required_argument, NULL, 't' },
{ "header", required_argument, NULL, 'H' }, { "header", required_argument, NULL, 'H' },
{ "errors", required_argument, NULL, 'E' },
{ "help", no_argument, NULL, 'h' }, { "help", no_argument, NULL, 'h' },
{ "version", no_argument, NULL, 'v' }, { "version", no_argument, NULL, 'v' },
{ NULL, 0, NULL, 0 } { NULL, 0, NULL, 0 }
@ -441,10 +438,10 @@ static int parse_args(struct config *cfg, char **url, char **headers, int argc,
memset(cfg, 0, sizeof(struct config)); memset(cfg, 0, sizeof(struct config));
cfg->threads = 2; cfg->threads = 2;
cfg->connections = 10; cfg->connections = 10;
cfg->requests = 100; cfg->duration = 10;
cfg->timeout = SOCKET_TIMEOUT_MS; cfg->timeout = SOCKET_TIMEOUT_MS;
while ((c = getopt_long(argc, argv, "t:c:r:E:H:v?", longopts, NULL)) != -1) { while ((c = getopt_long(argc, argv, "t:c:d:H:rv?", longopts, NULL)) != -1) {
switch (c) { switch (c) {
case 't': case 't':
if (scan_metric(optarg, &cfg->threads)) return -1; if (scan_metric(optarg, &cfg->threads)) return -1;
@ -452,11 +449,8 @@ static int parse_args(struct config *cfg, char **url, char **headers, int argc,
case 'c': case 'c':
if (scan_metric(optarg, &cfg->connections)) return -1; if (scan_metric(optarg, &cfg->connections)) return -1;
break; break;
case 'r': case 'd':
if (scan_metric(optarg, &cfg->requests)) return -1; if (scan_time(optarg, &cfg->duration)) return -1;
break;
case 'E':
if (scan_metric(optarg, &cfg->errors)) return -1;
break; break;
case 'H': case 'H':
*header++ = optarg; *header++ = optarg;
@ -465,6 +459,8 @@ static int parse_args(struct config *cfg, char **url, char **headers, int argc,
printf("wrk %s [%s] ", VERSION, aeGetApiName()); printf("wrk %s [%s] ", VERSION, aeGetApiName());
printf("Copyright (C) 2012 Will Glozer\n"); printf("Copyright (C) 2012 Will Glozer\n");
break; break;
case 'r':
fprintf(stderr, "wrk 2.0.0+ uses -d instead of -r\n");
case 'h': case 'h':
case '?': case '?':
case ':': case ':':
@ -473,17 +469,13 @@ static int parse_args(struct config *cfg, char **url, char **headers, int argc,
} }
} }
if (optind == argc || !cfg->threads || !cfg->requests) return -1; if (optind == argc || !cfg->threads || !cfg->duration) return -1;
if (!cfg->connections || cfg->connections < cfg->threads) { if (!cfg->connections || cfg->connections < cfg->threads) {
fprintf(stderr, "number of connections must be >= threads\n"); fprintf(stderr, "number of connections must be >= threads\n");
return -1; return -1;
} }
if (cfg->errors == 0) {
cfg->errors = cfg->requests / cfg->threads / 10;
}
*url = argv[optind]; *url = argv[optind];
*header = NULL; *header = NULL;

View File

@ -11,7 +11,7 @@
#include "http_parser.h" #include "http_parser.h"
#include "tinymt64.h" #include "tinymt64.h"
#define VERSION "1.2.0" #define VERSION "2.0.0"
#define RECVBUF 8192 #define RECVBUF 8192
#define SAMPLES 100000 #define SAMPLES 100000
@ -31,7 +31,7 @@ typedef struct {
pthread_t thread; pthread_t thread;
aeEventLoop *loop; aeEventLoop *loop;
uint64_t connections; uint64_t connections;
uint64_t requests; uint64_t stop_at;
uint64_t complete; uint64_t complete;
uint64_t bytes; uint64_t bytes;
uint64_t start; uint64_t start;