私はJavaScriptで 型付き配列 をいじっています。
_var buffer = new ArrayBuffer(16);
var int32View = new Int32Array(buffer);
_
通常の配列(JavaScriptの_[1, 257, true])
_は、値が任意のタイプである可能性があるため、パフォーマンスが低いと思います。したがって、メモリ内のオフセットに到達することは簡単ではありません。
私は当初、JavaScript配列の添え字はオブジェクトと同じように機能し(多くの類似点があるため)、 ハッシュマップ ベースであり、ハッシュベースのルックアップが必要だと考えていました。しかし、これを確認するための信頼できる情報はあまり見つかりませんでした。
したがって、型付き配列が非常にうまく機能する理由は、常に型付きであるCの通常の配列のように機能するためだと思います。上記の最初のコード例を考えて、型付き配列の10番目の値を取得したい...
_var value = int32View[10];
_
Int32
_であるため、各値は_32
_ビットまたは_4
_バイトで構成されている必要があります。10
_です。<array offset> + (4 * 10)
であり、次に_4
_バイトを読み取って合計値を取得します。基本的には自分の仮定を確認したいだけです。これについての私の考えは正しいですか、そうでない場合は、詳しく説明してください。
V8 source をチェックして、自分で答えられるかどうかを確認しましたが、Cが錆びていて、C++にあまり詳しくありません。
型付き配列は、パフォーマンス上の理由から、WebGL標準委員会によって設計されました。通常、Javascript配列は汎用であり、オブジェクトや他の配列などを保持できます。要素は、Cの場合のように、必ずしもメモリ内でシーケンシャルである必要はありません。WebGLでは、基になるC APIが期待する方法であるため、バッファがメモリ内でシーケンシャルである必要があります。それら。型付き配列を使用しない場合、通常の配列をWebGL関数に渡すには、多くの作業が必要です。各要素を検査し、型をチェックし、それが正しい場合(floatなど)、別のシーケンシャルにコピーする必要があります。 Cのようなバッファ、次にそのシーケンシャルバッファをCAPIに渡します。痛い-たくさんの仕事!パフォーマンスに敏感なWebGLアプリケーションの場合、これによりフレームレートが大幅に低下する可能性があります。
一方、質問で示唆しているように、型付き配列は、すでに舞台裏のストレージにあるシーケンシャルCのようなバッファーを使用します。型付き配列に書き込む場合、実際には、舞台裏でCのような配列に割り当てています。 WebGLの目的では、これは、対応するCAPIがバッファーを直接使用できることを意味します。
メモリアドレスの計算だけでは不十分であることに注意してください。ブラウザ必須も境界を確認します-範囲外のアクセスを防ぐために、配列を確認してください。これはあらゆる種類のJavascript配列で発生する必要がありますが、多くの場合、巧妙なJavascriptエンジンは、インデックス値がすでに範囲内にあることを証明できる場合(0から配列の長さへのループなど)にチェックを省略できます。また、配列インデックスが実際には数値であり、文字列などではないことを確認する必要があります。しかし、本質的には、Cのようなアドレス指定を使用して説明するようになります。
しかし...それだけではありません!場合によっては、巧妙なJavascriptエンジンが通常のJavascript配列のタイプも推測できます。 V8のようなエンジンでは、通常のJavascript配列を作成し、それにfloatのみを格納すると、V8はそれがfloatの配列であると楽観的に判断し、そのために生成するコードを最適化する場合があります。その場合、パフォーマンスは型付き配列と同等になります。したがって、型付き配列は実際には最大のパフォーマンスを達成するために必要ではありません。配列を予測どおりに使用するだけで(すべての要素が同じ型で)、一部のエンジンはそのために最適化することもできます。
では、なぜ型付き配列がまだ存在する必要があるのでしょうか。
したがって、要するに、通常の配列は理論的には型付き配列と同じくらい高速です。ただし、型付き配列を使用すると、ピークパフォーマンスに到達するのがはるかに簡単になります。
はい、あなたはほとんど正しいです。標準のJavaScript配列では、JavaScriptエンジンは、配列内のデータがすべてオブジェクトであると想定する必要があります。これはCのような配列/ベクトルとして保存でき、メモリへのアクセスは説明したとおりです。問題は、データが値ではなく、その値(オブジェクト)を参照するものであるということです。
したがって、a[i] = b[i] + 2
は、エンジンに次のことを要求します。
型付き配列を使用すると、エンジンは次のことができます。
注:これらは、コンパイルされるコード(周囲のコードを含む)と問題のエンジンに依存するため、JavaScriptエンジンが実行する正確な手順ではありません。
これにより、結果の計算がはるかに効率的になります。また、型付き配列にはメモリレイアウト保証(nバイト値の配列)があるため、データ(オーディオ、ビデオなど)と直接インターフェイスするために使用できます。
パフォーマンスに関しては、物事は急速に変化する可能性があります。 AshleysBrainが言うように、VMは、通常の配列を型付き配列として迅速かつ正確に実装できると推測できるかどうかにかかっています。これは、特定のJavaScriptVMの特定の最適化に依存します。新しいブラウザバージョンで変更できます。
これChrome開発者のコメント は2012年6月の時点で機能したガイダンスを提供します:
最後の点を詳しく説明すると、この現象はJavaで何年も前から見られます。小さなコードを単独で何度も繰り返し実行して速度をテストすると、 VMは、それを最適化します。特定のテストにのみ意味のある最適化を行います。ベンチマークでは、別のプログラム内で同じコードを実行したり、比較したりする場合に比べて、速度が100倍向上します。同じコードを異なる方法で最適化するいくつかの異なるテストを実行した直後に実行します。
私はjavascriptエンジンに実際に貢献しているわけではなく、v8でいくつかの読み取り値しか持っていなかったので、私の答えは完全に真実ではないかもしれません。
配列のウェル値(穴/ギャップのない通常の配列のみ、スパースではありません。スパース配列はオブジェクトとして扱われます。)はすべてポインターまたは固定長の数値です(v8では32ビット、31ビット整数の場合)最後に0
ビットでタグ付けされています。それ以外の場合はポインタです)。
したがって、バイト数は配列全体で同じであるため、メモリ位置を見つけることはtypedArrayと何ら変わりはないと思います。ただし、オブジェクトの場合は、ボックス化解除レイヤーを1つ追加する必要があるという違いがあります。これは、通常のtypedArrayでは発生しません。
そしてもちろん、typedArraysにアクセスするとき、通常の配列が持っている型チェックは絶対にありません(ただし、ホットコード用にのみ生成される高度に最適化されたコードで削除される可能性があります)。
書き込みの場合、同じタイプであれば、それほど遅くなることはありません。別のタイプの場合、JSエンジンはそのポリモーフィックコードを生成する可能性がありますが、これは低速です。
Jsperf.comでいくつかのベンチマークを作成して確認することもできます。