http: speed up callbacks, use array indices
authorBen Noordhuis <info@bnoordhuis.nl>
Tue, 13 Aug 2013 21:47:17 +0000 (23:47 +0200)
committerBen Noordhuis <info@bnoordhuis.nl>
Wed, 14 Aug 2013 16:08:04 +0000 (18:08 +0200)
Use array indices rather than named properties to store callbacks on
the HTTPParser object.  Speeds up the http benchmarks by a few percent.

lib/_http_common.js
src/node_http_parser.cc
test/simple/test-http-parser-bad-ref.js
test/simple/test-http-parser.js

index 4cf30ee..0d55651 100644 (file)
@@ -27,7 +27,6 @@ var IncomingMessage = incoming.IncomingMessage;
 var readStart = incoming.readStart;
 var readStop = incoming.readStop;
 
-
 var debug = require('util').debuglog('http');
 exports.debug = debug;
 
@@ -35,6 +34,11 @@ exports.CRLF = '\r\n';
 exports.chunkExpression = /chunk/i;
 exports.continueExpression = /100-continue/i;
 
+var kOnHeaders = HTTPParser.kOnHeaders | 0;
+var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
+var kOnBody = HTTPParser.kOnBody | 0;
+var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
+
 // Only called in the slow case where slow means
 // that the request headers were either fragmented
 // across multiple TCP packets or too large to be
@@ -180,10 +184,10 @@ var parsers = new FreeList('parsers', 1000, function() {
   // across multiple TCP packets or too large to be
   // processed in a single run. This method is also
   // called to process trailing HTTP headers.
-  parser.onHeaders = parserOnHeaders;
-  parser.onHeadersComplete = parserOnHeadersComplete;
-  parser.onBody = parserOnBody;
-  parser.onMessageComplete = parserOnMessageComplete;
+  parser[kOnHeaders] = parserOnHeaders;
+  parser[kOnHeadersComplete] = parserOnHeadersComplete;
+  parser[kOnBody] = parserOnBody;
+  parser[kOnMessageComplete] = parserOnMessageComplete;
 
   return parser;
 });
index 72fda5e..80485ec 100644 (file)
@@ -60,10 +60,10 @@ using v8::Object;
 using v8::String;
 using v8::Value;
 
-static Cached<String> on_headers_sym;
-static Cached<String> on_headers_complete_sym;
-static Cached<String> on_body_sym;
-static Cached<String> on_message_complete_sym;
+const uint32_t kOnHeaders = 0;
+const uint32_t kOnHeadersComplete = 1;
+const uint32_t kOnBody = 2;
+const uint32_t kOnMessageComplete = 3;
 
 static Cached<String> method_sym;
 static Cached<String> status_code_sym;
