From d62cf59dc152f8df6954ee7ffe9381fc9b524e32 Mon Sep 17 00:00:00 2001 From: isaacs Date: Thu, 14 Mar 2013 07:48:18 -0700 Subject: [PATCH] http: Don't hot-path end() for large buffers The benefits of the hot-path optimization below start to fall off when the buffer size gets up near 128KB, because the cost of the copy is more than the cost of the extra write() call. Switch to the write/end method at that point. Heuristics and magic numbers are awful, but slow http responses are worse. Fix #4975 --- benchmark/http/end-vs-write-end.js | 59 ++++++++++++++++++++++++++++++ lib/http.js | 8 ++++ 2 files changed, 67 insertions(+) create mode 100644 benchmark/http/end-vs-write-end.js diff --git a/benchmark/http/end-vs-write-end.js b/benchmark/http/end-vs-write-end.js new file mode 100644 index 00000000000..06fce6f4686 --- /dev/null +++ b/benchmark/http/end-vs-write-end.js @@ -0,0 +1,59 @@ +// When calling .end(buffer) right away, this triggers a "hot path" +// optimization in http.js, to avoid an extra write call. +// +// However, the overhead of copying a large buffer is higher than +// the overhead of an extra write() call, so the hot path was not +// always as hot as it could be. +// +// Verify that our assumptions are valid. + +var common = require('../common.js'); +var PORT = common.PORT; + +var bench = common.createBenchmark(main, { + type: ['asc', 'utf', 'buf'], + kb: [64, 128, 256, 1024], + c: [100], + method: ['write', 'end '] // two spaces added to line up each row +}); + +function main(conf) { + http = require('http'); + var chunk; + var len = conf.kb * 1024; + 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; + } + + function write(res) { + res.write(chunk); + res.end(); + } + + function end(res) { + res.end(chunk); + } + + var method = conf.method === 'write' ? write : end; + var args = ['-r', 5000, '-t', 8, '-c', conf.c]; + + var server = http.createServer(function(req, res) { + method(res); + }); + + server.listen(common.PORT, function() { + bench.http('/', args, function() { + server.close(); + }); + }); +} diff --git a/lib/http.js b/lib/http.js index 2bceaa01527..dacedd4477f 100644 --- a/lib/http.js +++ b/lib/http.js @@ -855,6 +855,14 @@ OutgoingMessage.prototype.end = function(data, encoding) { this.connection.writable && this.connection._httpMessage === this; + // The benefits of the hot-path optimization below start to fall + // off when the buffer size gets up near 128KB, because the cost + // of the copy is more than the cost of the extra write() call. + // Switch to the write/end method at that point. Heuristics and + // magic numbers are awful, but slow http responses are worse. + if (hot && Buffer.isBuffer(data) && data.length > 120 * 1024) + hot = false; + if (hot) { // Hot path. They're doing // res.writeHead();