[MEDIUM] optimized the request parser a bit more

Some while() constructs are not very efficient with gcc, yet they are
used to scan all the text in the start line and the headers. Replacing
them with more efficient (but ugly) loops provides a global gain of
about 2%, which is not bad at all !
This commit is contained in:
Willy Tarreau 2006-12-17 12:05:00 +01:00
parent 976f1ee561
commit 230fd0bfdf

View File

@ -515,22 +515,51 @@ int process_cli(struct session *t)
#endif #endif
} else if (parse == HTTP_PA_HEADER) { } else if (parse == HTTP_PA_HEADER) {
parse_inside_hdr: char *ptr;
/* Inside a non-empty header */ /* Inside a non-empty header */
parse_inside_hdr:
delete_header = 0; delete_header = 0;
while (req->lr < req->r && !IS_CTL(*req->lr))
req->lr++; ptr = req->lr;
if (req->lr == req->r)
#ifdef GCC_FINALLY_PRODUCES_EFFICIENT_WHILE_LOOPS
/* This code is disabled right now because
* eventhough it seems straightforward, the
* object code produced by GCC is so much
* suboptimal that about 10% of the time
* spend parsing header is there.
*/
while (ptr < req->r && !IS_CTL(*ptr))
ptr++;
req->lr = ptr;
if (ptr == req->r)
break; break;
#else
/* Just by using this loop instead of the previous one,
* the global performance increases by about 2% ! The
* code is also smaller by about 50 bytes.
*/
goto reqhdr_loop_chk;
reqhdr_loop:
ptr++;
reqhdr_loop_chk:
if (ptr == req->r) {
req->lr = ptr;
break;
}
if (*ptr != 0x7F && (unsigned)*ptr >= 0x20)
goto reqhdr_loop;
req->lr = ptr;
#endif
/* we have a CTL char */ /* we have a CTL char */
if (*req->lr == '\r') { if (*ptr == '\r') {
t->hreq.hdr_state = HTTP_PA_HDR_LF | HTTP_PA_CR_SKIP | HTTP_PA_LF_EXP; t->hreq.hdr_state = HTTP_PA_HDR_LF | HTTP_PA_CR_SKIP | HTTP_PA_LF_EXP;
req->lr++; req->lr++;
continue; continue;
} }
else if (*req->lr == '\n') { else if (*ptr == '\n') {
t->hreq.hdr_state = HTTP_PA_HDR_LF; t->hreq.hdr_state = HTTP_PA_HDR_LF;
QUICK_JUMP(parse_hdr_lf, continue); QUICK_JUMP(parse_hdr_lf, continue);
} }
@ -577,19 +606,49 @@ int process_cli(struct session *t)
QUICK_JUMP(parse_start, continue); QUICK_JUMP(parse_start, continue);
} else if (parse == HTTP_PA_START) { } else if (parse == HTTP_PA_START) {
char *ptr;
/* Inside the start line */
parse_start: parse_start:
/* Start line */ ptr = req->lr;
while (req->lr < req->r && !IS_CTL(*req->lr))
req->lr++; #ifdef GCC_FINALLY_PRODUCES_EFFICIENT_WHILE_LOOPS
if (req->lr == req->r) /* This code is disabled right now because
* eventhough it seems straightforward, the
* object code produced by GCC is so much
* suboptimal that about 10% of the time
* spend parsing header is there.
*/
while (ptr < req->r && !IS_CTL(*ptr))
ptr++;
req->lr = ptr;
if (ptr == req->r)
break; break;
#else
/* Just by using this loop instead of the previous one,
* the global performance increases by about 2% ! The
* code is also smaller by about 50 bytes.
*/
goto reqstrt_loop_chk;
reqstrt_loop:
ptr++;
reqstrt_loop_chk:
if (ptr == req->r) {
req->lr = ptr;
break;
}
if (*ptr != 0x7F && (unsigned)*ptr >= 0x20)
goto reqstrt_loop;
req->lr = ptr;
#endif
/* we have a CTL char */ /* we have a CTL char */
if (*req->lr == '\r') { if (*ptr == '\r') {
req->lr++; req->lr++;
t->hreq.hdr_state = HTTP_PA_STRT_LF | HTTP_PA_CR_SKIP | HTTP_PA_LF_EXP; t->hreq.hdr_state = HTTP_PA_STRT_LF | HTTP_PA_CR_SKIP | HTTP_PA_LF_EXP;
continue; continue;
} }
else if (*req->lr == '\n') { else if (*ptr == '\n') {
t->hreq.hdr_state = HTTP_PA_STRT_LF; t->hreq.hdr_state = HTTP_PA_STRT_LF;
/* we know that we still have one char available */ /* we know that we still have one char available */
QUICK_JUMP(parse_strt_lf, continue); QUICK_JUMP(parse_strt_lf, continue);
@ -765,26 +824,23 @@ int process_cli(struct session *t)
* filters and various options. In order to support 3-level * filters and various options. In order to support 3-level
* switching, here's how we should proceed : * switching, here's how we should proceed :
* *
* a) run fe->filters. * a) run be->filters.
* if (switch) then switch ->fi and ->be to the new backend. * if (switch) then switch ->fi and ->be to the new backend.
* b) run fi->filters. * b) run be->filters.
* If there's another switch, then switch ->be to the new be. * If there's another switch, then switch ->be to the new be.
* c) run be->filters * c) run be->filters
* There cannot be any switch from there, so ->be cannot be * There cannot be any switch from there, so ->be cannot be
* changed anymore. * changed anymore.
* *
* => filters always apply to ->be, then ->be may change.
*
* The response path will be able to apply either ->be, or * The response path will be able to apply either ->be, or
* ->be then ->fi, or ->be then ->fi then ->fe filters in order * ->be then ->fi, or ->be then ->fi then ->fe filters in order
* to match the reverse of the forward sequence. * to match the reverse of the forward sequence.
*/ */
do { do {
if (t->fi == t->fe) rule_set = t->be;
rule_set = t->fe;
else if (t->be == t->fi)
rule_set = t->fi;
else
rule_set = t->be;
/* try headers filters */ /* try headers filters */
if (rule_set->req_exp != NULL) if (rule_set->req_exp != NULL)
@ -815,9 +871,11 @@ int process_cli(struct session *t)
} while (rule_set != t->be); /* we loop only if t->be has changed */ } while (rule_set != t->be); /* we loop only if t->be has changed */
/* Right now, we know that we have processed the entire headers /*
* Right now, we know that we have processed the entire headers
* and that unwanted requests have been filtered out. We can do * and that unwanted requests have been filtered out. We can do
* whatever we want with the remaining request. * whatever we want with the remaining request. Also, now we
* may have separate values for ->fe, ->fi and ->be.
*/ */