diff --git a/benchmark/http/client-request-body.js b/benchmark/http/client-request-body.js new file mode 100644 index 0000000000..7a3468a670 --- /dev/null +++ b/benchmark/http/client-request-body.js @@ -0,0 +1,69 @@ +// Measure the time it takes for the HTTP client to send a request body. + +var common = require('../common.js'); +var http = require('http'); + +var bench = common.createBenchmark(main, { + dur: [5], + type: ['asc', 'utf', 'buf'], + bytes: [32, 256, 1024], + method: ['write', 'end '] // two spaces added to line up each row +}); + +function main(conf) { + var dur = +conf.dur; + var len = +conf.bytes; + + var encoding; + var chunk; + switch (conf.type) { + case 'buf': + chunk = new Buffer(len); + chunk.fill('x'); + break; + case 'utf': + encoding = 'utf8'; + chunk = new Array(len / 2 + 1).join('ΓΌ'); + break; + case 'asc': + chunk = new Array(len + 1).join('a'); + break; + } + + var nreqs = 0; + var options = { + headers: { 'Connection': 'keep-alive', 'Transfer-Encoding': 'chunked' }, + agent: new http.Agent({ maxSockets: 1 }), + host: '127.0.0.1', + port: common.PORT, + path: '/', + method: 'POST' + }; + + var server = http.createServer(function(req, res) { + res.end(); + }); + server.listen(options.port, options.host, function() { + setTimeout(done, dur * 1000); + bench.start(); + pummel(); + }); + + function pummel() { + var req = http.request(options, function(res) { + nreqs++; + pummel(); // Line up next request. + res.resume(); + }); + if (conf.method === 'write') { + req.write(chunk, encoding); + req.end(); + } else { + req.end(chunk, encoding); + } + } + + function done() { + bench.end(nreqs); + } +} diff --git a/lib/http.js b/lib/http.js index 4a4fe94f3e..bcde516dae 100644 --- a/lib/http.js +++ b/lib/http.js @@ -782,12 +782,28 @@ OutgoingMessage.prototype.write = function(chunk, encoding) { if (chunk.length === 0) return false; + // TODO(bnoordhuis) Temporary optimization hack, remove in v0.11. We only + // want to convert the buffer when we're sending: + // + // a) Transfer-Encoding chunks, because it lets us pack the chunk header + // and the chunk into a single write(), or + // + // b) the first chunk of a fixed-length request, because it lets us pack + // the request headers and the chunk into a single write(). + // + // Converting to strings is expensive, CPU-wise, but reducing the number + // of write() calls more than makes up for that because we're dramatically + // reducing the number of TCP roundtrips. + if (chunk instanceof Buffer && (this.chunkedEncoding || !this._headerSent)) { + chunk = chunk.toString('binary'); + encoding = 'binary'; + } + var len, ret; if (this.chunkedEncoding) { if (typeof(chunk) === 'string' && encoding !== 'hex' && - encoding !== 'base64' && - encoding !== 'binary') { + encoding !== 'base64') { len = Buffer.byteLength(chunk, encoding); chunk = len.toString(16) + CRLF + chunk + CRLF; ret = this._send(chunk, encoding);