From 6b981f388abd352f57c984818a26182af56b1f6d Mon Sep 17 00:00:00 2001 From: normalperson Date: Sun, 2 Mar 2008 02:34:57 +0000 Subject: http11: ~6% performance increase in header parsing Allocate one string object and avoid appending to it causing it to be resized. Additionally, optimize the string toupper copy so that it's done in a single pass. Also, use an inline, locale-independent toupper() implementation which should be more predictable for users with exotic locales (HTTP header names are always ASCII). The following test script was used: require 'mongrel' include Mongrel parser = HttpParser.new req = "GET /ruby HTTP/1.1\r\n" \ "User-Agent: curl/7.12.3\r\n" \ "Host: bogomips.org\r\n" \ "Accept: */*\r\n" \ "\r\n".freeze hash = Hash.new 100000.times do parser.execute(hash, req, 0) parser.reset hash.clear end git-svn-id: svn+ssh://rubyforge.org/var/svn/mongrel/trunk@989 19e92222-5c0b-0410-8929-a290d50e31e9 --- ext/http11/ext_help.h | 2 ++ ext/http11/http11.c | 29 +++++++++++++++++++---------- 2 files changed, 21 insertions(+), 10 deletions(-) (limited to 'ext') diff --git a/ext/http11/ext_help.h b/ext/http11/ext_help.h index 8b4d754..1017c64 100644 --- a/ext/http11/ext_help.h +++ b/ext/http11/ext_help.h @@ -4,6 +4,8 @@ #define RAISE_NOT_NULL(T) if(T == NULL) rb_raise(rb_eArgError, "NULL found for " # T " when shouldn't be."); #define DATA_GET(from,type,name) Data_Get_Struct(from,type,name); RAISE_NOT_NULL(name); #define REQUIRE_TYPE(V, T) if(TYPE(V) != T) rb_raise(rb_eTypeError, "Wrong argument type for " # V " required " # T); +#define ASCII_UPCASE_CHAR(ch) (ch & ~0x20) + #ifdef DEBUG #define TRACE() fprintf(stderr, "> %s:%d:%s\n", __FILE__, __LINE__, __FUNCTION__) diff --git a/ext/http11/http11.c b/ext/http11/http11.c index dd9bc79..3cb6697 100644 --- a/ext/http11/http11.c +++ b/ext/http11/http11.c @@ -7,7 +7,6 @@ #include #include #include "http11_parser.h" -#include #ifndef RSTRING_PTR #define RSTRING_PTR(s) (RSTRING(s)->ptr) @@ -69,7 +68,8 @@ DEF_MAX_LENGTH(HEADER, (1024 * (80 + 32))); void http_field(void *data, const char *field, size_t flen, const char *value, size_t vlen) { - char *ch, *end; + char *ch; + const char *fch; VALUE req = (VALUE)data; VALUE v = Qnil; VALUE f = Qnil; @@ -78,15 +78,24 @@ void http_field(void *data, const char *field, size_t flen, const char *value, s VALIDATE_MAX_LENGTH(vlen, FIELD_VALUE); v = rb_str_new(value, vlen); - f = rb_str_dup(global_http_prefix); - f = rb_str_buf_cat(f, field, flen); - for(ch = RSTRING_PTR(f), end = ch + RSTRING_LEN(f); ch < end; ch++) { - if(*ch == '-') { - *ch = '_'; - } else { - *ch = toupper(*ch); - } + /* + * using rb_str_new(NULL, len) here is faster than rb_str_buf_new(len) + * in my testing, because: there's no minimum allocation length (and + * no check for it, either), RSTRING_LEN(f) does not need to be + * written twice, and and RSTRING_PTR(f) will already be + * null-terminated for us. + */ + f = rb_str_new(NULL, RSTRING_LEN(global_http_prefix) + flen); + memcpy(RSTRING_PTR(f), + RSTRING_PTR(global_http_prefix), + RSTRING_LEN(global_http_prefix)); + + ch = RSTRING_PTR(f) + RSTRING_LEN(global_http_prefix); + for(fch = field; flen-- != 0; ++fch) { + *ch++ = (*fch >= 'a' && *fch <= 'z') ? + ASCII_UPCASE_CHAR(*fch) : + (*fch == '-' ? '_' : *fch); } rb_hash_aset(req, f, v); -- cgit v1.2.3-24-ge0c7