|
|
@@ -6,6 +6,9 @@
|
|
|
*
|
|
|
*/
|
|
|
|
|
|
+#include <vector>
|
|
|
+#include <algorithm>
|
|
|
+
|
|
|
#include "hv.h"
|
|
|
#include "hmain.h" // import parse_opt
|
|
|
#include "hloop.h"
|
|
|
@@ -15,7 +18,7 @@
|
|
|
#include "HttpParser.h"
|
|
|
using namespace hv;
|
|
|
|
|
|
-static const char options[] = "hvc:d:t:";
|
|
|
+static const char options[] = "hvc:d:t:b:i:";
|
|
|
|
|
|
static const char detail_options[] = R"(
|
|
|
-h Print help infomation
|
|
|
@@ -23,11 +26,15 @@ static const char detail_options[] = R"(
|
|
|
-c <connections> Number of connections, default: 1000
|
|
|
-d <duration> Duration of test, default: 10s
|
|
|
-t <threads> Number of threads, default: 4
|
|
|
+ -b <bytes> Content-Length, default: 0
|
|
|
+ -i <interval> Interval of timer, default: 0ms
|
|
|
)";
|
|
|
|
|
|
static int connections = 1000;
|
|
|
-static int duration = 10;
|
|
|
+static int duration = 10; // s
|
|
|
static int threads = 4;
|
|
|
+static int bytes = 0; // byte
|
|
|
+static int interval = 0; // ms
|
|
|
|
|
|
static bool verbose = false;
|
|
|
static const char* url = NULL;
|
|
|
@@ -48,6 +55,8 @@ typedef struct connection_s {
|
|
|
uint64_t response_cnt;
|
|
|
uint64_t ok_cnt;
|
|
|
uint64_t readbytes;
|
|
|
+ uint64_t start_time;
|
|
|
+ std::vector<int> delays;
|
|
|
|
|
|
connection_s()
|
|
|
: parser(HttpParser::New(HTTP_CLIENT, HTTP_V1))
|
|
|
@@ -63,6 +72,7 @@ typedef struct connection_s {
|
|
|
}
|
|
|
|
|
|
void SendRequest() {
|
|
|
+ start_time = hloop_now_ms(hevent_loop(io));
|
|
|
hio_write(io, request_msg.data(), request_msg.size());
|
|
|
++request_cnt;
|
|
|
parser->InitResponse(response.get());
|
|
|
@@ -77,6 +87,8 @@ typedef struct connection_s {
|
|
|
return false;
|
|
|
}
|
|
|
if (parser->IsComplete()) {
|
|
|
+ int delay = hloop_now_ms(hevent_loop(io)) - start_time;
|
|
|
+ delays.push_back(delay);
|
|
|
++response_cnt;
|
|
|
if (response->status_code == HTTP_STATUS_OK) {
|
|
|
++ok_cnt;
|
|
|
@@ -90,6 +102,7 @@ static connection_t** conns = NULL;
|
|
|
|
|
|
static std::atomic<int> connected_num(0);
|
|
|
static std::atomic<int> disconnected_num(0);
|
|
|
+static unsigned int connect_start_time = 0;
|
|
|
|
|
|
static void print_help() {
|
|
|
printf("Usage: wrk [%s] <url>\n", options);
|
|
|
@@ -106,6 +119,8 @@ static void print_result() {
|
|
|
uint64_t total_response_cnt = 0;
|
|
|
uint64_t total_ok_cnt = 0;
|
|
|
uint64_t total_readbytes = 0;
|
|
|
+ uint64_t total_delay = 0;
|
|
|
+ std::vector<int> delays;
|
|
|
connection_t* conn = NULL;
|
|
|
for (int i = 0; i < connections; ++i) {
|
|
|
conn = conns[i];
|
|
|
@@ -113,6 +128,11 @@ static void print_result() {
|
|
|
total_response_cnt += conn->response_cnt;
|
|
|
total_ok_cnt += conn->ok_cnt;
|
|
|
total_readbytes += conn->readbytes;
|
|
|
+ delays.insert(delays.end(), conn->delays.begin(), conn->delays.end());
|
|
|
+ }
|
|
|
+ std::sort(delays.begin(), delays.end());
|
|
|
+ for (int i = 0; i < delays.size(); ++i) {
|
|
|
+ total_delay += delays[i];
|
|
|
}
|
|
|
printf("%llu requests, %llu OK, %lluMB read in %ds\n",
|
|
|
LLU(total_request_cnt),
|
|
|
@@ -121,6 +141,19 @@ static void print_result() {
|
|
|
duration);
|
|
|
printf("Requests/sec: %8llu\n", LLU(total_response_cnt / duration));
|
|
|
printf("Transfer/sec: %8lluMB\n", LLU((total_readbytes / duration) >> 20));
|
|
|
+ printf("Latency avg: %8llums\n", LLU(total_delay / delays.size()));
|
|
|
+ printf("Percentage of the requests served within a certain time (ms)\n");
|
|
|
+ printf(" 50%% %d\n", delays[delays.size() * 0.5]);
|
|
|
+ printf(" 60%% %d\n", delays[delays.size() * 0.6]);
|
|
|
+ printf(" 70%% %d\n", delays[delays.size() * 0.7]);
|
|
|
+ printf(" 80%% %d\n", delays[delays.size() * 0.8]);
|
|
|
+ printf(" 90%% %d\n", delays[delays.size() * 0.9]);
|
|
|
+ printf(" 95%% %d\n", delays[delays.size() * 0.95]);
|
|
|
+ printf(" 96%% %d\n", delays[delays.size() * 0.96]);
|
|
|
+ printf(" 97%% %d\n", delays[delays.size() * 0.97]);
|
|
|
+ printf(" 98%% %d\n", delays[delays.size() * 0.98]);
|
|
|
+ printf(" 99%% %d\n", delays[delays.size() * 0.99]);
|
|
|
+ printf(" 100%% %d (longest delay)\n", delays.back());
|
|
|
}
|
|
|
|
|
|
static void start_reconnect(hio_t* io);
|
|
|
@@ -140,19 +173,30 @@ static void on_close(hio_t* io) {
|
|
|
static void on_recv(hio_t* io, void* buf, int readbytes) {
|
|
|
connection_t* conn = (connection_t*)hevent_userdata(io);
|
|
|
if (conn->RecvResponse((const char*)buf, readbytes)) {
|
|
|
- conn->SendRequest();
|
|
|
+ if (interval == 0) {
|
|
|
+ conn->SendRequest();
|
|
|
+ }
|
|
|
}
|
|
|
}
|
|
|
|
|
|
+static void send_heartbeat(hio_t* io) {
|
|
|
+ connection_t* conn = (connection_t*)hevent_userdata(io);
|
|
|
+ conn->SendRequest();
|
|
|
+}
|
|
|
+
|
|
|
static void on_connect(hio_t* io) {
|
|
|
if (++connected_num == connections) {
|
|
|
if (verbose) {
|
|
|
- printf("all connected\n");
|
|
|
+ printf("all connected in %ums\n", gettick_ms() - connect_start_time);
|
|
|
}
|
|
|
}
|
|
|
|
|
|
connection_t* conn = (connection_t*)hevent_userdata(io);
|
|
|
- conn->SendRequest();
|
|
|
+ if (interval == 0) {
|
|
|
+ conn->SendRequest();
|
|
|
+ } else {
|
|
|
+ hio_set_heartbeat(io, interval, send_heartbeat);
|
|
|
+ }
|
|
|
|
|
|
hio_setcb_read(io, on_recv);
|
|
|
hio_read(io);
|
|
|
@@ -203,10 +247,14 @@ int main(int argc, char** argv) {
|
|
|
const char* strConnections = get_arg("c");
|
|
|
const char* strDuration = get_arg("d");
|
|
|
const char* strThreads = get_arg("t");
|
|
|
+ const char* strBytes = get_arg("b");
|
|
|
+ const char* strInterval = get_arg("i");
|
|
|
|
|
|
if (strConnections) connections = atoi(strConnections);
|
|
|
if (strDuration) duration = atoi(strDuration);
|
|
|
if (strThreads) threads = atoi(strThreads);
|
|
|
+ if (strBytes) bytes = atoi(strBytes);
|
|
|
+ if (strInterval) interval = atoi(strInterval);
|
|
|
|
|
|
print_cmd();
|
|
|
|
|
|
@@ -243,14 +291,19 @@ int main(int argc, char** argv) {
|
|
|
// Dump request
|
|
|
request->headers["User-Agent"] = std::string("libhv/") + hv_version();
|
|
|
request->headers["Connection"] = "keep-alive";
|
|
|
+ if (bytes > 0) {
|
|
|
+ request->method = HTTP_POST;
|
|
|
+ request->body = std::string(bytes, 'a');
|
|
|
+ }
|
|
|
request_msg = request->Dump(true, true);
|
|
|
- printf("%s", request_msg.c_str());
|
|
|
+ printf("%.*s", int(request_msg.size() - request->body.size()), request_msg.c_str());
|
|
|
|
|
|
// EventLoopThreadPool
|
|
|
EventLoopThreadPool loop_threads(threads);
|
|
|
loop_threads.start(true);
|
|
|
|
|
|
// connections
|
|
|
+ connect_start_time = gettick_ms();
|
|
|
conns = (connection_t**)malloc(sizeof(connection_t*) * connections);
|
|
|
for (int i = 0; i < connections; ++i) {
|
|
|
conns[i] = new connection_t;
|