underscore.js を導入してから、for文を使う機会が減りました。
何故なら、underscore の each がとても便利だからです。
しかし、処理速度はどちらが速いのでしょうか?
for文が速いのなら、for文を極力使いたいですし、
each文が速いのなら、エラー処理も実装できて便利なので、後者を利用します。
以上よろしくお願い致します。
気になる質問をクリップする
クリップした質問は、後からいつでもMYページで確認できます。
またクリップした質問に回答があった際、通知やメールを受け取ることができます。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
回答4件
0
while 文
クローズしているようですが、速さを追い求めるなら while
文が最速です。
JavaScript
1var bench = new Array(10000000); 2var sTime, fTime; 3 4/** 5 * for-1 6 */ 7sTime = new Date(); 8 9for (var i = 0; i < bench.length; i++) { 10 bench[i] = Math.sqrt(Math.pow(12345) + Math.pow(53142)); 11} 12 13fTime = new Date(); 14 15console.log(fTime - sTime); // 結果 -> 718 16 17/** 18 * while-1 19 */ 20sTime = new Date(); 21var i = bench.length; 22 23while (i--) { 24 bench[i] = Math.sqrt(Math.pow(12345) + Math.pow(53142)); 25} 26 27fTime = new Date(); 28 29console.log(fTime - sTime); // 結果 -> 577
更に速く
ただし、前述のコードにはまだ最適化の余地があります。
JavaScript
1var bench = new Array(10000000); 2var sTime, fTime; 3 4/** 5 * for-2 6 */ 7sTime = new Date(); 8 9for (var i = 0, len = bench.length, sqrt = Math.sqrt, pow = Math.pow; i < len; i++) { 10 bench[i] = sqrt(pow(12345) + pow(53142)); 11} 12 13fTime = new Date(); 14 15console.log(fTime - sTime); // 結果 -> 641 16 17/** 18 * while-2 19 */ 20sTime = new Date(); 21var i = bench.length, 22 sqrt = Math.sqrt, 23 pow = Math.pow; 24 25while (i--) { 26 bench[i] = sqrt(pow(12345) + pow(53142)); 27} 28 29fTime = new Date(); 30 31console.log(fTime - sTime); // 結果 -> 555
ちなみに、最近のブラウザは最適化が進んでいる為、使用するブラウザ、クライアントPCのスペックによっては上記差は小さくなる可能性があります。
しかしながら、原理的には for
文は第一式、第三式の処理が while
文よりも余計にかかっているので while
文の方が速くなります。
第一式はともかく、第二式+第三式が両方とも実行されるのは for
文にパフォーマンス上は不利に働きます。
出来るだけ多くのブラウザで高速に動作させることを求めるならば、while
文の一択だと私は思います。
比較まとめ
コードの最適化を進めた上で比較してみました。
JavaScript
1var bench = new Array(10000000); 2var sTime, fTime; 3 4/** 5 * _.each 6 */ 7sTime = new Date(); 8var sqrt = Math.sqrt, 9 pow = Math.pow; 10_.each(bench, function (value, index) { 11 bench[index] = sqrt(pow(12345) + pow(53142)); 12}); 13fTime = new Date(); 14console.log('_.each', fTime - sTime); 15 16/** 17 * Array.prototype.forEach 18 */ 19sTime = new Date(); 20var sqrt = Math.sqrt, 21 pow = Math.pow; 22bench.forEach(function (value, index, array) { 23 array[index] = sqrt(pow(12345) + pow(53142)); 24}); 25fTime = new Date(); 26console.log('Array.prototype.forEach', fTime - sTime); 27 28/** 29 * for 30 */ 31sTime = new Date(); 32for (var i = 0, len = bench.length, sqrt = Math.sqrt, pow = Math.pow; i < len; i++) { 33 bench[i] = sqrt(pow(12345) + pow(53142)); 34} 35fTime = new Date(); 36console.log('for', fTime - sTime); 37 38/** 39 * while 40 */ 41sTime = new Date(); 42var i = bench.length, 43 sqrt = Math.sqrt, 44 pow = Math.pow; 45 46while (i--) { 47 bench[i] = sqrt(pow(12345) + pow(53142)); 48} 49 50fTime = new Date(); 51console.log('while', fTime - sTime);
私の環境では、次の結果となりました。
JavaScript
1"_.each 899" 2"Array.prototype.forEach 658" 3"for 574" 4"while 570"
- ライブラリはネイティブ機能に勝てません。
- 繰り返し処理において、関数呼び出しは制御構文(while, for 等)よりも遅いです。
更新履歴
- 2017/08/22 23:40 「比較まとめ」の節を追記
Re: airulove さん
投稿2017/08/22 14:17
編集2017/08/22 14:40総合スコア18156
0
for文と_.eachの処理速度に大差はなく、DOM操作の方が問題になることが多いということでした。
以下ソースです。
foreach vs for loop
For Loop: 0.143s
Underscore.js foreach: 0.171s
https://gist.github.com/cerivera/8534081
_.each vs for
chances are that most of the CPU cost is in DOM manipulation and reflows.
https://samuelrossille.com/posts/2013-02-22-each-vs-for.html
投稿2017/08/22 06:45
総合スコア1724
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
0
自己解決
簡単な検証をしてみました。
重い計算式が思いつかなかったので、
三平方の定理を使いました。
JavaScript
1var bench = new Array(10000000); 2var sTime = 0; 3var fTime = 0; 4 5sTime = new Date(); 6 7for (var i = 0; i < bench.length; i++) { 8 bench[i] = Math.sqrt(Math.pow(12345) + Math.pow(53142)); 9} 10 11fTime = new Date(); 12 13console.log(fTime - sTime); // 結果 -> 415 14 15sTime = fTime; 16 17_.each(bench, function (value, index) { 18 value = Math.sqrt(Math.pow(12345) + Math.pow(53142)); 19}); 20 21fTime = new Date(); 22 23console.log(fTime - sTime); // 結果 -> 1407
3倍以上の差がある結果になりました。
投稿2017/08/22 06:35
編集2017/08/22 06:37総合スコア35
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/08/22 06:59
2017/08/22 07:06 編集
2017/08/22 07:17
2017/08/22 07:20 編集
2017/08/22 07:23
2017/08/22 07:27
2017/08/22 07:30
2017/08/22 14:28
0
どんな処理をするかによって、書き方を含めて色々変わってくるのかと思います。ですので、処理の内容を決めて、いろいろな書き方を試して見ました。
処理内容: ある数までの素数の配列に対して、それぞれの逆数の和を計算します。(数学的には無限大に発散します)
JavaScript
1const _ = require('underscore'); 2const lodash = require('lodash'); 3const Lazy = require('lazy.js'); 4const R = require('ramda'); 5const Benchmark = require('benchmark'); 6 7function generatePrimeNumbers(max) { 8 const itr23 = (function*() { 9 yield 2; 10 yield 3; 11 let i = 1; 12 while (true) { 13 i += 4; 14 yield i; 15 i += 2; 16 yield i; 17 } 18 })(); 19 const primeNums = []; 20 const checkMax = Math.sqrt(max); 21 const primeChecks = new Array(max); 22 let checkNum = itr23.next().value; 23 while (checkNum <= checkMax) { 24 if (!primeChecks[checkNum]) { 25 primeNums.push(checkNum); 26 let targetNum = checkNum * checkNum; 27 while (targetNum <= max) { 28 primeChecks[targetNum] = true; 29 targetNum += checkNum; 30 } 31 } 32 checkNum = itr23.next().value; 33 } 34 while (checkNum <= max) { 35 if (!primeChecks[checkNum]) { 36 primeNums.push(checkNum); 37 } 38 checkNum = itr23.next().value; 39 } 40 return Object.freeze(primeNums); 41} 42 43// pnums = generatePrimeNumbers(100); 44// pnums = generatePrimeNumbers(10000); 45pnums = generatePrimeNumbers(1000000); 46// pnums = generatePrimeNumbers(10000000); 47 48let correctSum = 0; 49for (let i = 0, len = pnums.length; i < len; i++) { 50 correctSum += 1 / pnums[i]; 51} 52console.log(`sum = ${correctSum}`); 53 54const suite = new Benchmark.Suite; 55suite 56 .add('for(;;)', () => { 57 let sum = 0; 58 for (let i = 0, len = pnums.length; i < len; i++) { 59 sum += 1 / pnums[i]; 60 } 61 if (sum !== correctSum) throw 'Not correct'; 62 }) 63 .add('for of', () => { 64 let sum = 0; 65 for (let value of pnums) { 66 sum += 1 / value; 67 } 68 if (sum !== correctSum) throw 'Not correct'; 69 }) 70 .add('while', () => { 71 let sum = 0; 72 let i = 0; 73 const len = pnums.length; 74 while (i < len) { 75 sum += 1 / pnums[i]; 76 i++; 77 } 78 if (sum !== correctSum) throw 'Not correct'; 79 }) 80 .add('forEach', () => { 81 let sum = 0; 82 pnums.forEach(value => { 83 sum += 1 / value; 84 }); 85 if (sum !== correctSum) throw 'Not correct'; 86 }) 87 .add('_.each', () => { 88 let sum = 0; 89 _.each(pnums, value => { 90 sum += 1 / value; 91 }); 92 if (sum !== correctSum) throw 'Not correct'; 93 }) 94 .add('lodash.each', () => { 95 let sum = 0; 96 lodash.each(pnums, value => { 97 sum += 1 / value; 98 }); 99 if (sum !== correctSum) throw 'Not correct'; 100 }) 101 .add('map.reduce', () => { 102 const sum = pnums.map(v => 1 / v).reduce((a, b) => a + b, 0); 103 if (sum !== correctSum) throw 'Not correct'; 104 }) 105 .add('_.map => reduce', () => { 106 const sum = _.reduce(_.map(pnums, v => 1 / v), (a, b) => a + b, 0); 107 if (sum !== correctSum) throw 'Not correct'; 108 }) 109 .add('lodash.map => reduce', () => { 110 const sum = lodash.reduce(lodash.map(pnums, v => 1 / v), (a, b) => a + b, 111 0); 112 if (sum !== correctSum) throw 'Not correct'; 113 }) 114 .add('Lazy.map.reduce', () => { 115 const sum = Lazy(pnums).map(v => 1 / v).reduce((a, b) => a + b, 0); 116 if (sum !== correctSum) throw 'Not correct'; 117 }) 118 .add('Ramda map => reduce', () => { 119 const sum = R.reduce(R.add, 0, R.map(v => 1 / v, pnums)); 120 if (sum !== correctSum) throw 'Not correct'; 121 }) 122 .add('lodash.map => sum', () => { 123 const sum = lodash.sum(lodash.map(pnums, v => 1 / v)); 124 if (sum !== correctSum) throw 'Not correct'; 125 }) 126 .add('Lazy.map.sum', () => { 127 const sum = Lazy(pnums).map(v => 1 / v).sum(); 128 if (sum !== correctSum) throw 'Not correct'; 129 }) 130 .add('Ramda map => sum', () => { 131 const sum = R.sum(R.map(v => 1 / v, pnums)); 132 if (sum !== correctSum) throw 'Not correct'; 133 }) 134 .add('lodash.sumBy', () => { 135 const sum = lodash.sumBy(pnums, v => 1 / v); 136 if (sum !== correctSum) throw 'Not correct'; 137 }) 138 .on('cycle', function(event) { 139 console.log(String(event.target)); 140 }) 141 .on('complete', function() { 142 console.log('Fastest is ' + this.filter('fastest').map('name')); 143 }) 144 .run({ 145 'async': true 146 });
実行結果
sum = 2.8873280995676938 for(;;) x 184 ops/sec ±5.72% (63 runs sampled) for of x 56.19 ops/sec ±2.76% (57 runs sampled) while x 165 ops/sec ±7.68% (61 runs sampled) forEach x 17.92 ops/sec ±6.76% (33 runs sampled) _.each x 101 ops/sec ±4.00% (67 runs sampled) lodash.each x 99.58 ops/sec ±3.65% (69 runs sampled) map.reduce x 13.17 ops/sec ±6.51% (35 runs sampled) _.map => reduce x 53.82 ops/sec ±9.73% (55 runs sampled) lodash.map => reduce x 36.80 ops/sec ±7.56% (40 runs sampled) Lazy.map.reduce x 64.91 ops/sec ±4.08% (54 runs sampled) Ramda map => reduce x 54.05 ops/sec ±5.87% (55 runs sampled) lodash.map => sum x 38.73 ops/sec ±7.35% (40 runs sampled) Lazy.map.sum x 65.47 ops/sec ±10.10% (57 runs sampled) Ramda map => sum x 56.32 ops/sec ±4.42% (55 runs sampled) lodash.sumBy x 79.98 ops/sec ±4.40% (60 runs sampled) Fastest is for(;;)
【環境】
Windows 10 64bit
Node.js v8.4.0 (64bit)
underscore@1.8.3
lodash@4.17.4
lazy.js@0.5.0
ramda@0.24.1
※ Babel等でのトランスパイルはしていません。
前半のgeneratePrimeNumbers
はエラトステネスの篩を使った素数の配列を求める関数です。高速化するためにやけにこだわって作ってみました。この配列はベンチマークで共通で使うため、Obejct.freezeで変更できないようにしています。
後半はBenchmark.jsを使ったベンチマークです。ops/escが高いほど1秒あたりの処理数が多い=速いとなります。
実行タイミングで揺れはありますが、for(;;)が最速でwhileが次点でした。ただ、逆転する場合もありますので、ほぼ同じぐらいとみても良いかもしれません。underscore.jsやlodashのeachはそこそこの速さですが、for(;;)には負けるようです。逆に、ネイティブのはずのforEachはかなり遅いという結果になりました。
map.reduce以降はいわゆる関数型プログラミングの考え方でコーディングした場合です。同じような処理ならLazy.jsが優秀ですが、単純に何かをした和という話ならlodashのsumByが一番良いようです。こちらも、ネイティブのmapとreduceの組合せはとても遅いという結果になりました。
他にも書き方は色々ありますので、試して見ると良いでしょう。
JavaScriptの歴史からすると、ネイティブのforEachやmap、reduceというのはES5からであり比較的最近です。単なるfor(;;)文に比べてJavaScriptエンジン内での最適化がまだまだ甘い可能性はあると思われます。underscore.jsやlodashは速度を高めるためにfor(;;)を内部で使っているという話ですので、呼び出しのオーバーヘッド分ぐらいしか差が出なかったのかも知れません。
なお、この結果は上の【環境】に書いたNode.jsでの話であって、他のJavaScriptエンジンでは変わる可能性が十分にあります。Node.jsのバージョンや各ライブラリのバージョンによっても全く違う結果になる場合もありえますので、ご注意ください。
投稿2017/08/23 12:57
編集2017/08/23 13:04総合スコア21733
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
あなたの回答
tips
太字
斜体
打ち消し線
見出し
引用テキストの挿入
コードの挿入
リンクの挿入
リストの挿入
番号リストの挿入
表の挿入
水平線の挿入
プレビュー
質問の解決につながる回答をしましょう。 サンプルコードなど、より具体的な説明があると質問者の理解の助けになります。 また、読む側のことを考えた、分かりやすい文章を心がけましょう。
バッドをするには、ログインかつ
こちらの条件を満たす必要があります。
2017/08/22 14:21
2017/08/23 00:44
2017/08/23 02:39