From 86dc983467e9403995c63f2d4b12d0287e504e7b Mon Sep 17 00:00:00 2001 From: Pau Freixes Date: Mon, 17 Jan 2022 11:31:25 +0100 Subject: [PATCH 1/6] Preallocate buffer size when reading data MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit When small chunks are received from a large payload, i.e > 10MBs, performance gets impacted because due to the buffer allocation, for payloads of 10MBs with chunks of 2KBs it can take ~  20 seconds. We add an exponencial growth of the buffer providing enough room or subsequent chunks until a maximum threshold of 1MB. With that change we can see a x50 improvement with the trade off of having to allocate a little extra space, worst case scenario 1MB. --- lib/eventsource.js | 33 ++++++++++++++++++++++++++------- 1 file changed, 26 insertions(+), 7 deletions(-) diff --git a/lib/eventsource.js b/lib/eventsource.js index a81381d..3e0a55e 100644 --- a/lib/eventsource.js +++ b/lib/eventsource.js @@ -15,6 +15,8 @@ var colon = 58 var space = 32 var lineFeed = 10 var carriageReturn = 13 +// 1MB +var maxBufferAheadAllocation = 1024*1024 function hasBom (buf) { return bom.every(function (charCode, index) { @@ -179,19 +181,35 @@ function EventSource (url, eventSourceInitDict) { // text/event-stream parser adapted from webkit's // Source/WebCore/page/EventSource.cpp - var isFirst = true var buf var startingPos = 0 var startingFieldLength = -1 + var newBufferSize = 0 + var bytesUsed = 0 + res.on('data', function (chunk) { - buf = buf ? Buffer.concat([buf, chunk]) : chunk - if (isFirst && hasBom(buf)) { - buf = buf.slice(bom.length) + if (!buf) { + buf = chunk + if (hasBom(buf)) { + buf = buf.slice(bom.length) + } + } else { + if (chunk.length > buf.length - bytesUsed) { + newBufferSize = (buf.length * 2) + chunk.length + if (newBufferSize > maxBufferAheadAllocation) { + newBufferSize = buf.length + chunk.length + maxBufferAheadAllocation + } + newBuffer = new Buffer(newBufferSize) + buf.copy(newBuffer, 0, 0, bytesUsed) + buf = newBuffer + } + chunk.copy(buf, bytesUsed) } - isFirst = false + bytesUsed += chunk.length + var pos = 0 - var length = buf.length + var length = bytesUsed while (pos < length) { if (discardTrailingNewline) { @@ -236,7 +254,8 @@ function EventSource (url, eventSourceInitDict) { if (pos === length) { buf = void 0 } else if (pos > 0) { - buf = buf.slice(pos) + buf = buf.slice(pos, bytesUsed) + bytesUsed = buf.length } }) }) From a9edd801173697d6817586940e62db3e00832219 Mon Sep 17 00:00:00 2001 From: Pau Freixes Date: Wed, 19 Jan 2022 09:52:18 +0100 Subject: [PATCH 2/6] Change the threshold for 256KB Beyond that number there is no gain in the performance --- lib/eventsource.js | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/lib/eventsource.js b/lib/eventsource.js index 3e0a55e..3545aa4 100644 --- a/lib/eventsource.js +++ b/lib/eventsource.js @@ -15,8 +15,7 @@ var colon = 58 var space = 32 var lineFeed = 10 var carriageReturn = 13 -// 1MB -var maxBufferAheadAllocation = 1024*1024 +var maxBufferAheadAllocation = 1024*256 function hasBom (buf) { return bom.every(function (charCode, index) { From 99714d52511ff48bccdbd9d51bd57d3e0d598a57 Mon Sep 17 00:00:00 2001 From: Pau Freixes Date: Thu, 20 Jan 2022 09:57:56 +0100 Subject: [PATCH 3/6] Add description about 256KB rationale --- lib/eventsource.js | 1 + 1 file changed, 1 insertion(+) diff --git a/lib/eventsource.js b/lib/eventsource.js index 3545aa4..968b7e3 100644 --- a/lib/eventsource.js +++ b/lib/eventsource.js @@ -15,6 +15,7 @@ var colon = 58 var space = 32 var lineFeed = 10 var carriageReturn = 13 +// Beyond 256KB we could not observe any gain in performance var maxBufferAheadAllocation = 1024*256 function hasBom (buf) { From dbd7d13ed36727cd45626431a967a94c429ae0d3 Mon Sep 17 00:00:00 2001 From: Pau Freixes Date: Thu, 20 Jan 2022 22:16:58 +0100 Subject: [PATCH 4/6] Fix tests and use Buffer.alloc --- lib/eventsource.js | 10 ++++++---- 1 file changed, 6 insertions(+), 4 deletions(-) diff --git a/lib/eventsource.js b/lib/eventsource.js index 968b7e3..686c0be 100644 --- a/lib/eventsource.js +++ b/lib/eventsource.js @@ -16,7 +16,7 @@ var space = 32 var lineFeed = 10 var carriageReturn = 13 // Beyond 256KB we could not observe any gain in performance -var maxBufferAheadAllocation = 1024*256 +var maxBufferAheadAllocation = 1024 * 256 function hasBom (buf) { return bom.every(function (charCode, index) { @@ -182,6 +182,7 @@ function EventSource (url, eventSourceInitDict) { // text/event-stream parser adapted from webkit's // Source/WebCore/page/EventSource.cpp var buf + var newBuffer var startingPos = 0 var startingFieldLength = -1 var newBufferSize = 0 @@ -193,21 +194,21 @@ function EventSource (url, eventSourceInitDict) { if (hasBom(buf)) { buf = buf.slice(bom.length) } + bytesUsed = buf.length } else { if (chunk.length > buf.length - bytesUsed) { newBufferSize = (buf.length * 2) + chunk.length if (newBufferSize > maxBufferAheadAllocation) { newBufferSize = buf.length + chunk.length + maxBufferAheadAllocation } - newBuffer = new Buffer(newBufferSize) + newBuffer = Buffer.alloc(newBufferSize) buf.copy(newBuffer, 0, 0, bytesUsed) buf = newBuffer } chunk.copy(buf, bytesUsed) + bytesUsed += chunk.length } - bytesUsed += chunk.length - var pos = 0 var length = bytesUsed @@ -253,6 +254,7 @@ function EventSource (url, eventSourceInitDict) { if (pos === length) { buf = void 0 + bytesUsed = 0 } else if (pos > 0) { buf = buf.slice(pos, bytesUsed) bytesUsed = buf.length From bab41d9e7d802818bc06f8e1d9c070fc30375810 Mon Sep 17 00:00:00 2001 From: Pau Freixes Date: Thu, 20 Jan 2022 22:33:26 +0100 Subject: [PATCH 5/6] Modify test timeout that checks performance of the chunk parser --- test/eventsource_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/eventsource_test.js b/test/eventsource_test.js index b0be4ec..d08f23d 100644 --- a/test/eventsource_test.js +++ b/test/eventsource_test.js @@ -454,7 +454,7 @@ describe('Parser', function () { }) it('parses relatively huge messages efficiently', function (done) { - this.timeout(1000) + this.timeout(30) createServer(function (err, server) { if (err) return done(err) From ef3a4eeaa0e0807a678091a56f155e5c2bcd4c9c Mon Sep 17 00:00:00 2001 From: Pau Freixes Date: Thu, 20 Jan 2022 23:27:55 +0100 Subject: [PATCH 6/6] Less aggressive timeout --- test/eventsource_test.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/test/eventsource_test.js b/test/eventsource_test.js index d08f23d..31f6eea 100644 --- a/test/eventsource_test.js +++ b/test/eventsource_test.js @@ -454,7 +454,7 @@ describe('Parser', function () { }) it('parses relatively huge messages efficiently', function (done) { - this.timeout(30) + this.timeout(200) createServer(function (err, server) { if (err) return done(err)