Skip to content

Commit

Permalink
Speed up the http_parser_execute loop
Browse files Browse the repository at this point in the history
Changes:
* Cache parser->nread in a local variable (same optimization that was
  already in place for parser->state).
* Move the cost of the conditional branch for spaces in tokens out of
  the fast-tokenizer implementation and into the strict-tokenizer
  implementation.

Together, these changes yield a ~15% increase in MB/s and req/s in
the included bench program on x86_64.

PR-URL: nodejs#422
Reviewed-By: Ben Noordhuis <info@bnoordhuis.nl>
  • Loading branch information
Brian Pane authored and bnoordhuis committed May 30, 2018
1 parent 3123273 commit cf69c8e
Showing 1 changed file with 21 additions and 9 deletions.
30 changes: 21 additions & 9 deletions http_parser.c
Original file line number Diff line number Diff line change
Expand Up @@ -49,13 +49,15 @@

#define SET_ERRNO(e) \
do { \
parser->nread = nread; \
parser->http_errno = (e); \
} while(0)

#define CURRENT_STATE() p_state
#define UPDATE_STATE(V) p_state = (enum state) (V);
#define RETURN(V) \
do { \
parser->nread = nread; \
parser->state = CURRENT_STATE(); \
return (V); \
} while (0);
Expand Down Expand Up @@ -149,8 +151,8 @@ do { \
*/
#define COUNT_HEADER_SIZE(V) \
do { \
parser->nread += (V); \
if (UNLIKELY(parser->nread > (HTTP_MAX_HEADER_SIZE))) { \
nread += (V); \
if (UNLIKELY(nread > (HTTP_MAX_HEADER_SIZE))) { \
SET_ERRNO(HPE_HEADER_OVERFLOW); \
goto error; \
} \
Expand Down Expand Up @@ -192,7 +194,7 @@ static const char tokens[256] = {
/* 24 can 25 em 26 sub 27 esc 28 fs 29 gs 30 rs 31 us */
0, 0, 0, 0, 0, 0, 0, 0,
/* 32 sp 33 ! 34 " 35 # 36 $ 37 % 38 & 39 ' */
0, '!', 0, '#', '$', '%', '&', '\'',
' ', '!', 0, '#', '$', '%', '&', '\'',
/* 40 ( 41 ) 42 * 43 + 44 , 45 - 46 . 47 / */
0, 0, '*', '+', 0, '-', '.', 0,
/* 48 0 49 1 50 2 51 3 52 4 53 5 54 6 55 7 */
Expand Down Expand Up @@ -419,14 +421,14 @@ enum http_host_state
(c) == ';' || (c) == ':' || (c) == '&' || (c) == '=' || (c) == '+' || \
(c) == '$' || (c) == ',')

#define STRICT_TOKEN(c) (tokens[(unsigned char)c])
#define STRICT_TOKEN(c) ((c == ' ') ? 0 : tokens[(unsigned char)c])

#if HTTP_PARSER_STRICT
#define TOKEN(c) (tokens[(unsigned char)c])
#define TOKEN(c) STRICT_TOKEN(c)
#define IS_URL_CHAR(c) (BIT_AT(normal_url_char, (unsigned char)c))
#define IS_HOST_CHAR(c) (IS_ALPHANUM(c) || (c) == '.' || (c) == '-')
#else
#define TOKEN(c) ((c == ' ') ? ' ' : tokens[(unsigned char)c])
#define TOKEN(c) tokens[(unsigned char)c]
#define IS_URL_CHAR(c) \
(BIT_AT(normal_url_char, (unsigned char)c) || ((c) & 0x80))
#define IS_HOST_CHAR(c) \
Expand Down Expand Up @@ -644,6 +646,7 @@ size_t http_parser_execute (http_parser *parser,
const char *status_mark = 0;
enum state p_state = (enum state) parser->state;
const unsigned int lenient = parser->lenient_http_headers;
uint32_t nread = parser->nread;

/* We're in an error state. Don't bother doing anything. */
if (HTTP_PARSER_ERRNO(parser) != HPE_OK) {
Expand Down Expand Up @@ -1238,8 +1241,14 @@ size_t http_parser_execute (http_parser *parser,
break;

switch (parser->header_state) {
case h_general:
case h_general: {
size_t limit = data + len - p;
limit = MIN(limit, HTTP_MAX_HEADER_SIZE);
while (p+1 < data + limit && TOKEN(p[1])) {
p++;
}
break;
}

case h_C:
parser->index++;
Expand Down Expand Up @@ -1487,7 +1496,6 @@ size_t http_parser_execute (http_parser *parser,
p = data + len;
}
--p;

break;
}

Expand Down Expand Up @@ -1795,6 +1803,7 @@ size_t http_parser_execute (http_parser *parser,
STRICT_CHECK(ch != LF);

parser->nread = 0;
nread = 0;

hasBody = parser->flags & F_CHUNKED ||
(parser->content_length > 0 && parser->content_length != ULLONG_MAX);
Expand Down Expand Up @@ -1889,7 +1898,7 @@ size_t http_parser_execute (http_parser *parser,

case s_chunk_size_start:
{
assert(parser->nread == 1);
assert(nread == 1);
assert(parser->flags & F_CHUNKED);

unhex_val = unhex[(unsigned char)ch];
Expand Down Expand Up @@ -1957,6 +1966,7 @@ size_t http_parser_execute (http_parser *parser,
STRICT_CHECK(ch != LF);

parser->nread = 0;
nread = 0;

if (parser->content_length == 0) {
parser->flags |= F_TRAILING;
Expand Down Expand Up @@ -2003,6 +2013,7 @@ size_t http_parser_execute (http_parser *parser,
assert(parser->flags & F_CHUNKED);
STRICT_CHECK(ch != LF);
parser->nread = 0;
nread = 0;
UPDATE_STATE(s_chunk_size_start);
CALLBACK_NOTIFY(chunk_complete);
break;
Expand Down Expand Up @@ -2436,6 +2447,7 @@ http_parser_pause(http_parser *parser, int paused) {
*/
if (HTTP_PARSER_ERRNO(parser) == HPE_OK ||
HTTP_PARSER_ERRNO(parser) == HPE_PAUSED) {
uint32_t nread = parser->nread; /* used by the SET_ERRNO macro */
SET_ERRNO((paused) ? HPE_PAUSED : HPE_OK);
} else {
assert(0 && "Attempting to pause parser in error state");
Expand Down

0 comments on commit cf69c8e

Please sign in to comment.