[解決済み] v8 JavaScriptのconst、let、varのパフォーマンスへの影響?
質問
機能の違いに関係なく、新しいキーワード 'let' と 'const' を使用すると、'var' と比較してパフォーマンスに何らかの一般的または特異的な影響がありますか?
プログラムを実行した後
function timeit(f, N, S) {
var start, timeTaken;
var stats = {min: 1e50, max: 0, N: 0, sum: 0, sqsum: 0};
var i;
for (i = 0; i < S; ++i) {
start = Date.now();
f(N);
timeTaken = Date.now() - start;
stats.min = Math.min(timeTaken, stats.min);
stats.max = Math.max(timeTaken, stats.max);
stats.sum += timeTaken;
stats.sqsum += timeTaken * timeTaken;
stats.N++
}
var mean = stats.sum / stats.N;
var sqmean = stats.sqsum / stats.N;
return {min: stats.min, max: stats.max, mean: mean, spread: Math.sqrt(sqmean - mean * mean)};
}
var variable1 = 10;
var variable2 = 10;
var variable3 = 10;
var variable4 = 10;
var variable5 = 10;
var variable6 = 10;
var variable7 = 10;
var variable8 = 10;
var variable9 = 10;
var variable10 = 10;
function varAccess(N) {
var i, sum;
for (i = 0; i < N; ++i) {
sum += variable1;
sum += variable2;
sum += variable3;
sum += variable4;
sum += variable5;
sum += variable6;
sum += variable7;
sum += variable8;
sum += variable9;
sum += variable10;
}
return sum;
}
const constant1 = 10;
const constant2 = 10;
const constant3 = 10;
const constant4 = 10;
const constant5 = 10;
const constant6 = 10;
const constant7 = 10;
const constant8 = 10;
const constant9 = 10;
const constant10 = 10;
function constAccess(N) {
var i, sum;
for (i = 0; i < N; ++i) {
sum += constant1;
sum += constant2;
sum += constant3;
sum += constant4;
sum += constant5;
sum += constant6;
sum += constant7;
sum += constant8;
sum += constant9;
sum += constant10;
}
return sum;
}
function control(N) {
var i, sum;
for (i = 0; i < N; ++i) {
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
sum += 10;
}
return sum;
}
console.log("ctl = " + JSON.stringify(timeit(control, 10000000, 50)));
console.log("con = " + JSON.stringify(timeit(constAccess, 10000000, 50)));
console.log("var = " + JSON.stringify(timeit(varAccess, 10000000, 50)));
.. 私の結果は以下の通りでした。
ctl = {"min":101,"max":117,"mean":108.34,"spread":4.145407097016924}
con = {"min":107,"max":572,"mean":435.7,"spread":169.4998820058587}
var = {"min":103,"max":608,"mean":439.82,"spread":176.44417700791374}
しかし、ここで述べたような議論は、特定のシナリオにおけるパフォーマンスの違いの実際の可能性を示しているようです。 https://esdiscuss.org/topic/performance-concern-with-let-const
どのように解決するのですか?
TL;DR
理論的には というように、このループの最適化されていないバージョンです。
for (let i = 0; i < 500; ++i) {
doSomethingWith(i);
}
を使った最適化されていないバージョンのループより遅いかもしれません。
var
:
for (var i = 0; i < 500; ++i) {
doSomethingWith(i);
}
というのは
異なる
i
変数は、ループの繰り返しごとに
let
であるのに対し
i
と
var
.
それに対しての反論
というのは
var
はループの外側で宣言されているのに対して
let
はループの中でしか宣言されないのに対して、
実際には
2018年現在、最新のJavaScriptエンジンはループのイントロスペクションを十分に行い、いつその差を最適化できるかを知っています。(それ以前でも、あなたのループは十分な仕事をしていて、追加の
let
-に関連するオーバーヘッドが消えてしまうほど、ループは十分に機能していました。しかし、今ではそれを心配する必要さえありません)。
合成ベンチマークに注意 これらは非常に間違えやすく、実際のコードにはない方法で JavaScript エンジンの最適化機能を作動させます (良い意味でも悪い意味でも)。しかし、合成ベンチマークが必要な場合は、これを使用します。
const now = typeof performance === "object" && performance.now
? performance.now.bind(performance)
: Date.now.bind(Date);
const btn = document.getElementById("btn");
btn.addEventListener("click", function() {
btn.disabled = true;
runTest();
});
const maxTests = 100;
const loopLimit = 50000000;
const expectedX = 1249999975000000;
function runTest(index = 1, results = {usingVar: 0, usingLet: 0}) {
console.log(`Running Test #${index} of ${maxTests}`);
setTimeout(() => {
const varTime = usingVar();
const letTime = usingLet();
results.usingVar += varTime;
results.usingLet += letTime;
console.log(`Test ${index}: var = ${varTime}ms, let = ${letTime}ms`);
++index;
if (index <= maxTests) {
setTimeout(() => runTest(index, results), 0);
} else {
console.log(`Average time with var: ${(results.usingVar / maxTests).toFixed(2)}ms`);
console.log(`Average time with let: ${(results.usingLet / maxTests).toFixed(2)}ms`);
btn.disabled = false;
}
}, 0);
}
function usingVar() {
const start = now();
let x = 0;
for (var i = 0; i < loopLimit; i++) {
x += i;
}
if (x !== expectedX) {
throw new Error("Error in test");
}
return now() - start;
}
function usingLet() {
const start = now();
let x = 0;
for (let i = 0; i < loopLimit; i++) {
x += i;
}
if (x !== expectedX) {
throw new Error("Error in test");
}
return now() - start;
}
<input id="btn" type="button" value="Start">
V8/Chrome または SpiderMonkey/Firefox のいずれでも、その合成テストに有意差はないと書かれています。(両方のブラウザでテストを繰り返すと、一方が勝つか、もう一方が勝つか、どちらの場合も誤差の範囲になります)。しかし、繰り返しますが、これは合成ベンチマークであり、あなたのコードではありません。あなたのコードがパフォーマンスの問題を抱えたとき、そして抱えた場合に、コードのパフォーマンスについて心配するのです。
スタイルの問題として、私は
let
の方がスコープの利点と、ループ変数をクロージャで使う場合のクロージャ・イン・ループの利点があるからです。
詳細
との重要な違いは
var
と
let
の中に
for
のループは、別の
i
が作成されることです。これは古典的な「クロージャ・イン・ループ」問題に対処するものです。
function usingVar() {
for (var i = 0; i < 3; ++i) {
setTimeout(function() {
console.log("var's i: " + i);
}, 0);
}
}
function usingLet() {
for (let i = 0; i < 3; ++i) {
setTimeout(function() {
console.log("let's i: " + i);
}, 0);
}
}
usingVar();
setTimeout(usingLet, 20);
ループ本体ごとに新しいEnvironmentRecordを作成する(
スペックリンク
) は作業であり、作業には時間がかかります。そのため、理論上は
let
バージョンよりも遅くなります。
var
バージョンよりも遅くなります。
しかし、この違いが問題になるのは、ループ内で関数(クロージャ)を作成し、その関数で
i
を使用するループ内の関数 (クロージャ) を作成する場合のみです。そうでなければ、この違いは観察できず、最適化することができます。
2018 年現在、V8 (および Firefox の SpiderMonkey) は十分なイントロスペクションを行っているようで、let
の可変反復セマンティクスを利用しないループではパフォーマンスコストがかからないという十分なイントロスペクションを行っているようです。参照
このテスト
.
場合によっては
const
が最適化の機会を提供することがあります。
var
ではできない最適化の機会を与えるかもしれません。特にグローバル変数ではそうです。
グローバル変数の問題は、それがまあ、グローバルであるということです。
任意の
コード
どこでも
でアクセスすることができます。ですから、もしあなたが変数を
var
で変数を宣言した場合、エンジンはその変数が後で読み込まれたコードの結果、決して変更されないと仮定することができないのです。
と
const
では、値が変更できないことをエンジンに明示的に伝えていることになります¹。そのため、値が変更できないことを知りながら、それを使用するコードに変数参照ではなくリテラルを出力するなど、エンジンが望む最適化を自由に行うことができます。
¹ オブジェクトの場合、値は
参照
への参照であり、オブジェクトそのものではないことに注意してください。そのため
const o = {}
であれば、オブジェクトの状態を変更することができます (
o.answer = 42
) を作ることはできませんが
o
を新しいオブジェクトを指すようにすることはできません (そうすると、それが含むオブジェクトの参照を変更する必要があるからです)。
を使用する場合
let
または
const
で、他の
var
-のような状況であっても、性能に違いが出ることはないでしょう。この関数は
var
を使っても
let
といった具合です。
function foo() {
var i = 0;
while (Math.random() < 0.5) {
++i;
}
return i;
}
もちろん、これらはすべて重要ではありませんし、解決すべき真の問題がある場合にのみ、心配することです。
関連
-
[解決済み] JavaScriptで "use strict "は何をするのか、その根拠は?
-
[解決済み] let "と "var "の使い分けは?
-
[解決済み] varキーワードの目的と、どのような場合に使用する(または省略する)べきですか?
-
[解決済み] const int*、const int * const、int const *の違いは何ですか?
-
[解決済み] C#のconstとreadonlyの違いは何ですか?
-
[解決済み] constexpr` と `const` の相違点
-
[解決済み】JavaScriptで2つの配列を結合し、項目の重複を排除する方法
-
[解決済み】letやconstで宣言された変数はホイストされるのか?
-
[解決済み] JSXとLoadshを使用して、ある要素をn回繰り返す方法
-
[解決済み] Chromeのwebkitインスペクタで「Unsafe JavaScript attempt to access frame with URL...」というエラーが継続的に発生する。
最新
-
nginxです。[emerg] 0.0.0.0:80 への bind() に失敗しました (98: アドレスは既に使用中です)
-
htmlページでギリシャ文字を使うには
-
ピュアhtml+cssでの要素読み込み効果
-
純粋なhtml + cssで五輪を実現するサンプルコード
-
ナビゲーションバー・ドロップダウンメニューのHTML+CSSサンプルコード
-
タイピング効果を実現するピュアhtml+css
-
htmlの選択ボックスのプレースホルダー作成に関する質問
-
html css3 伸縮しない 画像表示効果
-
トップナビゲーションバーメニュー作成用HTML+CSS
-
html+css 実装 サイバーパンク風ボタン
おすすめ
-
[解決済み] JavaScriptで次の要素/前の要素を取得しますか?
-
[解決済み] reactのrender関数でdynamic hrefを作成するには?
-
[解決済み] モバイルWeb HTML5フレームワークの選び方【終了しました
-
[解決済み] javascript の関数から `undefined` と `null` のどちらを返すのが良いのでしょうか?
-
[解決済み] CORS OriginヘッダーとCSRFトークンによるCSRF保護
-
[解決済み] JSXとLoadshを使用して、ある要素をn回繰り返す方法
-
[解決済み] Reactコンポーネントでthis.setStateを複数回使用するとどうなりますか?
-
[解決済み] JavaScriptで長い配列を小さい配列に分割する方法
-
[解決済み] HTML要素にスクロールバーがあるかどうかをチェックする
-
[解決済み] querySelectorAllがない場合、ライブラリを使用せずに属性で要素を取得する?