fast and memory efficient way to deserialize a GUID from a byte array to a string in JavaScript

87 views Asked by At

At the moment I've used this code:

getGuid(): string {
    const guid = this.array[this.offset + 3].toString(16).padStart(2, '0') + this.array[this.offset + 2].toString(16).padStart(2, '0') + this.array[this.offset + 1].toString(16).padStart(2, '0') + this.array[this.offset + 0].toString(16).padStart(2, '0') + "-" +
        this.array[this.offset + 5].toString(16).padStart(2, '0') + this.array[this.offset + 4].toString(16).padStart(2, '0') + "-" +
        this.array[this.offset + 7].toString(16).padStart(2, '0') + this.array[this.offset + 6].toString(16).padStart(2, '0') + "-" +
        this.array[this.offset + 8].toString(16).padStart(2, '0') + this.array[this.offset + 9].toString(16).padStart(2, '0') + "-" +
        this.array[this.offset + 10].toString(16).padStart(2, '0') + this.array[this.offset + 11].toString(16).padStart(2, '0') + this.array[this.offset + 12].toString(16).padStart(2, '0') + this.array[this.offset + 13].toString(16).padStart(2, '0') + this.array[this.offset + 14].toString(16).padStart(2, '0') + this.array[this.offset + 15].toString(16).padStart(2, '0');
    this.offset += 16;
    return guid;
}

but I think it is highly inefficient, and has many gc overhead because of all the strings created. Problem is, I get thousand messages guids per second which I need to convert to strings. (it's in browser not in a node application)

this.array is a Uint8Array

1

There are 1 answers

3
T.J. Crowder On BEST ANSWER

In general, without a specific measurable problem, it's hard to suggest solutions that could solve that problem. Without measurements (ideally real-world measurements rather than synthetic benchmarks), you don't know whether what you're doing improves things or makes them worse.

That said, one thing that jumps out in this use case is that you're recreating padded hex strings for the same 256 input values repeatedly. Given the volumes you're seeing, you might do better to build those strings once and reuse them. That consumes a small bit of static memory to store the 256 strings, but avoids the repeated toString and padStart operations on every input GUID.

A second thing you can do is cache the repeated property accesses to a local const.

Example:

// Build the strings once
const byteStrings = Array.from({ length: 256 }, (_, value) =>
    value.toString(16).padStart(2, "0")
);

// Reuse them
class Something {
    // ...
    getGuid(): string {
        const { array, offset } = this;
        const guid =
            byteStrings[array[offset + 3]] +
            byteStrings[array[offset + 2]] +
            byteStrings[array[offset + 1]] +
            byteStrings[array[offset + 0]] +
            "-" +
            byteStrings[array[offset + 5]] +
            byteStrings[array[offset + 4]] +
            "-" +
            byteStrings[array[offset + 7]] +
            byteStrings[array[offset + 6]] +
            "-" +
            byteStrings[array[offset + 8]] +
            byteStrings[array[offset + 9]] +
            "-" +
            byteStrings[array[offset + 10]] +
            byteStrings[array[offset + 11]] +
            byteStrings[array[offset + 12]] +
            byteStrings[array[offset + 13]] +
            byteStrings[array[offset + 14]] +
            byteStrings[array[offset + 15]];
        this.offset += 16;
        return guid;
    }
}

byteStrings could even just be a simple array literal, though the one-time creation loop shouldn't be an issue:

const byteStrings = [ "00", "01", "02", "03", "04", "05", "06", /*...*/, "ff"];

But again, take both of these with a grain of salt, and profile/benchmark to see whether either helps.