From 7147884b046f624b0c01d0a95aaa4e779e05b1aa Mon Sep 17 00:00:00 2001 From: streamich Date: Wed, 13 Dec 2023 12:01:27 +0100 Subject: [PATCH 1/7] =?UTF-8?q?perf(json-pack):=20=E2=9A=A1=EF=B8=8F=20spe?= =?UTF-8?q?ed=20up=20command=20writes?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-pack/__bench__/bench.resp.encoding.ts | 9 +++++++++ src/json-pack/resp/RespEncoder.ts | 11 +++++------ src/util/buffers/Writer.ts | 1 + 3 files changed, 15 insertions(+), 6 deletions(-) diff --git a/src/json-pack/__bench__/bench.resp.encoding.ts b/src/json-pack/__bench__/bench.resp.encoding.ts index d7469da2e7..199c2cb5fb 100644 --- a/src/json-pack/__bench__/bench.resp.encoding.ts +++ b/src/json-pack/__bench__/bench.resp.encoding.ts @@ -29,6 +29,15 @@ const benchmark: IBenchmark = { }; }, }, + { + name: 'json-joy/json-pack RespEncoder.encodeCmd()', + setup: () => { + const encoder = new RespEncoder(); + return (data: any) => { + encoder.encodeCmd(data); + }; + }, + }, { name: '@redis/client', setup: () => { diff --git a/src/json-pack/resp/RespEncoder.ts b/src/json-pack/resp/RespEncoder.ts index d37bb7cbdf..16a12a884e 100644 --- a/src/json-pack/resp/RespEncoder.ts +++ b/src/json-pack/resp/RespEncoder.ts @@ -59,12 +59,11 @@ export class RespEncoder Date: Wed, 13 Dec 2023 12:16:55 +0100 Subject: [PATCH 2/7] =?UTF-8?q?chore(util):=20=F0=9F=A4=96=20unrolling=20d?= =?UTF-8?q?oes=20not=20add=20speed?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/util/buffers/Writer.ts | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/src/util/buffers/Writer.ts b/src/util/buffers/Writer.ts index d1ff507c2c..8b96de2361 100644 --- a/src/util/buffers/Writer.ts +++ b/src/util/buffers/Writer.ts @@ -261,10 +261,9 @@ export class Writer implements IWriter, IWriterGrowable { const length = str.length; this.ensureCapacity(length); const uint8 = this.uint8; - let offset = this.x; + let x = this.x; let pos = 0; - // TODO: unroll the loop for short strings - while (pos < length) uint8[offset++] = str.charCodeAt(pos++); - this.x = offset; + while (pos < length) uint8[x++] = str.charCodeAt(pos++); + this.x = x; } } From 56050da25c494a0b66109d032d15755bd242a483 Mon Sep 17 00:00:00 2001 From: streamich Date: Wed, 13 Dec 2023 23:42:10 +0100 Subject: [PATCH 3/7] =?UTF-8?q?fix(json-pack):=20=F0=9F=90=9B=20account=20?= =?UTF-8?q?for=20encoding=20specifier=20in=20verbatim=20string=20decoding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-pack/resp/RespDecoder.ts | 4 +- src/json-pack/resp/RespEncoder.ts | 47 ++++++++++++++----- .../resp/__tests__/RespDecoder.spec.ts | 8 ++++ 3 files changed, 44 insertions(+), 15 deletions(-) diff --git a/src/json-pack/resp/RespDecoder.ts b/src/json-pack/resp/RespDecoder.ts index 0f5f26a114..07ebd8d81b 100644 --- a/src/json-pack/resp/RespDecoder.ts +++ b/src/json-pack/resp/RespDecoder.ts @@ -198,11 +198,11 @@ export class RespDecoder= pow) { + digits++; + pow *= 10; + } + const writer = this.writer; + writer.ensureCapacity(digits); + const uint8 = writer.uint8; + const x = writer.x; + const newX = x + digits; + let i = newX - 1; + while (i >= x) { + const remainder = length % 10; + uint8[i--] = remainder + 48; + length = (length - remainder) / 10; + } + writer.x = newX; + } public encodeCmd(args: unknown[]): Uint8Array { this.writeCmd(args); @@ -213,7 +234,7 @@ export class RespEncoder this.writeAny(value)); } @@ -340,7 +361,7 @@ export class RespEncoder - writer.ascii(length + ''); + this.writeLength(length); writer.u16(RESP.RN); // \r\n for (let i = 0; i < length; i++) this.writeAny(elements[i]); } @@ -375,7 +396,7 @@ export class RespEncoder { for (const [name, value] of stringCases) { test(name, () => assertCodec(value)); } + + describe('verbatim strings', () => { + test('example from docs', () => { + const encoded = '=15\r\ntxt:Some string\r\n'; + const decoded = decode(encoded); + expect(decoded).toBe('Some string'); + }); + }); }); describe('binary', () => { From 0f49b501e1db51035728796d200dc93cd1f3e7c9 Mon Sep 17 00:00:00 2001 From: streamich Date: Wed, 13 Dec 2023 23:43:48 +0100 Subject: [PATCH 4/7] =?UTF-8?q?style(json-pack):=20=F0=9F=92=84=20run=20Pr?= =?UTF-8?q?ettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-pack/resp/RespEncoder.ts | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/json-pack/resp/RespEncoder.ts b/src/json-pack/resp/RespEncoder.ts index 0ed755438a..9b930180b0 100644 --- a/src/json-pack/resp/RespEncoder.ts +++ b/src/json-pack/resp/RespEncoder.ts @@ -50,7 +50,7 @@ export class RespEncoder Date: Thu, 14 Dec 2023 00:06:13 +0100 Subject: [PATCH 5/7] =?UTF-8?q?fix(json-pack):=20=F0=9F=90=9B=20correct=20?= =?UTF-8?q?command=20and=20streaming=20string=20encoding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-pack/resp/RespEncoder.ts | 10 ++++++---- src/json-pack/resp/__tests__/RespEncoder.spec.ts | 9 +++++---- 2 files changed, 11 insertions(+), 8 deletions(-) diff --git a/src/json-pack/resp/RespEncoder.ts b/src/json-pack/resp/RespEncoder.ts index 9b930180b0..a197b18ec3 100644 --- a/src/json-pack/resp/RespEncoder.ts +++ b/src/json-pack/resp/RespEncoder.ts @@ -82,7 +82,7 @@ export class RespEncoder { const encoder = new RespEncoder(); encoder.writeVerbatimStr('txt', ''); const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('=0\r\ntxt:\r\n'); + expect(toStr(encoded)).toBe('=4\r\ntxt:\r\n'); }); test('short string', () => { const encoder = new RespEncoder(); - encoder.writeVerbatimStr('txt', ''); + encoder.writeVerbatimStr('txt', 'asdf'); const encoded = encoder.writer.flush(); - expect(toStr(encoded)).toBe('=0\r\ntxt:\r\n'); + expect(toStr(encoded)).toBe('=8\r\ntxt:asdf\r\n'); }); }); }); @@ -331,7 +332,7 @@ describe('commands', () => { test('can encode Uint8Array', () => { const encoder = new RespEncoder(); - const encoded = encoder.encodeCmd([Buffer.from('SET'), 'foo', 123]); + const encoded = encoder.encodeCmd([bufferToUint8Array(Buffer.from('SET')), 'foo', 123]); expect(toStr(encoded)).toBe('*3\r\n$3\r\nSET\r\n$3\r\nfoo\r\n$3\r\n123\r\n'); }); }); From 2f9c337dd2d5a2df6a9ade124f844175db1a12d5 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 14 Dec 2023 00:18:58 +0100 Subject: [PATCH 6/7] =?UTF-8?q?perf(json-pack):=20=E2=9A=A1=EF=B8=8F=20imp?= =?UTF-8?q?rove=20length=20encoding?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-pack/resp/RespEncoder.ts | 22 ++++++++++++++++++---- 1 file changed, 18 insertions(+), 4 deletions(-) diff --git a/src/json-pack/resp/RespEncoder.ts b/src/json-pack/resp/RespEncoder.ts index a197b18ec3..03bd2f15aa 100644 --- a/src/json-pack/resp/RespEncoder.ts +++ b/src/json-pack/resp/RespEncoder.ts @@ -53,10 +53,24 @@ export class RespEncoder= pow) { - digits++; - pow *= 10; + if (length < 10000) { + if (length < 100) { + if (length < 10) digits = 1; else digits = 2; + } else { + if (length < 1000) digits = 3; else digits = 4; + } + } else if (length < 100000000) { + if (length < 1000000) { + if (length < 100000) digits = 5; else digits = 6; + } else { + if (length < 10000000) digits = 7; else digits = 8; + } + } else { + let pow = 10; + while (length >= pow) { + digits++; + pow *= 10; + } } const writer = this.writer; writer.ensureCapacity(digits); From 27bdaf5372af313265ece0a685c356cb7e4e64a1 Mon Sep 17 00:00:00 2001 From: streamich Date: Thu, 14 Dec 2023 00:19:31 +0100 Subject: [PATCH 7/7] =?UTF-8?q?style(json-pack):=20=F0=9F=92=84=20run=20Pr?= =?UTF-8?q?ettier?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/json-pack/resp/RespEncoder.ts | 12 ++++++++---- 1 file changed, 8 insertions(+), 4 deletions(-) diff --git a/src/json-pack/resp/RespEncoder.ts b/src/json-pack/resp/RespEncoder.ts index 03bd2f15aa..ffca7b42dc 100644 --- a/src/json-pack/resp/RespEncoder.ts +++ b/src/json-pack/resp/RespEncoder.ts @@ -55,15 +55,19 @@ export class RespEncoder