0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 1 year has passed since last update.

JavaScriptによる大規模配列初期化のパフォーマンス検証

Last updated at Posted at 2023-09-12

今回の目的

以下の自分のシリーズ記事で素数判別アルゴリズムの善悪を議論しているのだが,JavaScript によるプログラム内で大規模な配列を使用しており,この配列アクセスのオーバーヘッドが無視できない状況になっているというか,如何にオーバーヘッドを減らせるか?が主題になりつつある。

自分はほぼ C 言語インタプリタのような感覚で JavaScript を利用してきたので,言語仕様的にもパフォーマンス的に曖昧な点を残したままだった。なので,今回の記事では JavaScript による大規模配列の性能テストを行ってみたい。

結論を先に書く

  • もう無印の JScript からは卒業しろ!遅すぎる!
  • JScript9 と Chakra では Chakra のほうが良いが,得手不得手があるので要注意。
  • 大規模な配列を宣言するとき
    • 可能であれば TypedArray を使え! ※初期値としてゼロが保証されているのがいい
    • 配列の宣言は new Array(size) を使え!new を省略した Array(size)は使うな!
      new を付けたほうが速い
    • [] で宣言して push は言うほど速くない。※可変サイズなので便利なんだけどね
  • 大規模な配列を初期化するとき
    • 配列の初期化は for(...) ループで回せ!
    • 配列の初期化に fill() メソッドを使うな!
    • 配列にはできるだけ整数を代入しろ!
      スピードは速い順に integerdoublebool である。
      とくに Chakra は bool が遅いので要注意。

過去に自分の記事に掲載したサンプルコードは全て new が抜けている件
一応,脚注1によると,

標準ビルトインオブジェクトの一部は new 演算子の省略が認められています。ただし,new 演算子を省略した場合に挙動が変わるものがあるので注意が必要です。

とあるので文法的には間違いではないらしい。

宣言および初期化テスト結果

JavaScript で取り扱える安全な整数の上限 $2^{53} - 1$ の平方根を上回らない最大の整数 $94,906,265$ を配列の size として宣言および全域初期化するまでの時間を計測した。なお,Windows Script Host のデフォルトである JScript エンジン,Internet Explorer 9 以降の JScript9 エンジン,Chakra エンジンを比較してみたが,JScript エンジンだけは十数分経っても終了しなかったため,結果からは省いている。

有意に速い!というところは赤色で示した。

表* 配列の宣言と初期化方法による処理時間の比較(単位は秒)
No Declaration Initialize JScript9 Chakra
#000 a = [] for( ... ) a.push(true) 1.315 2.218
#001 for( ... ) a.push(0) 2.242 1.027
#002 for( ... ) a.push(0.0) 2.252 1.032
#100 a = Array(size) for( ... ) a[i] = true 1.426 2.151
#101 for( ... ) a.push(0) 1.452 1.884
#102 for(...) a.push(0.0) 1.360 2.001
#110 a.fill(true) N/A 2.629
#111 a.fill(0) N/A 2.176
#112 a.fill(0.0) N/A 2.168
#200 a = new Array(size) for( ... ) a[i] = true 1.380 2.230
#201 for( ... ) a[i] = 0 0.955 0.584
#202 for( ... ) a[i] = 0.0 0.963 0.597
#210 a.fill(true) N/A 2.597
#211 a.fill(0) N/A 1.142
#212 a.fill(0.0) N/A 1.156
#300 a = new Uint8Array(size) N/A 0.064
#301 a = new Uint32Array(size) N/A 0.154
#302 a = new Float64Array(size) N/A 0.274
  • Core i5 10400, 8GB, Windows10 22H2
  • 計測時間は 10 回の平均値です。
  • N/A(Not Available)利用不可
テストスクリプトはコチラ
test-array.js
//------------------------------------------------------------------------------
// 定数定義
//------------------------------------------------------------------------------
var	MAX_SAFE_INTEGER = 9007199254740991;	// 最大の整数
var	MAX_SIEVE = 94906265;					// 最大の整数の平方根
//------------------------------------------------------------------------------
// テスト関数
//------------------------------------------------------------------------------
function test000() { var table = []; for( var i = 0; i < MAX_SIEVE; i++ ) table.push( true ); return( table ); }
function test001() { var table = []; for( var i = 0; i < MAX_SIEVE; i++ ) table.push( 0 );    return( table ); }
function test002() { var table = []; for( var i = 0; i < MAX_SIEVE; i++ ) table.push( 0.0 );  return( table ); }