@@ -255,7 +255,7 @@ class Parser : public ObjectWrap {
 
   HTTP_CB(on_headers_complete) {
     Local<Object> obj = handle(node_isolate);
-    Local<Value> cb = obj->Get(on_headers_complete_sym);
+    Local<Value> cb = obj->Get(kOnHeadersComplete);
 
     if (!cb->IsFunction())
       return 0;
@@ -315,7 +315,7 @@ class Parser : public ObjectWrap {
     HandleScope scope(node_isolate);
 
     Local<Object> obj = handle(node_isolate);
-    Local<Value> cb = obj->Get(on_body_sym);
+    Local<Value> cb = obj->Get(kOnBody);
 
     if (!cb->IsFunction())
       return 0;
@@ -344,7 +344,7 @@ class Parser : public ObjectWrap {
       Flush();  // Flush trailing HTTP headers.
 
     Local<Object> obj = handle(node_isolate);
-    Local<Value> cb = obj->Get(on_message_complete_sym);
+    Local<Value> cb = obj->Get(kOnMessageComplete);
 
     if (!cb->IsFunction())
       return 0;
@@ -506,7 +506,7 @@ class Parser : public ObjectWrap {
     HandleScope scope(node_isolate);
 
     Local<Object> obj = handle(node_isolate);
-    Local<Value> cb = obj->Get(on_headers_sym);
+    Local<Value> cb = obj->Get(kOnHeaders);
 
     if (!cb->IsFunction())
       return;
@@ -558,6 +558,14 @@ void InitHttpParser(Handle<Object> target) {
          Integer::New(HTTP_REQUEST, node_isolate));
   t->Set(FIXED_ONE_BYTE_STRING(node_isolate, "RESPONSE"),
          Integer::New(HTTP_RESPONSE, node_isolate));
+  t->Set(FIXED_ONE_BYTE_STRING(node_isolate, "kOnHeaders"),
+         Integer::NewFromUnsigned(kOnHeaders, node_isolate));
+  t->Set(FIXED_ONE_BYTE_STRING(node_isolate, "kOnHeadersComplete"),
+         Integer::NewFromUnsigned(kOnHeadersComplete, node_isolate));
+  t->Set(FIXED_ONE_BYTE_STRING(node_isolate, "kOnBody"),
+         Integer::NewFromUnsigned(kOnBody, node_isolate));
+  t->Set(FIXED_ONE_BYTE_STRING(node_isolate, "kOnMessageComplete"),
+         Integer::NewFromUnsigned(kOnMessageComplete, node_isolate));
 
   NODE_SET_PROTOTYPE_METHOD(t, "execute", Parser::Execute);
   NODE_SET_PROTOTYPE_METHOD(t, "finish", Parser::Finish);
@@ -566,15 +574,6 @@ void InitHttpParser(Handle<Object> target) {
   target->Set(FIXED_ONE_BYTE_STRING(node_isolate, "HTTPParser"),
               t->GetFunction());
 
-  on_headers_sym =
-      FIXED_ONE_BYTE_STRING(node_isolate, "onHeaders");
-  on_headers_complete_sym =
-      FIXED_ONE_BYTE_STRING(node_isolate, "onHeadersComplete");
-  on_body_sym =
-      FIXED_ONE_BYTE_STRING(node_isolate, "onBody");
-  on_message_complete_sym =
-      FIXED_ONE_BYTE_STRING(node_isolate, "onMessageComplete");
-
 #define X(num, name, string)                                                  \
   name ## _sym = OneByteString(node_isolate, #string);
   HTTP_METHOD_MAP(X)
index 4a9c705..0e2a77b 100644 (file)
@@ -7,6 +7,11 @@ var common = require('../common');
 var assert = require('assert');
 var HTTPParser = process.binding('http_parser').HTTPParser;
 
+var kOnHeaders = HTTPParser.kOnHeaders | 0;
+var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
+var kOnBody = HTTPParser.kOnBody | 0;
+var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
+
 var headersComplete = 0;
 var messagesComplete = 0;
 
@@ -23,19 +28,19 @@ function demoBug(part1, part2) {
   parser.headers = [];
   parser.url = '';
 
-  parser.onHeaders = function(headers, url) {
+  parser[kOnHeaders] = function(headers, url) {
     parser.headers = parser.headers.concat(headers);
     parser.url += url;
   };
 
-  parser.onHeadersComplete = function(info) {
+  parser[kOnHeadersComplete] = function(info) {
     headersComplete++;
     console.log('url', info.url);
   };
 
-  parser.onBody = function(b, start, len) { };
+  parser[kOnBody] = function(b, start, len) { };
 
-  parser.onMessageComplete = function() {
+  parser[kOnMessageComplete] = function() {
     messagesComplete++;
   };
 
index e8c8095..e0f1fbf 100644 (file)
@@ -28,6 +28,11 @@ var CRLF = '\r\n';
 var REQUEST = HTTPParser.REQUEST;
 var RESPONSE = HTTPParser.RESPONSE;
 
+var kOnHeaders = HTTPParser.kOnHeaders | 0;
+var kOnHeadersComplete = HTTPParser.kOnHeadersComplete | 0;
+var kOnBody = HTTPParser.kOnBody | 0;
+var kOnMessageComplete = HTTPParser.kOnMessageComplete | 0;
+
 // The purpose of this test is not to check HTTP compliance but to test the
 // binding. Tests for pathological http messages should be submitted
 // upstream to https://github.com/joyent/http-parser for inclusion into
@@ -40,19 +45,19 @@ function newParser(type) {
   parser.headers = [];
   parser.url = '';
 
-  parser.onHeaders = function(headers, url) {
+  parser[kOnHeaders] = function(headers, url) {
     parser.headers = parser.headers.concat(headers);
     parser.url += url;
   };
 
-  parser.onHeadersComplete = function(info) {
+  parser[kOnHeadersComplete] = function(info) {
   };
 
-  parser.onBody = function(b, start, len) {
+  parser[kOnBody] = function(b, start, len) {
     assert.ok(false, 'Function should not be called.');
   };
 
-  parser.onMessageComplete = function() {
+  parser[kOnMessageComplete] = function() {
   };
 
   return parser;
@@ -92,7 +97,7 @@ function expectBody(expected) {
 
   var parser = newParser(REQUEST);
 
-  parser.onHeadersComplete = mustCall(function(info) {
+  parser[kOnHeadersComplete] = mustCall(function(info) {
     assert.equal(info.method, 'GET');
     assert.equal(info.url || parser.url, '/hello');
     assert.equal(info.versionMajor, 1);
@@ -106,7 +111,7 @@ function expectBody(expected) {
   // thrown from parser.execute()
   //
 
-  parser.onHeadersComplete = function(info) {
+  parser[kOnHeadersComplete] = function(info) {
     throw new Error('hello world');
   };
 
@@ -131,14 +136,14 @@ function expectBody(expected) {
 
   var parser = newParser(RESPONSE);
 
-  parser.onHeadersComplete = mustCall(function(info) {
+  parser[kOnHeadersComplete] = mustCall(function(info) {
     assert.equal(info.method, undefined);
     assert.equal(info.versionMajor, 1);
     assert.equal(info.versionMinor, 1);
     assert.equal(info.statusCode, 200);
   });
 
-  parser.onBody = mustCall(function(buf, start, len) {
+  parser[kOnBody] = mustCall(function(buf, start, len) {
     var body = '' + buf.slice(start, start + len);
     assert.equal(body, 'pong');
   });
@@ -157,7 +162,7 @@ function expectBody(expected) {
 
   var parser = newParser(RESPONSE);
 
-  parser.onHeadersComplete = mustCall(function(info) {
+  parser[kOnHeadersComplete] = mustCall(function(info) {
     assert.equal(info.method, undefined);
     assert.equal(info.versionMajor, 1);
     assert.equal(info.versionMinor, 0);
@@ -194,16 +199,16 @@ function expectBody(expected) {
 
   var parser = newParser(REQUEST);
 
-  parser.onHeadersComplete = mustCall(function(info) {
+  parser[kOnHeadersComplete] = mustCall(function(info) {
     assert.equal(info.method, 'POST');
     assert.equal(info.url || parser.url, '/it');
     assert.equal(info.versionMajor, 1);
     assert.equal(info.versionMinor, 1);
     // expect to see trailing headers now
-    parser.onHeaders = mustCall(onHeaders);
+    parser[kOnHeaders] = mustCall(onHeaders);
   });
 
-  parser.onBody = mustCall(function(buf, start, len) {
+  parser[kOnBody] = mustCall(function(buf, start, len) {
     var body = '' + buf.slice(start, start + len);
     assert.equal(body, 'ping');
     seen_body = true;
@@ -226,7 +231,7 @@ function expectBody(expected) {
 
   var parser = newParser(REQUEST);
 
-  parser.onHeadersComplete = mustCall(function(info) {
+  parser[kOnHeadersComplete] = mustCall(function(info) {
     assert.equal(info.method, 'GET');
     assert.equal(info.versionMajor, 1);
     assert.equal(info.versionMinor, 0);
@@ -255,7 +260,7 @@ function expectBody(expected) {
 
   var parser = newParser(REQUEST);
 
-  parser.onHeadersComplete = mustCall(function(info) {
+  parser[kOnHeadersComplete] = mustCall(function(info) {
     assert.equal(info.method, 'GET');
     assert.equal(info.url || parser.url, '/foo/bar/baz?quux=42#1337');
     assert.equal(info.versionMajor, 1);
@@ -287,14 +292,14 @@ function expectBody(expected) {
 
   var parser = newParser(REQUEST);
 
-  parser.onHeadersComplete = mustCall(function(info) {
+  parser[kOnHeadersComplete] = mustCall(function(info) {
     assert.equal(info.method, 'POST');
     assert.equal(info.url || parser.url, '/it');
     assert.equal(info.versionMajor, 1);
     assert.equal(info.versionMinor, 1);
   });
 
-  parser.onBody = mustCall(function(buf, start, len) {
+  parser[kOnBody] = mustCall(function(buf, start, len) {
     var body = '' + buf.slice(start, start + len);
     assert.equal(body, 'foo=42&bar=1337');
   });
@@ -322,7 +327,7 @@ function expectBody(expected) {
 
   var parser = newParser(REQUEST);
 
-  parser.onHeadersComplete = mustCall(function(info) {
+  parser[kOnHeadersComplete] = mustCall(function(info) {
     assert.equal(info.method, 'POST');
     assert.equal(info.url || parser.url, '/it');
     assert.equal(info.versionMajor, 1);
@@ -337,7 +342,7 @@ function expectBody(expected) {
     assert.equal(body, body_parts[body_part++]);
   }
 
-  parser.onBody = mustCall(onBody, body_parts.length);
+  parser[kOnBody] = mustCall(onBody, body_parts.length);
   parser.execute(request, 0, request.length);
 })();
 
@@ -358,7 +363,7 @@ function expectBody(expected) {
 
   var parser = newParser(REQUEST);
 
-  parser.onHeadersComplete = mustCall(function(info) {
+  parser[kOnHeadersComplete] = mustCall(function(info) {
     assert.equal(info.method, 'POST');
     assert.equal(info.url || parser.url, '/it');
     assert.equal(info.versionMajor, 1);
@@ -375,7 +380,7 @@ function expectBody(expected) {
     assert.equal(body, body_parts[body_part++]);
   }
 
-  parser.onBody = mustCall(onBody, body_parts.length);
+  parser[kOnBody] = mustCall(onBody, body_parts.length);
   parser.execute(request, 0, request.length);
 
   request = Buffer(
@@ -415,7 +420,7 @@ function expectBody(expected) {
   function test(a, b) {
     var parser = newParser(REQUEST);
 
-    parser.onHeadersComplete = mustCall(function(info) {
+    parser[kOnHeadersComplete] = mustCall(function(info) {
       assert.equal(info.method, 'POST');
       assert.equal(info.url || parser.url, '/helpme');
       assert.equal(info.versionMajor, 1);
@@ -424,7 +429,7 @@ function expectBody(expected) {
 
     var expected_body = '123123456123456789123456789ABC123456789ABCDEF';
 
-    parser.onBody = function(buf, start, len) {
+    parser[kOnBody] = function(buf, start, len) {
       var chunk = '' + buf.slice(start, start + len);
       assert.equal(expected_body.indexOf(chunk), 0);
       expected_body = expected_body.slice(chunk.length);
@@ -471,7 +476,7 @@ function expectBody(expected) {
 
   var parser = newParser(REQUEST);
 
-  parser.onHeadersComplete = mustCall(function(info) {
+  parser[kOnHeadersComplete] = mustCall(function(info) {
     assert.equal(info.method, 'POST');
     assert.equal(info.url || parser.url, '/it');
     assert.equal(info.versionMajor, 1);
@@ -483,7 +488,7 @@ function expectBody(expected) {
 
   var expected_body = '123123456123456789123456789ABC123456789ABCDEF';
 
-  parser.onBody = function(buf, start, len) {
+  parser[kOnBody] = function(buf, start, len) {
     var chunk = '' + buf.slice(start, start + len);
     assert.equal(expected_body.indexOf(chunk), 0);
     expected_body = expected_body.slice(chunk.length);
@@ -538,12 +543,12 @@ function expectBody(expected) {
   };
 
   var parser = newParser(REQUEST);
-  parser.onHeadersComplete = onHeadersComplete1;
-  parser.onBody = expectBody('ping');
+  parser[kOnHeadersComplete] = onHeadersComplete1;
+  parser[kOnBody] = expectBody('ping');
   parser.execute(req1, 0, req1.length);
 
   parser.reinitialize(REQUEST);
-  parser.onBody = expectBody('pong');
-  parser.onHeadersComplete = onHeadersComplete2;
+  parser[kOnBody] = expectBody('pong');
+  parser[kOnHeadersComplete] = onHeadersComplete2;
   parser.execute(req2, 0, req2.length);
 })();