質問をすることでしか得られない、回答やアドバイスがある。

15分調べてもわからないことは、質問しよう!

新規登録して質問してみよう
ただいま回答率
85.51%
JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Underscore.js

Underscore.jsは、JavaScriptのためのユーティリティライブラリです。JavaScriptの関数・配列、オブジェクトを扱う際に度々発生する処理がメソッドとしてまとめられています。他のライブラリに依存しないため、稼働中のアプリケーションにも導入可能です。

Q&A

解決済

4回答

587閲覧

for文と Underscore.js の each はどちらが速いですか?

airulove

総合スコア35

JavaScript

JavaScriptは、プログラミング言語のひとつです。ネットスケープコミュニケーションズで開発されました。 開発当初はLiveScriptと呼ばれていましたが、業務提携していたサン・マイクロシステムズが開発したJavaが脚光を浴びていたことから、JavaScriptと改名されました。 動きのあるWebページを作ることを目的に開発されたもので、主要なWebブラウザのほとんどに搭載されています。

Underscore.js

Underscore.jsは、JavaScriptのためのユーティリティライブラリです。JavaScriptの関数・配列、オブジェクトを扱う際に度々発生する処理がメソッドとしてまとめられています。他のライブラリに依存しないため、稼働中のアプリケーションにも導入可能です。

0グッド

0クリップ

投稿2017/08/22 06:25

underscore.js を導入してから、for文を使う機会が減りました。
何故なら、underscore の each がとても便利だからです。

しかし、処理速度はどちらが速いのでしょうか?

for文が速いのなら、for文を極力使いたいですし、
each文が速いのなら、エラー処理も実装できて便利なので、後者を利用します。

以上よろしくお願い致します。

気になる質問をクリップする

クリップした質問は、後からいつでもMYページで確認できます。

またクリップした質問に回答があった際、通知やメールを受け取ることができます。

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

guest

回答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
think49

総合スコア18156

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

airulove

2017/08/22 14:21

回答ありがとうございます!! 参考にさせて頂きます。
x_x

2017/08/23 00:44

わたしのところでは、forEach が圧倒的に速かったのですがなぜでしょうか?
think49

2017/08/23 02:39

forEach に実装の特別な最適化が働いているのかもしれません。 私は Google Chrome 60.0.3112.101 で検証しました。
guest

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

IShix

総合スコア1724

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

airulove

2017/08/22 06:49 編集

確かにDOM操作は重いですよね。 私は可能な限り、 Templete を使っています。 回答ありがとうございました。
IShix

2017/08/22 07:19

_.each使った方がいいですよ。 パフォーマンスを常に考える人は問題があった場合、探す力がある人だと思います。 できる限り書きやすく保守しやすいように書いて問題が起きれば取り組めばいいと思います。 最近のブラウザのエンジンも高性能になりましたからね。
airulove

2017/08/22 07:21

可読性も重要だと思うので、 each を今後も使いたいと思います。 ありがとうございました。
guest

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
airulove

総合スコア35

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

x_x

2017/08/22 06:59

sTime = fTime;は、sTime = new Date();ではないですか? その結果、784、453となり逆転しました。 配列格納が遅いようです。
airulove

2017/08/22 07:06 編集

sTime = fTime は new Date() で再定義したのと値は変わらないと思ったので、そうしたのですが、試しにご指摘の通りに再定義して検証してみました。 その結果、403、1296 となりました。 配列格納は each の方が遅いようです。
x_x

2017/08/22 07:17

わたしのところでは、何度か試したのですが、数値のブレはあるにせよ前者が遅いのですけどねぇ。Win版最新Chromeです。
airulove

2017/08/22 07:20 編集

もしかして、 32bit 版とかではないですか? 私は 64bit 版を使っています。 若しくは CPU の種類で結果が変わるのかもしれませんね。
x_x

2017/08/22 07:23

64bit版ですね。念のためIE11とFxでも確認してみましたが、いずれも後者が速かったです。Underscore.jsのバージョンが古いということはないでしょうか?
airulove

2017/08/22 07:27

確かに 1 年以上更新してないですね。 最新版入れて確認してみます。
airulove

2017/08/22 07:30

最新版入れてみましたが結果は変化なしです。
think49

2017/08/22 14:28

率直に申し上げますと、_.each のコードは value = Math.sqrt(Math.pow(12345) + Math.pow(53142)); となっていて、配列格納されてなく、for 文と同じ動作になっていないのでフェアな比較ではないと私は思います。 同じ条件で比較すれば、ライブラリが標準APIより速くなる道理はないと思います。 特に繰り返し処理に関しては、関数呼び出しと制御構文の間には超えられない壁があるように思います。
guest

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
raccy

総合スコア21733

バッドをするには、ログインかつ

こちらの条件を満たす必要があります。

あなたの回答

tips

太字

斜体

打ち消し線

見出し

引用テキストの挿入

コードの挿入

リンクの挿入

リストの挿入

番号リストの挿入

表の挿入

水平線の挿入

プレビュー

15分調べてもわからないことは
teratailで質問しよう!

ただいまの回答率
85.51%

質問をまとめることで
思考を整理して素早く解決

テンプレート機能で
簡単に質問をまとめる

質問する

関連した質問