function test100() { var table = Array( MAX_SIEVE ); for( var i = 0; i < table.length; i++ ) table[i] = true; return( table ); }
function test101() { var table = Array( MAX_SIEVE ); for( var i = 0; i < table.length; i++ ) table[i] = 0;    return( table ); }
function test102() { var table = Array( MAX_SIEVE ); for( var i = 0; i < table.length; i++ ) table[i] = 0.0;  return( table ); }
function test110() { var table = Array( MAX_SIEVE ); table.fill( true ); return( table ); }
function test111() { var table = Array( MAX_SIEVE ); table.fill( 0    ); return( table ); }
function test112() { var table = Array( MAX_SIEVE ); table.fill( 0.0  ); return( table ); }

function test200() { var table = new Array( MAX_SIEVE ); for( var i = 0; i < table.length; i++ ) table[i] = true; return( table ); }
function test201() { var table = new Array( MAX_SIEVE ); for( var i = 0; i < table.length; i++ ) table[i] = 0;    return( table ); }
function test202() { var table = new Array( MAX_SIEVE ); for( var i = 0; i < table.length; i++ ) table[i] = 0.0;  return( table ); }
function test210() { var table = new Array( MAX_SIEVE ); table.fill( true ); return( table ); }
function test211() { var table = new Array( MAX_SIEVE ); table.fill( 0    ); return( table ); }
function test212() { var table = new Array( MAX_SIEVE ); table.fill( 0.0  ); return( table ); }

function test300() { var table = new Uint8Array( MAX_SIEVE );   return( table ); }
function test301() { var table = new Uint32Array( MAX_SIEVE );  return( table ); }
function test302() { var table = new Float64Array( MAX_SIEVE ); return( table ); }
//------------------------------------------------------------------------------
// 関数テーブル
//------------------------------------------------------------------------------
var	func_table = {
	"000":test000, "001":test001, "002":test002,
	"100":test100, "101":test101, "102":test102,"110":test110,"111":test111,"112":test112,
	"200":test200, "201":test201, "202":test202,"210":test210,"211":test211,"212":test212,
	"300":test300, "301":test301, "302":test302
};
//------------------------------------------------------------------------------
// メイン関数
//------------------------------------------------------------------------------
function main( args ) {
	if( args.Count == 0 ) {
		WScript.Echo( "Usage: test-array(.js) [no]" );
		WScript.Echo( "no   declaration          initialize" );
		WScript.Echo( "000: a = []               for(...) a.push(true)" );
		WScript.Echo( "001: a = []               for(...) a.push(0)" );
		WScript.Echo( "002: a = []               for(...) a.push(0.0)" );
		WScript.Echo( "100: a = Array(size)      for(...) a[i] = true" );
		WScript.Echo( "101: a = Array(size)      for(...) a[i] = 0" );
		WScript.Echo( "102: a = Array(size)      for(...) a[i] = 0.0" );
		WScript.Echo( "110: a = Array(size)      a.fill(true)" );
		WScript.Echo( "111: a = Array(size)      a.fill(0)" );
		WScript.Echo( "112: a = Array(size)      a.fill(0.0)" );
		WScript.Echo( "200: a = new Array(size)  for(...) a[i] = true" );
		WScript.Echo( "201: a = new Array(size)  for(...) a[i] = 0" );
		WScript.Echo( "202: a = new Array(size)  for(...) a[i] = 0.0" );
		WScript.Echo( "210: a = new Array(size)  a.fill(true)" );
		WScript.Echo( "211: a = new Array(size)  a.fill(0)" );
		WScript.Echo( "212: a = new Array(size)  a.fill(0.0)" );
		WScript.Echo( "300: a = new Uint8Array(size)" );
		WScript.Echo( "301: a = new Uint32Array(size)" );
		WScript.Echo( "302: a = new Float64Array(size)" );
		return( false );
	}
	var	func = func_table[args(0)];
	if( func == null ) {
		WScript.Echo( "コマンド " + args(0) + " には未対応です!!"  );
		return( false );
	}
	var	table = func();
	return( true );
}
var	args = WScript.Arguments.Unnamed;
var	ret = main( args );

今後の課題

しかしながら,脚注2によると,Google 謹製の Chrome V8 エンジンでは逆に new Array(size) を使用せず,[] あるいは new Array() のように空配列で宣言して push で増やしていくのが良いとのこと。Packed ArrayHoley Array の問題らしい。

そもそも最大与党の V8 エンジンを評価してないのってダメだよな・・・

  1. JavaScriptの配列初期化 - Qiita

  2. 高速化の観点から new Array(100) を使わない方が良い理由

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?