Skip to content

Commit 304ca09

Browse files
author
Ruben Bridgewater
committed
Improve multiple chunks being concated by using a bufferPool
It is going to increase in size if required
1 parent 0471209 commit 304ca09

File tree

3 files changed

+109
-6
lines changed

3 files changed

+109
-6
lines changed

benchmark/index.js

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -58,6 +58,8 @@ for (i = 0; i < bigArraySize; i++) {
5858
}
5959

6060
var bigArrayBuffer = new Buffer(bigArray)
61+
var chunkedStringPart1 = new Buffer('+foobar')
62+
var chunkedStringPart2 = new Buffer('bazEND\r\n')
6163

6264
var parserOld = new ParserOLD({
6365
returnReply: checkReply,
@@ -105,6 +107,23 @@ suite.add('NEW CODE: multiple chunks in a bulk string', function () {
105107
parser.execute(endBuffer)
106108
})
107109

110+
// CHUNKED STRINGS
111+
112+
suite.add('\nOLD CODE: multiple chunks in a string', function () {
113+
parserOld.execute(chunkedStringPart1)
114+
parserOld.execute(chunkedStringPart2)
115+
})
116+
117+
suite.add('HIREDIS: multiple chunks in a string', function () {
118+
parserHiRedis.execute(chunkedStringPart1)
119+
parserHiRedis.execute(chunkedStringPart2)
120+
})
121+
122+
suite.add('NEW CODE: multiple chunks in a string', function () {
123+
parser.execute(chunkedStringPart1)
124+
parser.execute(chunkedStringPart2)
125+
})
126+
108127
// BIG BULK STRING
109128

110129
suite.add('\nOLD CODE: 4mb bulk string', function () {

lib/parser.js

Lines changed: 22 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
'use strict'
22

33
var ReplyError = require('./replyError')
4+
// TODO: Consider shrinking the bufferPool if it's not used a lot by using interval check
5+
var bufferPool = new Buffer(64 * 1024)
46

57
/**
68
* Used for lengths and numbers only, faster perf on arrays / bulks
@@ -261,6 +263,19 @@ function JavascriptRedisParser (options) {
261263
this.bufferCache = []
262264
}
263265

266+
function concat (parser, length) {
267+
var list = parser.bufferCache
268+
var pos = 0
269+
if (bufferPool.length < length) {
270+
bufferPool = new Buffer(length)
271+
}
272+
for (var i = 0; i < list.length; i++) {
273+
list[i].copy(bufferPool, pos)
274+
pos += list[i].length
275+
}
276+
return bufferPool.slice(parser.offset, length)
277+
}
278+
264279
/**
265280
* Parse the redis buffer
266281
* @param buffer
@@ -273,16 +288,17 @@ JavascriptRedisParser.prototype.execute = function (buffer) {
273288
var oldLength = this.buffer.length
274289
var remainingLength = oldLength - this.offset
275290
var newLength = remainingLength + buffer.length
276-
var newBuffer = new Buffer(newLength)
291+
// ~ 5% speed increase over using new Buffer(length) all the time
292+
if (bufferPool.length < newLength) { // We can't rely on the chunk size
293+
bufferPool = new Buffer(newLength)
294+
}
295+
var newBuffer = bufferPool
277296
this.buffer.copy(newBuffer, 0, this.offset, oldLength)
278297
buffer.copy(newBuffer, remainingLength, 0, buffer.length)
279-
this.buffer = newBuffer
298+
this.buffer = newBuffer.slice(0, newLength)
280299
} else if (this.totalChunkSize + buffer.length >= this.bigStrSize) {
281300
this.bufferCache.push(buffer)
282-
if (this.offset !== 0) {
283-
this.bufferCache[0] = this.bufferCache[0].slice(this.offset)
284-
}
285-
this.buffer = Buffer.concat(this.bufferCache, this.totalChunkSize + buffer.length - this.offset)
301+
this.buffer = concat(this, this.totalChunkSize + buffer.length)
286302
this.bigStrSize = 0
287303
this.totalChunkSize = 0
288304
this.bufferCache = []

test/parsers.spec.js

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -449,6 +449,74 @@ describe('parsers', function () {
449449
parser.execute(new Buffer(':' + number + '\r\n'))
450450
assert.strictEqual(replyCount, 2)
451451
})
452+
453+
it('handle big data', function () {
454+
if (Parser.name === 'HiredisReplyParser') {
455+
return this.skip()
456+
}
457+
var lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, ' +
458+
'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' +
459+
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ' +
460+
'ut aliquip ex ea commodo consequat. Duis aute irure dolor in' // 256 chars
461+
var bigStringArray = (new Array(Math.pow(2, 16) / lorem.length).join(lorem + ' ')).split(' ') // Math.pow(2, 16) chars long
462+
var startBigBuffer = new Buffer('$' + (4 * 1024 * 1024) + '\r\n')
463+
var chunks = new Array(64)
464+
for (var i = 0; i < 64; i++) {
465+
chunks[i] = new Buffer(bigStringArray.join(' ') + '.') // Math.pow(2, 16) chars long
466+
}
467+
var replyCount = 0
468+
function checkReply (reply) {
469+
assert.strictEqual(reply.length, 4 * 1024 * 1024)
470+
replyCount++
471+
}
472+
var parser = new Parser({
473+
returnReply: checkReply,
474+
returnError: returnError,
475+
returnFatalError: returnFatalError
476+
})
477+
parser.execute(startBigBuffer)
478+
for (i = 0; i < 64; i++) {
479+
assert.strictEqual(parser.bufferCache.length, i + 1)
480+
parser.execute(chunks[i])
481+
}
482+
assert.strictEqual(replyCount, 0)
483+
parser.execute(new Buffer('\r\n'))
484+
assert.strictEqual(replyCount, 1)
485+
})
486+
487+
it('handle big data 2', function () {
488+
if (Parser.name === 'HiredisReplyParser') {
489+
return this.skip()
490+
}
491+
var lorem = 'Lorem ipsum dolor sit amet, consectetur adipiscing elit, ' +
492+
'sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. ' +
493+
'Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ' +
494+
'ut aliquip ex ea commodo consequat. Duis aute irure dolor in' // 256 chars
495+
var bigStringArray = (new Array(Math.pow(2, 16) / lorem.length).join(lorem + ' ')).split(' ') // Math.pow(2, 16) chars long
496+
var startBigBuffer = new Buffer('\r\n$' + (4 * 1024 * 1024) + '\r\n')
497+
var chunks = new Array(64)
498+
for (var i = 0; i < 64; i++) {
499+
chunks[i] = new Buffer(bigStringArray.join(' ') + '.') // Math.pow(2, 16) chars long
500+
}
501+
var replyCount = 0
502+
function checkReply (reply) {
503+
replyCount++
504+
}
505+
var parser = new Parser({
506+
returnReply: checkReply,
507+
returnError: returnError,
508+
returnFatalError: returnFatalError
509+
})
510+
parser.execute(new Buffer('+test'))
511+
parser.execute(startBigBuffer)
512+
for (i = 0; i < 64; i++) {
513+
assert.strictEqual(parser.bufferCache.length, i + 1)
514+
parser.execute(chunks[i])
515+
}
516+
assert.strictEqual(replyCount, 1)
517+
parser.execute(new Buffer('\r\n'))
518+
assert.strictEqual(replyCount, 2)
519+
})
452520
})
453521
})
454522
})

0 commit comments

Comments
 (0)