仅出于背景考虑,我正在用JavaScript编写bzip2解压缩库,但我不明白是什么导致游程长度解码步骤中的性能下降。 最终,它需要用恒定值填充数组的一部分:
function fill(buf, from, to, val) {
for (var i = from; i < to; ++i)
buf[i] = val;
}
仅使用单一类型的数组调用该函数,而其他参数始终在范围之内。仅此代码就相当于C代码,每秒执行数亿个数组访问,一切都很好。正如预期的那样,涡轮风扇会注入边界检查以确保索引保持在有效范围内。
但是当我将这些绑定检查明确添加到函数中时:
function fill(buf, from, to, val) {
if (from < 0 || to < from || buf.length < to)
throw new Error();
for (var i = from; i < to; ++i)
buf[i] = val;
}
突然使用通用KEYED_STORE_IC完成数组写入,从而将执行速度降低了 50 。
这是我的测试设置(100万次通话,约40亿次写入):
var b = new Int32Array(10000);
for (var j = 0; j < 999999; ++j) {
fill(b, j & 0xFFF, 6000 + (j % 0xFF), j);
}
该库在浏览器和节点8.x / 10.x上应表现良好。该测试是使用v8.10(V8版本6.2.414.50)完成的。
答案 0 :(得分:2)
V8开发人员在这里。您如何衡量这种差异?我无法复制它。
我已将您的代码复制粘贴到squarefree.com/shell/shell.html
中,对其中一个函数进行模运算重命名:
function fill(buf, from, to, val) {
for (var i = from; i < to; ++i)
buf[i] = val;
}
function fill2(buf, from, to, val) {
if (from < 0 || to < from || buf.length < to)
throw new Error();
for (var i = from; i < to; ++i)
buf[i] = val;
}
var b = new Int32Array(10000);
var t1 = Date.now();
for (var j = 0; j < 999999; ++j) {
fill(b, j & 0xFFF, 6000 + (j % 0xFF), j);
}
var t2 = Date.now();
for (var j = 0; j < 999999; ++j) {
fill2(b, j & 0xFFF, 6000 + (j % 0xFF), j);
}
var t3 = Date.now();
执行该操作(当前的Chrome Canary)后,我得到:
> t2 - t1
< 5820
> t3 - t2
< 5887
因此,手动边界检查增加了非常小的性能损失,这似乎是合理的。
我的猜测是,您暂时在索引计算中遇到了一个错误,并最终无法进行越界访问。那可以解释为什么使用通用IC,以及为什么观察到严重的速度下降。