在 JavaScript 中将 GUID 从字节数组反序列化为字符串的快速且内存高效的方法

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

提问人:Jochen Kühner 提问时间:11/8/2023 最后编辑:Jason AllerJochen Kühner 更新时间:11/8/2023 访问量:62

问:

目前我使用了以下代码:

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;
}

但我认为它的效率非常低,并且由于创建的所有字符串而产生许多 gc 开销。问题是,我每秒收到一千条消息,我需要将其转换为字符串。(它在浏览器中,而不是在节点应用程序中)

this.array 是一个 Uint8Array

JavaScript 反序列化 uint8array

评论

5赞 T.J. Crowder 11/8/2023
你有什么直接证据表明这些临时字符串实际上是一个问题吗?
0赞 Jochen Kühner 11/8/2023
它们不是直接的问题,但我正在开发一个通用的反序列化库,它应该尽可能快和内存高效

答:

2赞 T.J. Crowder 11/8/2023 #1

一般来说,如果没有具体的可衡量问题,就很难提出可以解决该问题的解决方案。如果没有测量(理想情况下是真实世界的测量而不是合成基准),你就不知道你正在做的事情是改善还是让事情变得更糟。

也就是说,在此用例中跳出的一件事是,您正在为相同的 256 个输入值重复重新创建填充的十六进制字符串。鉴于您看到的卷,最好一次性构建这些字符串并重用它们。这会消耗少量静态内存来存储 256 个字符串,但避免了对每个输入 GUID 的重复和操作。toStringpadStart

您可以做的第二件事是将重复的属性访问缓存到本地 .const

例:

// 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甚至可能只是一个简单的数组文字,尽管一次性创建循环应该不是问题:

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

但同样,对这两者都持保留态度,并进行分析/基准测试,看看是否有帮助。

评论

0赞 Jochen Kühner 11/8/2023
喜欢这个主意!我将创建一个基准测试
0赞 Jochen Kühner 11/8/2023
看起来它快了近 5 倍。jsbench.me/c6loosw7fx/1
0赞 T.J. Crowder 11/8/2023
太好了,很高兴有帮助!我认为查找可能特别有帮助。:-)(事实上,这似乎是一个大问题,尽管这件事也有帮助。[jsbench.me 不允许我保存 2048 个字符>设置块。 jsben.ch 没有那么严格,但显示的结果与我无法保存的 jsbench.me 测试相似。 🤷 ♂️希望您在实际使用中看到类似的改进。const