vue 缓存编译结果的源码中, 用template作为cache的key,用户写的template那么长,这合适吗

dianwei
  • 154
// check cache
const key = options.delimiters
  ? String(options.delimiters) + template
  : template
if (cache[key]) {
  return cache[key]
}

如题,template即为要编译的模板。用这么长的字符串作为对象的属性, 性能会不会很糟糕,为什么这么做?

回复
阅读 1.4k
2 个回答
然后去远足
  • 33.5k
✓ 已被采纳

好问题。

所谓性能问题,我理解为可能会有两方面:一是大量字符串对象在内存中会不会很快就内存不足,二是长字符串做 Key 对于对象属性寻址有无影响。

其中第一点可以不用担心,JS 里字符串是 Intern 的,V8 中更是对字符串的存储和拼接做了很多优化。

关于第二点我没有找到相关资料,不过我们可以手动测试一下:

// 构造一个短字符串和一个长字符串
let k1 = 'short';
let k2 = 'long'.repeat(65535);
let obj = { [k1]: 0, [k2]: 0 };

// 读写访问对象属性 1000W 次
console.time('short');
for (let i = 0; i < 10000000; i++) {
    obj[k1] += Math.random();
}
console.timeEnd('short');
console.time('long');
for (let i = 0; i < 10000000; i++) {
    obj[k2] += Math.random();
}
console.timeEnd('long');

执行结果如图:

image.png

我们看出,确实有一定影响,在 1000W 次读写测试中长 Key 比短 Key 要慢很多,但并没有数量级上的差异。而我们在编译时也基本不可能有 1000W 次读写,所以这点儿开销是完全可以忍受的

楼上这种测试方法是十分不严谨的,考虑一个程序的性能必须要考虑到他的最极端的情况也就是我们所说的最坏时间复杂度,在你的上面测试程序中时间局部性和空间局部性都是非常高的,而一个局部性很高的程序时很容易做优化的,已经不符合大部分程序的实际运行情况了,事实上随着key的增长计算其hash值所需的时间也就越长这需要的时间是O(N)的,参考我下面的例子

const N = 1e4
const shortKeys = Array.from({length: N}, () => Math.random() + '')
const longKeys = Array.from({length: N}, () => Array.from({length: 1e3}).fill(Math.random() + '').join(''))

const obj1 = {}
const obj2 = {}
let ans1 = 0

console.time('short')
for(const key of shortKeys) obj1[key] = 1

for(const key of shortKeys) ans1 ^= obj1[key]
console.timeEnd('short')

console.time('long')
for(const key of longKeys) obj2[key] = 1

for(const key of longKeys) ans1 ^= obj2[key]
console.timeEnd('long')

image.png
可以看出key越长会严重降低hashtable的命中的速度,当然这只是大部分程序实际使用时的情况,就vue中的用例而言,他的时间局部性也是很好的,再加上对template做一次hash和重新解析一遍template前者是要快很多的,所以vue这种做法是很划算的

撰写回答
你尚未登录,登录后可以
  • 和开发者交流问题的细节
  • 关注并接收问题和回答的更新提醒
  • 参与内容的编辑和改进,让解决方法与时俱进
宣传栏