ローカル番号のフォーマット速度

I.目的



スクリプトの1つで、 読みやすいように大きな数値をフォーマットする必要が生じました。 ランクごとにグループに分割する方法に関するいくつかのレシピを見つけましたが、パフォーマンスに関する疑問は議論の中で表現されました。 私はいくつかのテストを実施することにしました。

II。 ソリューションオプション



数値を持つ変数があるとします。

var i = 100000; 


次の方法で、出力を100,000 (または100 000または100.000 )に変換できます。

1.正規表現による正規表現



いくつかのオプションがありますが、これは私にとって最もコンパクトなもののようです。

  i.toString().replace( /\B(?=(?:\d{3})+$)/g, ',' ); 


2. Intlオブジェクトの使用



つまり、 NumberFormatコンストラクターのformat()メソッドです。 2つのオプションが可能です。

a。 デフォルトでは:



  var fn_undef = new Intl.NumberFormat(); fn_undef.format(i); 


b。 強制ロケール:



  var fn_en_US = new Intl.NumberFormat('en-US'); fn_en_US.format(i); 


3. Number.toLocaleString()メソッドを使用する



説明から理解できるように、この方法は以前の方法と多くの共通点を持っています。 また、2つのオプションを検討してください。

a。 デフォルトでは:



  i.toLocaleString(); 


b。 強制ロケール:



  i.toLocaleString('en-US'); 


この方法は最短で最も便利なように見えますが、実際には最も陰湿な方法であることがわかりました。

III。 テスト



多数のフォーマットされた数値を含むテーブルを表示する必要があると想像してください。その場合、メソッドの速度の違いが重要になります。 複数のエンジンおよび異なるランタイム環境で数十万の操作を含むサイクルをテストしてみましょう。

1. Node.js 4.1.0



残念ながら、このバージョンのNode.jsではru-RUロケールがサポートされていない(またはサポートを追加する方法がわかりません)ため、一貫性を保つためにどこでもen-USロケールを使用する必要がありました。

最初に、スクリプトは変数を定義し、説明のために、すべての方法でフォーマットを表示します(5つの同一の結果)。 その後、5つのテストサイクルが続き、それぞれの経過時間が表示されます。

Node.jsのコード
 'use strict'; var i = 100000; const fn_undef = new Intl.NumberFormat(); const fn_en_US = new Intl.NumberFormat('en-US'); console.log( i.toString().replace( /\B(?=(?:\d{3})+$)/g, ',' ) ); console.log( fn_undef.format(i) ); console.log( fn_en_US.format(i) ); console.log( i.toLocaleString() ); console.log( i.toLocaleString('en-US') ); var time = process.hrtime(); while (i-- > 0) { i.toString().replace( /\B(?=(?:\d{3})+$)/g, ',' ); } console.log(process.hrtime(time)); i = 100000; time = process.hrtime(); while (i-- > 0) { fn_undef.format(i); } console.log(process.hrtime(time)); i = 100000; time = process.hrtime(); while (i-- > 0) { fn_en_US.format(i); } console.log(process.hrtime(time)); i = 100000; time = process.hrtime(); while (i-- > 0) { i.toLocaleString(); } console.log(process.hrtime(time)); i = 100000; time = process.hrtime(); while (i-- > 0) { i.toLocaleString('en-US'); } console.log(process.hrtime(time)); 


hrtimeプロファイリングhrtimeは、配列内の2つの数値(秒数とナノ秒数)のタプルとして時間差を生成します。

出力例(初期イラストを除く):

 [ 0, 64840650 ] [ 0, 473762595 ] [ 0, 470775460 ] [ 0, 514655925 ] [ 14, 120328524 ] 


ご覧のとおり、最初のオプションは最速です。 次の2つはほとんど違いはありませんが、最初の2つよりも1桁遅くなります。 4番目の方法はさらに少し遅くなります。 しかし、後者は異常に遅いです。

Intl.NumberFormat.format() Number.toLocaleString()メソッドとNumber.toLocaleString()メソッドIntl.NumberFormat.format()重要な違いが現れます。最初にコンストラクターでロケールを1回設定し、2回目で各呼び出しでロケールを設定します。 ロケールを決定する際、インタープリターは、ヘルプで説明されている非常にリソースを消費する操作を実行します 。 前者の場合は1回、フォーマッターの全期間にわたって実行します。後者の場合は、新たに10万回生成します。 コードにはわずかな違いがありますが、実行時には非常に重要です。

