h2load: Print "space savings" to measure header compression efficiency
This commit is contained in:
parent
58d636abdb
commit
0dc7fee713
|
@ -34,9 +34,13 @@ traffic
|
||||||
bytes.
|
bytes.
|
||||||
headers
|
headers
|
||||||
The number of response header bytes from the server without
|
The number of response header bytes from the server without
|
||||||
decompression. For HTTP/2, this is the sum of the payload of
|
decompression. The ``space savings`` shows efficiency of header
|
||||||
HEADERS frame. For SPDY, this is the sum of the payload of
|
compression. Let ``decompressed(headers)`` to the number of bytes
|
||||||
SYN_REPLY frame.
|
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
|
data
|
||||||
The number of response body bytes received from the server.
|
The number of response body bytes received from the server.
|
||||||
|
|
||||||
|
|
|
@ -95,8 +95,8 @@ RequestStat::RequestStat() : data_offset(0), completed(false) {}
|
||||||
Stats::Stats(size_t req_todo)
|
Stats::Stats(size_t req_todo)
|
||||||
: req_todo(0), req_started(0), req_done(0), req_success(0),
|
: 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),
|
req_status_success(0), req_failed(0), req_error(0), req_timedout(0),
|
||||||
bytes_total(0), bytes_head(0), bytes_body(0), status(),
|
bytes_total(0), bytes_head(0), bytes_head_decomp(0), bytes_body(0),
|
||||||
req_stats(req_todo) {}
|
status(), req_stats(req_todo) {}
|
||||||
|
|
||||||
Stream::Stream() : status_success(-1) {}
|
Stream::Stream() : status_success(-1) {}
|
||||||
|
|
||||||
|
@ -2073,6 +2073,7 @@ int main(int argc, char **argv) {
|
||||||
stats.req_error += s.req_error;
|
stats.req_error += s.req_error;
|
||||||
stats.bytes_total += s.bytes_total;
|
stats.bytes_total += s.bytes_total;
|
||||||
stats.bytes_head += s.bytes_head;
|
stats.bytes_head += s.bytes_head;
|
||||||
|
stats.bytes_head_decomp += s.bytes_head_decomp;
|
||||||
stats.bytes_body += s.bytes_body;
|
stats.bytes_body += s.bytes_body;
|
||||||
|
|
||||||
for (size_t i = 0; i < stats.status.size(); ++i) {
|
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();
|
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<double>(stats.bytes_head) / stats.bytes_head_decomp;
|
||||||
|
}
|
||||||
|
|
||||||
|
std::cout << std::fixed << std::setprecision(2) << R"(
|
||||||
finished in )" << util::format_duration(duration) << ", " << rps << " req/s, "
|
finished in )" << util::format_duration(duration) << ", " << rps << " req/s, "
|
||||||
<< util::utos_with_funit(bps) << R"(B/s
|
<< util::utos_with_funit(bps) << R"(B/s
|
||||||
requests: )" << stats.req_todo << " total, " << stats.req_started
|
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, "
|
status codes: )" << stats.status[2] << " 2xx, " << stats.status[3] << " 3xx, "
|
||||||
<< stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
|
<< stats.status[4] << " 4xx, " << stats.status[5] << R"( 5xx
|
||||||
traffic: )" << stats.bytes_total << " bytes total, " << stats.bytes_head
|
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
|
min max mean sd +/- sd
|
||||||
time for request: )" << std::setw(10) << util::format_duration(ts.request.min)
|
time for request: )" << std::setw(10) << util::format_duration(ts.request.min)
|
||||||
<< " " << std::setw(10) << util::format_duration(ts.request.max)
|
<< " " << std::setw(10) << util::format_duration(ts.request.max)
|
||||||
|
|
|
@ -166,8 +166,12 @@ struct Stats {
|
||||||
// The number of bytes received on the "wire". If SSL/TLS is used,
|
// The number of bytes received on the "wire". If SSL/TLS is used,
|
||||||
// this is the number of decrypted bytes the application received.
|
// this is the number of decrypted bytes the application received.
|
||||||
int64_t bytes_total;
|
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;
|
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.
|
// The number of bytes received in DATA frame.
|
||||||
int64_t bytes_body;
|
int64_t bytes_body;
|
||||||
// The number of each HTTP status category, status[i] is status code
|
// The number of each HTTP status category, status[i] is status code
|
||||||
|
|
|
@ -99,6 +99,7 @@ int htp_hdr_keycb(http_parser *htp, const char *data, size_t len) {
|
||||||
auto client = session->get_client();
|
auto client = session->get_client();
|
||||||
|
|
||||||
client->worker->stats.bytes_head += len;
|
client->worker->stats.bytes_head += len;
|
||||||
|
client->worker->stats.bytes_head_decomp += len;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -109,6 +110,7 @@ int htp_hdr_valcb(http_parser *htp, const char *data, size_t len) {
|
||||||
auto client = session->get_client();
|
auto client = session->get_client();
|
||||||
|
|
||||||
client->worker->stats.bytes_head += len;
|
client->worker->stats.bytes_head += len;
|
||||||
|
client->worker->stats.bytes_head_decomp += len;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
|
@ -51,6 +51,7 @@ int on_header_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
|
client->on_header(frame->hd.stream_id, name, namelen, value, valuelen);
|
||||||
|
client->worker->stats.bytes_head_decomp += namelen + valuelen;
|
||||||
return 0;
|
return 0;
|
||||||
}
|
}
|
||||||
} // namespace
|
} // namespace
|
||||||
|
@ -63,7 +64,9 @@ int on_frame_recv_callback(nghttp2_session *session, const nghttp2_frame *frame,
|
||||||
frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
|
frame->headers.cat != NGHTTP2_HCAT_RESPONSE) {
|
||||||
return 0;
|
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) {
|
if (frame->hd.flags & NGHTTP2_FLAG_END_STREAM) {
|
||||||
client->record_ttfb();
|
client->record_ttfb();
|
||||||
}
|
}
|
||||||
|
|
|
@ -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) {
|
for (auto p = frame->syn_reply.nv; *p; p += 2) {
|
||||||
auto name = *p;
|
auto name = *p;
|
||||||
auto value = *(p + 1);
|
auto value = *(p + 1);
|
||||||
|
auto namelen = strlen(name);
|
||||||
|
auto valuelen = strlen(value);
|
||||||
client->on_header(frame->syn_reply.stream_id,
|
client->on_header(frame->syn_reply.stream_id,
|
||||||
reinterpret_cast<const uint8_t *>(name), strlen(name),
|
reinterpret_cast<const uint8_t *>(name), namelen,
|
||||||
reinterpret_cast<const uint8_t *>(value), strlen(value));
|
reinterpret_cast<const uint8_t *>(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) {
|
if (frame->syn_stream.hd.flags & SPDYLAY_CTRL_FLAG_FIN) {
|
||||||
client->record_ttfb();
|
client->record_ttfb();
|
||||||
|
|
Loading…
Reference in New Issue