From 0dc7fee7133d1cd2c7d5031c6d26cbba801d22e8 Mon Sep 17 00:00:00 2001 From: Tatsuhiro Tsujikawa Date: Wed, 4 Nov 2015 01:03:35 +0900 Subject: [PATCH] h2load: Print "space savings" to measure header compression efficiency --- doc/h2load.h2r | 12 ++++++++---- src/h2load.cc | 16 ++++++++++++---- src/h2load.h | 6 +++++- src/h2load_http1_session.cc | 2 ++ src/h2load_http2_session.cc | 5 ++++- src/h2load_spdy_session.cc | 13 ++++++++++--- 6 files changed, 41 insertions(+), 13 deletions(-) diff --git a/doc/h2load.h2r b/doc/h2load.h2r index 14aa763e..86e57e08 100644 --- a/doc/h2load.h2r +++ b/doc/h2load.h2r @@ -33,10 +33,14 @@ traffic requests were made via TLS, this value is the number of decrpyted bytes. headers - The number of response header bytes from the server without - decompression. For HTTP/2, this is the sum of the payload of - HEADERS frame. For SPDY, this is the sum of the payload of - SYN_REPLY frame. + The number of response header bytes from the server without + decompression. The ``space savings`` shows efficiency of header + compression. Let ``decompressed(headers)`` to the number of bytes + used for header fields after decompression. The ``space savings`` + is calculated by (1 - ``headers`` / ``decompressed(headers)``) * + 100. For HTTP/1.1, this is usually 0.00%, since it does not have + header compression. For HTTP/2 and SPDY, it shows some insightful + numbers. data The number of response body bytes received from the server. diff --git a/src/h2load.cc b/src/h2load.cc index 0658d623..61e1112a 100644 --- a/src/h2load.cc +++ b/src/h2load.cc @@ -95,8 +95,8 @@ RequestStat::RequestStat() : data_offset(0), completed(false) {} Stats::Stats(size_t req_todo) : req_todo(0), req_started(0), req_done(0), req_success(0), req_status_success(0), req_failed(0), req_error(0), req_timedout(0), - bytes_total(0), bytes_head(0), bytes_body(0), status(), - req_stats(req_todo) {} + bytes_total(0), bytes_head(0), bytes_head_decomp(0), bytes_body(0), + status(), req_stats(req_todo) {} Stream::Stream() : status_success(-1) {} @@ -2073,6 +2073,7 @@ int main(int argc, char **argv) { stats.req_error += s.req_error; stats.bytes_total += s.bytes_total; stats.bytes_head += s.bytes_head; + stats.bytes_head_decomp += s.bytes_head_decomp; stats.bytes_body += s.bytes_body; for (size_t i = 0; i < stats.status.size(); ++i) { @@ -2102,7 +2103,13 @@ int main(int argc, char **argv) { bps = stats.bytes_total / secd.count(); } - std::cout << R"( + double header_space_savings = 0.; + if (stats.bytes_head_decomp > 0) { + header_space_savings = + 1. - static_cast(stats.bytes_head) / stats.bytes_head_decomp; + } + + std::cout << std::fixed << std::setprecision(2) << R"( finished in )" << util::format_duration(duration) << ", " << rps << " req/s, " << util::utos_with_funit(bps) << R"(B/s requests: )" << stats.req_todo << " total, " << stats.req_started @@ -2113,7 +2120,8 @@ requests: )" << stats.req_todo << " total, " << stats.req_started status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, " << stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head - << " bytes headers, " << stats.bytes_body << R"( bytes data + << " bytes headers (space savings " << header_space_savings * 100 + << "%), " << stats.bytes_body << R"( bytes data min max mean sd +/- sd time for request: )" << std::setw(10) << util::format_duration(ts.request.min) << " " << std::setw(10) << util::format_duration(ts.request.max) diff --git a/src/h2load.h b/src/h2load.h index 0b5f747b..fe6c99ff 100644 --- a/src/h2load.h +++ b/src/h2load.h @@ -166,8 +166,12 @@ struct Stats { // The number of bytes received on the "wire". If SSL/TLS is used, // this is the number of decrypted bytes the application received. int64_t bytes_total; - // The number of bytes received in HEADERS frame payload. + // The number of bytes received for header fields. This is + // compressed version. int64_t bytes_head; + // The number of bytes received for header fields after they are + // decompressed. + int64_t bytes_head_decomp; // The number of bytes received in DATA frame. int64_t bytes_body; // The number of each HTTP status category, status[i] is status code diff --git a/src/h2load_http1_session.cc b/src/h2load_http1_session.cc index 30691ed8..728233d3 100644 --- a/src/h2load_http1_session.cc +++ b/src/h2load_http1_session.cc @@ -99,6 +99,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) { auto client = session->get_client(); client->worker->stats.bytes_head += len; + client->worker->stats.bytes_head_decomp += len; return 0; } } // namespace @@ -109,6 +110,7 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) { auto client = session->get_client(); client->worker->stats.bytes_head += len; + client->worker->stats.bytes_head_decomp += len; return 0; } } // namespace diff --git a/src/h2load_http2_session.cc b/src/h2load_http2_session.cc index 00ff92fe..0bbbe8e8 100644 --- a/src/h2load_http2_session.cc +++ b/src/h2load_http2_session.cc @@ -51,6 +51,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame, return 0; } client->on_header(frame->hd.stream_id, name, namelen, value, valuelen); + client->worker->stats.bytes_head_decomp += namelen + valuelen; return 0; } } // namespace @@ -63,7 +64,9 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame, frame->headers.cat != NGHTTP2_HCAT_RESPONSE) { return 0; } - client->worker->stats.bytes_head += frame->hd.length; + client->worker->stats.bytes_head += + frame->hd.length - frame->headers.padlen - + ((frame->hd.flags & NGHTTP2_FLAG_PRIORITY) ? 5 : 0); if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) { client->record_ttfb(); } diff --git a/src/h2load_spdy_session.cc b/src/h2load_spdy_session.cc index a7d52de7..a3b85591 100644 --- a/src/h2load_spdy_session.cc +++ b/src/h2load_spdy_session.cc @@ -65,11 +65,18 @@ void on_ctrl_recv_callback(spdylay_session *session, spdylay_frame_type type, for (auto p = frame->syn_reply.nv; *p; p += 2) { auto name = *p; auto value = *(p + 1); + auto namelen = strlen(name); + auto valuelen = strlen(value); client->on_header(frame->syn_reply.stream_id, - reinterpret_cast(name), strlen(name), - reinterpret_cast(value), strlen(value)); + reinterpret_cast(name), namelen, + reinterpret_cast(value), valuelen); + client->worker->stats.bytes_head_decomp += namelen + valuelen; } - client->worker->stats.bytes_head += frame->syn_reply.hd.length; + + // Strictly speaking, we have to subtract 2 (unused field) if SPDY + // version is 2. But it is already deprecated, and we don't do + // extra work for it. + client->worker->stats.bytes_head += frame->syn_reply.hd.length - 4; if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) { client->record_ttfb();