1. ホーム
  2. javascript

[解決済み] V8でこのコードスニペットを使用すると、なぜ<=は<より遅いのですか?

2022-04-23 05:42:59

質問

スライドを読んでいるところ V8でJavascriptの速度制限を突破する 以下のような例があります。なぜ <= よりも遅いです。 < この場合、どなたか説明できる方はいらっしゃいますか?何かコメントがあれば、よろしくお願いします。

遅い。

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

[詳細情報] の速度向上は顕著で、私のローカル環境でのテストでは、以下のような結果でした。

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();


まず何よりも、性能差は関係ありません。 <<= 演算子を直接使用することができます。そのため <= Stack Overflowで遅いという記事を読んだからというのが理由ですが、そんなことはありません。


次に、配列が "holey"であることを指摘する人がいました。これはOPの投稿にあるコード・スニペットでは明確ではありませんでしたが、以下の初期化コードを見ていただければ一目瞭然です。 this.primes :

this.primes = new Array(iterations);

この結果、配列には a HOLEY 要素の種類 V8では、たとえ配列が完全に埋まる/詰まる/連続することになったとしても、です。一般に、穴のあいた配列に対する操作は、詰められた配列に対する操作よりも遅くなりますが、この場合、その差はごくわずかで、1 Smi を追加する程度です ( 小整数 を打つたびに(穴を防ぐための)チェックを行います。 this.primes[i] の中のループで isPrimeDivisible . 大したことはありません。

TL;DR である配列は HOLEY は、ここでは問題ではありません。


他の方から、コードが圏外に読まれているとのご指摘をいただきました。一般に、以下のように推奨されています。 配列の長さを超えて読み込まないようにする この場合、確かにパフォーマンスの大幅な低下は避けられたはずです。しかし、なぜでしょうか? V8は、これらのアウトオブバウンドのシナリオのいくつかを、パフォーマンスにわずかな影響を与えるだけで処理することができます。 では、この特殊なケースは何が特別なのでしょうか?

アウトオブバウンズの読み取りは this.primes[i] あり undefined をこの行に追加します。

if ((candidate % this.primes[i]) == 0) return true;

そして、その結果、次のようになります。 本当の問題 : % 演算子が非整数のオペランドで使われるようになったのです!

  • integer % someOtherInteger は非常に効率的に計算することができます。JavaScriptエンジンはこのような場合に高度に最適化された機械語コードを生成することができます。

  • integer % undefined 一方、この方法では Float64Mod というのは undefined はdoubleで表現される。

このコードは、確かに <=< をこの行に追加してください。

for (var i = 1; i <= this.prime_count; ++i) {

...ではなく <= よりも優れた演算子です。 < が、この特殊なケースで境界外読出しを回避できるというだけの理由です。