Skip to content

Commit

Permalink
Performance optimisations
Browse files Browse the repository at this point in the history
Signed-off-by: Thomas Mäder <tmader@redhat.com>
  • Loading branch information
tsmaeder committed Apr 26, 2022
1 parent 2a999ce commit 18ff9e2
Show file tree
Hide file tree
Showing 8 changed files with 243 additions and 68 deletions.
4 changes: 2 additions & 2 deletions src/array-buffer-message-buffer.spec.ts
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ import { ArrayBufferReadBuffer, ArrrayBufferWriteBuffer } from './array-buffer-m

describe('array message buffer tests', () => {
it('basic read write test', () => {
const buffer = new ArrayBuffer(1024);
const buffer = new Uint8Array(1024);
const writer = new ArrrayBufferWriteBuffer(buffer);

writer.writeByte(8);
Expand All @@ -34,7 +34,7 @@ describe('array message buffer tests', () => {

expect(reader.readByte()).equal(8);
expect(reader.readInt()).equal(10000)
expect(reader.readBytes()).deep.equal(new Uint8Array([1, 2, 3, 4]).buffer);
expect(reader.readBytes()).deep.equal(new Uint8Array([1, 2, 3, 4]));
expect(reader.readString()).equal('this is a string');
expect(reader.readString()).equal('another string');
})
Expand Down
86 changes: 60 additions & 26 deletions src/array-buffer-message-buffer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -17,11 +17,11 @@ import { Emitter, Event } from './env/event';
import { ReadBuffer, WriteBuffer } from './message-buffer';

export class ArrrayBufferWriteBuffer implements WriteBuffer {
constructor(private buffer: ArrayBuffer = new ArrayBuffer(1024), private offset: number = 0) {
}
private encoder = new TextEncoder();
private msg: DataView;

private get msg() {
return new DataView(this.buffer);
constructor(private buffer: Uint8Array = new Uint8Array(1024 * 1024), private offset: number = 0) {
this.msg = new DataView(buffer.buffer);
}

ensureCapacity(value: number): WriteBuffer {
Expand All @@ -30,19 +30,37 @@ export class ArrrayBufferWriteBuffer implements WriteBuffer {
newLength *= 2;
}
if (newLength !== this.buffer.byteLength) {
const newBuffer = new ArrayBuffer(newLength);
new Uint8Array(newBuffer).set(new Uint8Array(this.buffer));
console.log("reallocating to " + newLength);
const newBuffer = new Uint8Array(newLength);
newBuffer.set(this.buffer);
this.buffer = newBuffer;
this.msg = new DataView(this.buffer.buffer);
}
return this;
}

writeLength(length: number): WriteBuffer {
if (length < 127) {
this.writeByte(length);
} else {
this.writeByte(128 + (length & 127));
this.writeLength(length >> 7);
}
return this;
}

writeByte(value: number): WriteBuffer {
this.ensureCapacity(1);
this.msg.setUint8(this.offset++, value);
this.buffer[this.offset++] = value;
return this;
}

writeNumber(value: number): WriteBuffer {
this.ensureCapacity(8);
this.msg.setFloat64(this.offset, value);
this.offset += 8;
return this;
}

writeInt(value: number): WriteBuffer {
this.ensureCapacity(4);
Expand All @@ -52,19 +70,21 @@ export class ArrrayBufferWriteBuffer implements WriteBuffer {
}

writeString(value: string): WriteBuffer {
const encoded = this.encodeString(value);
this.writeBytes(encoded);
this.ensureCapacity(4 * value.length);
const result = this.encoder.encodeInto(value, this.buffer.subarray(this.offset + 4));
this.msg.setUint32(this.offset, result.written!);
this.offset += 4 + result.written!;
return this;
}

private encodeString(value: string): Uint8Array {
return new TextEncoder().encode(value);
encodeString(value: string): Uint8Array {
return this.encoder.encode(value);
}

writeBytes(value: ArrayBuffer): WriteBuffer {
this.ensureCapacity(value.byteLength + 4);
this.writeInt(value.byteLength);
new Uint8Array(this.buffer).set(new Uint8Array(value), this.offset);
writeBytes(value: Uint8Array): WriteBuffer {
this.writeLength(value.byteLength);
this.ensureCapacity(value.length);
this.buffer.set(value, this.offset);
this.offset += value.byteLength;
return this;
}
Expand All @@ -78,34 +98,49 @@ export class ArrrayBufferWriteBuffer implements WriteBuffer {
this.onCommitEmitter.fire(this.getCurrentContents());
}

getCurrentContents(): ArrayBuffer {
getCurrentContents(): Uint8Array {
return this.buffer.slice(0, this.offset);
}
}

export class ArrayBufferReadBuffer implements ReadBuffer {
private offset: number = 0;
private msg;

constructor(private readonly buffer: ArrayBuffer) {
}

private get msg(): DataView {
return new DataView(this.buffer);
constructor(private readonly buffer: Uint8Array) {
this.msg = new DataView(buffer.buffer);
}

readByte(): number {
return this.msg.getUint8(this.offset++);
}

readLength(): number {
let shift = 0;
let byte = this.readByte();
let value = (byte & 127) << shift;
while (byte > 127) {
shift += 7;
byte = this.readByte();
value = value + ((byte & 127) << shift);
}
return value;
}

readNumber(): number {
const result = this.msg.getFloat64(this.offset);
this.offset += 8;
return result;
}

readInt(): number {
const result = this.msg.getInt32(this.offset);
this.offset += 4;
return result;
}

readString(): string {
const len = this.msg.getUint32(this.offset);
this.offset += 4;
const len = this.readInt();
const result = this.decodeString(this.buffer.slice(this.offset, this.offset + len));
this.offset += len;
return result;
Expand All @@ -115,9 +150,8 @@ export class ArrayBufferReadBuffer implements ReadBuffer {
return new TextDecoder().decode(buf);
}

readBytes(): ArrayBuffer {
const length = this.msg.getUint32(this.offset);
this.offset += 4;
readBytes(): Uint8Array {
const length = this.readLength();
const result = this.buffer.slice(this.offset, this.offset + length);
this.offset += length;
return result;
Expand Down
4 changes: 2 additions & 2 deletions src/channel.ts
Original file line number Diff line number Diff line change
Expand Up @@ -201,14 +201,14 @@ export class ChannelPipe {
readonly left: ForwardingChannel = new ForwardingChannel(() => this.right.onCloseEmitter.fire(), () => {
const leftWrite = new ArrrayBufferWriteBuffer();
leftWrite.onCommit(buffer => {
this.right.onMessageEmitter.fire(new ArrayBufferReadBuffer(buffer));
this.right.onMessageEmitter.fire(new ArrayBufferReadBuffer(new Uint8Array(buffer)));
});
return leftWrite;
});
readonly right: ForwardingChannel = new ForwardingChannel(() => this.left.onCloseEmitter.fire(), () => {
const rightWrite = new ArrrayBufferWriteBuffer();
rightWrite.onCommit(buffer => {
this.left.onMessageEmitter.fire(new ArrayBufferReadBuffer(buffer));
this.left.onMessageEmitter.fire(new ArrayBufferReadBuffer(new Uint8Array(buffer)));
})
return rightWrite;
});
Expand Down
136 changes: 110 additions & 26 deletions src/experiments.ts
Original file line number Diff line number Diff line change
Expand Up @@ -13,44 +13,128 @@
*
* SPDX-License-Identifier: EPL-2.0 OR GPL-2.0 WITH Classpath-exception-2.0
********************************************************************************/
import { ChannelPipe } from './channel';
import { RpcHandler, RpcProxyHandler } from './rpc-proxy';
import * as fs from 'fs';
import { MessageDecoder, MessageEncoder } from './message-encoder';
import { ArrayBufferReadBuffer, ArrrayBufferWriteBuffer } from './array-buffer-message-buffer';

/**
* This file is for fiddling around and testing. Not production code.
*/

const pipe = new ChannelPipe();

interface ReadFile {
read(path: string): Promise<ArrayBuffer>;
//onst toEncode = ['string1', 'string2'];

// Encode with stringify
//const stringified = JSON.stringify(toEncode);
//const encoded1 = Buffer.from(stringified);
//console.log(encoded1.byteLength);

// then encoded1.byteLength= 21

// Encode with MessageEncoder
const encoder = new MessageEncoder();
//const writer = new ArrrayBufferWriteBuffer();
//encoder.writeTypedValue(writer, toEncode);
//const encoded2 = writer.getCurrentContents();
//console.log(encoded2.byteLength);
// then encoded2.byteLength= 42
// so factor 2 in this case

const test1 = {
'curve': 'yes',
'successful': false,
'does': [
[
'tool',
'strange',
'declared',
false,
'if',
false,
false,
true,
true,
196639994
],
-1697924638.043861,
1921422646,
'hide',
false,
true,
true,
-400170969,
550424783,
-2118374202.4598904
],
'fish': 664495385.6069336,
'eat': -1205575089,
'boat': 1495629676,
'arm': 'nation',
'height': false,
'underline': 'have',
'satellites': -20686813.87966633
};

const test2: unknown[] = [];
for (let index = 0; index < 100; index++) {
test2.push(test1);
}

class Server implements ReadFile {
read(path: string): Promise<ArrayBuffer> {
const bytes = fs.readFileSync(path);
const result = new ArrayBuffer(bytes.byteLength);
bytes.copy(new Uint8Array(result));
return Promise.resolve(result);
}
const test3: string[] = [];
for (let index = 0; index < 1000; index++) {
test3.push(`${index}`);

}

const handler = new RpcHandler(new Server());
handler.onChannelOpen(pipe.right);

const proxyHandler = new RpcProxyHandler<ReadFile>();
const proxy: ReadFile = new Proxy(Object.create(null), proxyHandler);
proxyHandler.onChannelOpen(pipe.left);
time(10000, () => writeTypedValue(test2), 'New encoding of object');
time(10000, () => writeTypedValue(JSON.stringify(test2)), 'Stringify of object');

function writeTypedValue(object: unknown) {
const writer = new ArrrayBufferWriteBuffer();
encoder.writeTypedValue(writer, object);
writer.getCurrentContents();
}

function time(times: number, payload: () => void, name: string) {
const start2 = Date.now();
for (let index = 0; index < times; index++) {
payload();
// test(test1);
// test(test2);
// test(test3);
}
const end2 = Date.now();
console.log(`${name} took ${end2 - start2} ms.`);
}

export function test(object: unknown): void {
// console.log('Start test');
const encoder = new MessageEncoder();
const decoder = new MessageDecoder();
// const string = fs.readFileSync(process.argv[2], 'utf8');
// const object = JSON.parse(string);

const t0 = new Date().getTime();
//const start1 = Date.now();
const result = Buffer.from(JSON.stringify(object));
//const end1 = Date.now();
//console.log(`Stringify encoding of object took ${end1 - start1} ms. Final byte length: ${result.byteLength}`);

proxy.read(process.argv[2]).then(value => {
const t1 = new Date().getTime();
console.log(`read file of length: ${value.byteLength} in ${t1 - t0}ms`);
console.log(value.slice(0, 20));
}).catch(e => {
console.log(e);
});
const writer = new ArrrayBufferWriteBuffer();
//const start2 = Date.now();
encoder.writeTypedValue(writer, object);
const result2 = writer.getCurrentContents();
//const end2 = Date.now();
//console.log(`New encoding of object took ${end2 - start2} ms. Final byte length: ${result2.byteLength}`);

//const start3 = Date.now();
JSON.parse(result.toString());
//const end3 = Date.now();
//console.log(`Stringify Reading took ${end3 - start3} ms for`);

const reader = new ArrayBufferReadBuffer(result2);
//const start4 = Date.now();
decoder.readTypedValue(reader);
//const end4 = Date.now();
//console.log(`New Reading took ${end4 - start4} ms for`);
// console.log();
}
Loading

0 comments on commit 18ff9e2

Please sign in to comment.