From 08133f45c73cbae241881979dd41d0d63b4f84a0 Mon Sep 17 00:00:00 2001 From: Brendan Ashworth Date: Sun, 25 Jan 2015 15:26:40 -0800 Subject: [PATCH] http: optimize outgoing requests MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit This commit does some small optimization changes on `lib/_http_outgoing.js`. These include switching from `while` loops to `for` loops, moving away from `util` to `typeof` checks, and removing dead code. It also includes variable caches to avoid lookups and generic style changes. All in all, much faster execution. It gets an across the board increase in req/sec on the benchmarks, from my experience about a 10% increase. PR-URL: https://github.com/iojs/io.js/pull/605 Reviewed-By: Ben Noordhuis Reviewed-By: Nicu Micleușanu Reviewed-By: Christian Vaagland Tellnes Reviewed-By: Brian White --- lib/_http_outgoing.js | 72 ++++++++++++++++++++++++++----------------- 1 file changed, 44 insertions(+), 28 deletions(-) diff --git a/lib/_http_outgoing.js b/lib/_http_outgoing.js index de977eb19c4cb5..2e64632a73fbf2 100644 --- a/lib/_http_outgoing.js +++ b/lib/_http_outgoing.js @@ -78,6 +78,7 @@ exports.OutgoingMessage = OutgoingMessage; OutgoingMessage.prototype.setTimeout = function(msecs, callback) { if (callback) this.on('timeout', callback); + if (!this.socket) { this.once('socket', function(socket) { socket.setTimeout(msecs); @@ -133,32 +134,36 @@ OutgoingMessage.prototype._writeRaw = function(data, encoding, callback) { return true; } - if (this.connection && - this.connection._httpMessage === this && - this.connection.writable && - !this.connection.destroyed) { + var connection = this.connection; + if (connection && + connection._httpMessage === this && + connection.writable && + !connection.destroyed) { // There might be pending data in the this.output buffer. - while (this.output.length) { - if (!this.connection.writable) { - this._buffer(data, encoding, callback); - return false; + var outputLength = this.output.length; + if (outputLength > 0) { + var output = this.output; + var outputEncodings = this.outputEncodings; + var outputCallbacks = this.outputCallbacks; + for (var i = 0; i < outputLength; i++) { + connection.write(output[i], outputEncodings[i], + outputCallbacks[i]); } - var c = this.output.shift(); - var e = this.outputEncodings.shift(); - var cb = this.outputCallbacks.shift(); - this.connection.write(c, e, cb); + + this.output = []; + this.outputEncodings = []; + this.outputCallbacks = []; } // Directly write to socket. - return this.connection.write(data, encoding, callback); - } else if (this.connection && this.connection.destroyed) { + return connection.write(data, encoding, callback); + } else if (connection && connection.destroyed) { // The socket was destroyed. If we're still trying to write to it, // then we haven't gotten the 'close' event yet. return false; } else { // buffer, as long as we're not destroyed. - this._buffer(data, encoding, callback); - return false; + return this._buffer(data, encoding, callback); } }; @@ -183,8 +188,6 @@ OutgoingMessage.prototype._storeHeader = function(firstLine, headers) { messageHeader: firstLine }; - var field, value; - if (headers) { var keys = Object.keys(headers); var isArray = Array.isArray(headers); @@ -365,14 +368,16 @@ OutgoingMessage.prototype._renderHeaders = function() { throw new Error('Can\'t render headers after they are sent to the client.'); } - if (!this._headers) return {}; + var headersMap = this._headers; + if (!headersMap) return {}; var headers = {}; - var keys = Object.keys(this._headers); + var keys = Object.keys(headersMap); + var headerNames = this._headerNames; for (var i = 0, l = keys.length; i < l; i++) { var key = keys[i]; - headers[this._headerNames[key]] = this._headers[key]; + headers[headerNames[key]] = headersMap[key]; } return headers; }; @@ -571,13 +576,24 @@ OutgoingMessage.prototype._finish = function() { // This function, outgoingFlush(), is called by both the Server and Client // to attempt to flush any pending messages out to the socket. OutgoingMessage.prototype._flush = function() { - if (this.socket && this.socket.writable) { - var ret; - while (this.output.length) { - var data = this.output.shift(); - var encoding = this.outputEncodings.shift(); - var cb = this.outputCallbacks.shift(); - ret = this.socket.write(data, encoding, cb); + var socket = this.socket; + var outputLength, ret; + + if (socket && socket.writable) { + // There might be remaining data in this.output; write it out + outputLength = this.output.length; + if (outputLength > 0) { + var output = this.output; + var outputEncodings = this.outputEncodings; + var outputCallbacks = this.outputCallbacks; + for (var i = 0; i < outputLength; i++) { + ret = socket.write(output[i], outputEncodings[i], + outputCallbacks[i]); + } + + this.output = []; + this.outputEncodings = []; + this.outputCallbacks = []; } if (this.finished) {