如何在Javascript中用Int8、Int16存储大型数据结构优化性能
实现方案
核心思路
你需要的三个字段总长度刚好为32位(8bit id + 8bit harm + 16bit val),可以直接打包进单个Uint32类型的存储单元,使用连续内存的Uint32Array存储所有数据,相比普通JS对象的哈希表存储,随机访问和批量遍历的性能都会有明显提升,和你之前优化Float32音频数组的效果一致。
位布局定义
我们统一约定32位空间的位分配规则(你也可以根据自己的使用习惯调整偏移量):
- 最高8位(位2431):存储固定的`id`值,取值范围0127,完全覆盖Int8需求
- 中间8位(位1623):存储固定的`harm`值,取值范围0127,完全覆盖Int8需求
- 最低16位(位015):存储可变的`val`值,取值范围02047,远小于Int16的最大值32767,完全够用
具体实现步骤
1. 预定义常量与存储结构
// 位掩码与偏移量常量,避免魔法数 const ID_OFFSET = 24; const HARM_OFFSET = 16; const VAL_MASK = 0xFFFF; const META_MASK = 0xFFFF0000; // id和harm取值都是0~127,所有可能的组合总数为128*128=16384,仅占用64KB内存,空间完全足够 const DATA_TOTAL_COUNT = 128 * 128; const dataBuffer = new Uint32Array(DATA_TOTAL_COUNT);
2. 构建属性名元信息映射
因为你原有代码是通过属性名(比如EvenHarmonics)访问数据,所以需要先把所有属性对应的固定id、harm值预存在映射表里:
const KeyMeta = { MIDISource: {id: 0, harm: 0}, MIDIChannel: {id: 1, harm: 1}, MIDISplit: {id: 2, harm: 1}, MultiTouch: {id: 3, harm: 1}, // 把剩下的5000个属性的元信息全部按上面的格式补全 };
3. 初始化填充数据
把原有Data对象的所有数据打包写入dataBuffer:
Object.entries(KeyMeta).forEach(([key, {id, harm}]) => { const originalVal = Data[key].val; const index = id * 128 + harm; // 按位规则打包成32位整数写入数组 dataBuffer[index] = (id << ID_OFFSET) | (harm << HARM_OFFSET) | (originalVal & VAL_MASK); });
4. 兼容原有访问方式(可选)
如果你不想修改原有代码的访问语法,可以用Proxy封装一层,完全兼容你原来的访问写法:
const Data = new Proxy({}, { get(target, prop) { const meta = KeyMeta[prop]; if (!meta) return undefined; const index = meta.id * 128 + meta.harm; const packed = dataBuffer[index]; return { id: (packed >> ID_OFFSET) & 0xFF, harm: (packed >> HARM_OFFSET) & 0xFF, get val() { return dataBuffer[index] & VAL_MASK; }, set val(newVal) { // 仅修改val对应的低16位,保留固定的id、harm位 dataBuffer[index] = (dataBuffer[index] & META_MASK) | (newVal & VAL_MASK); } } } });
封装完成后,你原有代码的Data.EvenHarmonics.val读取和赋值操作都可以直接正常运行,不需要做任何修改。
极端性能优化场景
如果你的使用场景对性能要求极高,可以跳过Proxy封装,直接通过下标访问数据,避免Proxy的代理开销:
// 读取val const getVal = (id, harm) => dataBuffer[id * 128 + harm] & VAL_MASK; // 写入val const setVal = (id, harm, newVal) => { const idx = id * 128 + harm; dataBuffer[idx] = (dataBuffer[idx] & META_MASK) | (newVal & VAL_MASK); };
内容的提问来源于stack exchange,提问作者Mike Bryant
相关产品推荐
相关产品推荐

