我正在阅读幻灯片Breaking the Javascript Speed Limit with V8,并且有一个类似下面代码的示例。我无法弄清楚为什么<=比<这种情况下要慢,有人可以解释一下吗?任何意见表示赞赏。
<=
<
减缓:
this.isPrimeDivisible = function(candidate) { for (var i = 1; i <= this.prime_count; ++i) { if (candidate % this.primes[i] == 0) return true; } return false; }
(提示:primes 是一个长度为 prime_count 的数组)
快点:
this.isPrimeDivisible = function(candidate) { for (var i = 1; i < this.prime_count; ++i) { if (candidate % this.primes[i] == 0) return true; } return false; }
[More Info] 速度提升显着,在我本地环境测试,结果如下:
V8 version 7.3.0 (candidate)
time d8 prime.js 287107 12.71 user 0.05 system 0:12.84 elapsed
time d8 prime.js 287107 1.82 user 0.01 system 0:01.84 elapsed
我在 Google 从事 V8 工作,并希望在现有答案和评论的基础上提供一些额外的见解。
作为参考,以下是幻灯片中的完整代码示例:
var iterations = 25000; function Primes() { this.prime_count = 0; this.primes = new Array(iterations); this.getPrimeCount = function() { return this.prime_count; } this.getPrime = function(i) { return this.primes[i]; } this.addPrime = function(i) { this.primes[this.prime_count++] = i; } this.isPrimeDivisible = function(candidate) { for (var i = 1; i <= this.prime_count; ++i) { if ((candidate % this.primes[i]) == 0) return true; } return false; } }; function main() { var p = new Primes(); var c = 1; while (p.getPrimeCount() < iterations) { if (!p.isPrimeDivisible(c)) { p.addPrime(c); } c++; } console.log(p.getPrime(p.getPrimeCount() - 1)); } main();
首先,性能差异与<and<=运算符没有直接关系。所以请不要为了避免<=在你的代码中跳过箍,因为你在 Stack Overflow 上读到它很慢——它不是!
其次,人们指出数组是“有孔的”。这在 OP 帖子中的代码片段中并不清楚,但是当您查看初始化代码时很清楚this.primes:
this.primes
this.primes = new Array(iterations);
这会导致数组在 V8 中具有HOLEY元素类型,即使该数组最终完全填充/打包/连续。一般来说,空洞数组上的操作比压缩数组上的操作要慢,但在这种情况下,差异可以忽略不计: 每次 我们this.primes[i]在isPrimeDivisible. 没什么大不了!
HOLEY
this.primes[i]
isPrimeDivisible
TL;DR 数组HOLEY不是这里的问题。
其他人指出,代码读取越界。通常建议避免读取超出数组长度的内容,在这种情况下,确实可以避免性能大幅下降。但为什么呢?V8 可以处理其中一些越界场景,而对性能的影响很小。那么,这个特殊案例有什么特别之处呢?
越界读取导致在this.primes[i]这一undefined行:
undefined
if ((candidate % this.primes[i]) == 0) return true;
这给我们带来 了真正的问题 :%运算符现在与非整数操作数一起使用!
%
integer % someOtherInteger可以非常有效地计算;JavaScript 引擎可以为这种情况生成高度优化的机器代码。
integer % someOtherInteger
integer % undefined另一方面Float64Mod,由于undefined表示为双精度,因此效率较低。
integer % undefined
Float64Mod
确实可以通过在这一行更改<=into来改进代码片段:<
for (var i = 1; i <= this.prime_count; ++i) {
…不是因为<=它是某种比 更好的运算符<,而是因为这避免了在这种特殊情况下的越界读取。