暫定的な結論を導き出すことができます。目的のロケールが事前にわかっている場合は、正規表現の置換を使用することをお勧めします。 ロケールが予測できない場合、ロケールIntl.NumberFormat.format()強制的に設定Intl.NumberFormat.format()メソッドを使用することをおIntl.NumberFormat.format()します。

ブラウザでこのコードをテストすると(プロファイリング関数を置き換えて)、この結論が正しいことがわかります。

2.ブラウザ



コンソールでこのコードを実行します。

ブラウザ用のコード
 var i = 100000; const fn_undef = new Intl.NumberFormat(); const fn_en_US = new Intl.NumberFormat('en-US'); console.log( i.toString().replace( /\B(?=(?:\d{3})+$)/g, ',' ) ); console.log( fn_undef.format(i) ); console.log( fn_en_US.format(i) ); console.log( i.toLocaleString() ); console.log( i.toLocaleString('en-US') ); var time = Date.now(); while (i-- > 0) { i.toString().replace( /\B(?=(?:\d{3})+$)/g, ',' ); } console.log(Date.now() - time); i = 100000; time = Date.now(); while (i-- > 0) { fn_undef.format(i); } console.log(Date.now() - time); i = 100000; time = Date.now(); while (i-- > 0) { fn_en_US.format(i); } console.log(Date.now() - time); i = 100000; time = Date.now(); while (i-- > 0) { i.toLocaleString(); } console.log(Date.now() - time); i = 100000; time = Date.now(); while (i-- > 0) { i.toLocaleString('en-US'); } console.log(Date.now() - time); 


ここでミリ秒を比較する必要がありますが、それは非常に明確です。

a。 クローム47.0.2515.0



  80 543 528 604 18699 


b。 Firefox 44.0a1



  218 724 730 439 7177 


c。 IE 11.0.14



  215 328 355 32628 37384 


後者のメソッドのChromeはNode.jsに遅れをとっており、Firefoxは同じ問題のある場所で2倍速く、IE 11では最後から2番目のメソッドの方が速度にはるかに近かった(つまり、ロケールを省略するとこのオプションはあまり保存されない) IE)。

最後に、客観性を高め、確認したい人の便宜を図るため、 jsperf.comにページ追加しました 。 私の最新版のテストでは、次のものが作成されました。

スクリーンショット







サイトはサイクルを実行する主な作業を引き受けるため、そこのコードは単純化されています。 コードを編集して、独自のテストバリアントを追加することにより、実験することができます。

PSコメントにさらに2つの方法が追加されました。 それらは、コードが大幅に大きくなりますが、多くのテストケースでは、正規表現の置換(Nodeおよびブラウザーコンソールでのテスト: onetwo )よりもさらに高速です。 7つの方法すべてを含むテストページを追加しました。 彼女は私に与えます:

スクリーンショット








PS 2さらに2つの関数が登場し、新しいテスト( onetwoを作成してjsperf.comに追加しました 。 同時に、ループからコンパイルを削除して、正規表現を使用してコードをわずかに修正しました:リテラル正規表現はループで再コンパイルされないとMDNで言われていますが、ループの外側で定義されているか(Perlで定義されている場合でもループ内で変更されない正規表現の再コンパイルを禁止する追加のフラグですが、これらの場合のJSの動作はわかりません)。 いずれにせよ、Node.jsおよびブラウザーでのテストでは、通常のループから抜け出すときに速度がわずかに向上しました。 新しいテストの結果によると、9つのメソッドのうち、新しい4つの「数学」メソッドが確実に勝ちますが、同時に、各ブラウザーで異なる「数学」メソッドが勝ちます。 私の新しい結果:

スクリーンショット







PS 3もう1つの+1関数: 新しいテーブル (既に10個のオプション)、 インジケーター

PS 4私は最も線形のオプションを追加することにしましたNumber.MAX_SAFE_INTEGERの安全な範囲で可能なすべての整数の長さを繰り返し、文字Number.MAX_SAFE_INTEGERに文字列を連結し、適切な場所にセパレータを挿入します。 これは11番目のオプションexhaustion()関数)であり、非常に高速であることが判明し、Firefoxテストでも1位になりました。

Source: https://habr.com/ru/post/J267387/


All Articles