Typed Arrays: Binary Data in the Browser

HTML5 Rocks

型付き配列(Typed Array)は比較的最近ブラウザに追加されたもので、WebGLで効率的にバイナリデータを扱うための方法が必要だったため生まれました。型付き配列はC言語の配列のようにメモリのバッファに対して型ビューを使ってアクセスします。型付き配列は生のメモリにデータが保存されるため、JavaScriptエンジンはネイティブライブラリに直接メモリ上のデータを渡すことができます。苦労してネイティブな表現に変換する必要はありません。結果的にWebGLや他のAPIにバイナリデータを渡す際にJavaScriptの配列を使うよりもはるかにパフォーマンスが向上します。

型付き配列ビュー(Typed Array view)はArrayBufferの断片に対する単一型の配列のように振る舞います。Float32Array、Float64Array、Int32ArrayやUint8Arrayのような自己記述的な名前を持つ全ての通常の数値型のビューが存在します。canvasのImageData(Uint8ClampedArray)のように画素配列として置き換えるための特別なビューも存在します。

DataViewは型付き配列ビューとは趣の異なるビューです。これは異なる種類のデータが含まれるようなものを扱うためのものです。DataViewオブジェクトは配列ライクなAPIの代わりに任意のバイトオフセットの任意のデータ型を読み書きするためのget/set APIを提供します。

型付き配列の使用方法の基本

型付き配列ビュー

型付き配列を使うためにはArrayBufferとビューを作成する必要があります。最も簡単な方法は、目的のサイズと型を指定して型付き配列ビューを作成することです。

// 型付き配列ビューは通常の配列のように扱えます。
var f64a = new Float64Array(8);
f64a[0] = 10;
f64a[1] = 20;
f64a[2] = f64a[0] + f64a[1];

型付き配列ビューには複数の異なった型が存在します。それぞれの型で共通のAPIが使用されているので、一つの型についての使用方法を知ることで他の全ての型の使用方法を知ることができます。次の例では現在存在する型付き配列ビューを作成しています。

// 浮動小数点型の配列
var f64 = new Float64Array(8);
var f32 = new Float32Array(16);

// 符号付き整数の配列
var i32 = new Int32Array(16);
var i16 = new Int16Array(32);
var i8 = new Int8Array(64);

// 符号なし整数の配列
var u32 = new Uint32Array(16);
var u16 = new Uint16Array(32);
var u8 = new Uint8Array(64);
var pixels = new Uint8ClampedArray(64);

最後のものは少し特殊で、入力値を0から255の間に固定(0以下の数値を0、255以上の数値を255に)します。これは特に画像処理アルゴリズムを扱うときに便利で、8ビットの範囲からオーバーフローした値を手動で修正する必要がなくなります。

Uint8Arrayに格納された画像データにガンマ係数を適用する例です。あまり格好がよくないですね:

u8[i] = Math.min(255, Math.max(0, u8[i] * gamma));

Uint8ClampedArrayではこの処理を省略できます:

pixels[i] *= gamma;

別の型付き配列ビューを作成する方法は、最初にArrayBufferを作成しておき、それを指すビューを作成することです。通常、ArrayBufferとして外部データを扱うようなAPIに対して型付き配列ビューを作成する場合に使用されます。

var ab = new ArrayBuffer(256); // 256-byte ArrayBuffer.
var faFull = new Uint8Array(ab);
var faFirstHalf = new Uint8Array(ab, 0, 128);
var faThirdQuarter = new Uint8Array(ab, 128, 64);
var faRest = new Uint8Array(ab, 192);

また、1つのArrayBufferに対して複数のビューを持つことができます。

var fa = new Float32Array(64);
var ba = new Uint8Array(fa.buffer, 0, Float32Array.BYTES_PER_ELEMENT); // First float of fa.

ある型付き配列から別の型付き配列にコピーするには、型付き配列のsetメソッドを使うと最速で行えます。memcpyライクに使用するためには、Uint8Arrayのビューを作成し、setメソッドでデータをコピーするようにします。

function memcpy(dst, dstOffset, src, srcOffset, length) {
  var dstU8 = new Uint8Array(dst, dstOffset, length);
  var srcU8 = new Uint8Array(src, srcOffset, length);
  dstU8.set(srcU8);
};

DataView

異なる種類の型データが含まれているArrayBufferを使う最も簡単な方法はDataViewを使用することです。ヘッダーとして1個の8ビット符号なし整数、2個の16ビット符号なし整数、本体データとして32ビット浮動小数点数の配列を持つファイル形式があったとします。型付き配列ビューだけを使用して読み込むことはできますが、これは少々面倒です。DataViewでヘッダーを、本体のfloat型の配列については型付き配列ビューを使用してみましょう。

var dv = new DataView(buffer);
var vector_length = dv.getUint8(0);
var width = dv.getUint16(1); // 0+uint8 = 1 bytes offset
var height = dv.getUint16(3); // 0+uint8+uint16 = 3 bytes offset
var vectors = new Float32Array(width*height*vector_length);
for (var i=0, off=5; i<vectors.length; i++, off+=4) {
  vectors[i] = dv.getFloat32(off);
}

上記の例では、全ての値はビッグエンディアンで読み込まれます。バッファ内の値がリトルエンディアンの場合はgetterに対してオプションでlittleEndianパラメータを設定することができます:

...
var width = dv.getUint16(1, true);
var height = dv.getUint16(3, true);
...
vectors[i] = dv.getFloat32(off, true);
...

型付き配列ビューは常にネイティブのバイトオーダーになることに注意してください。これにより高速で処理することができます。エンディアンが問題になるようなデータを読み書きする場合はDataViewを使用する必要があります。

また、DataViewはバッファに値を書き込むためのメソッドを持っています。setterは"set"とそれに続くデータ型のように、getterと同じ方法で命名されています。

dv.setInt32(0, 25, false); // set big-endian int32 at byte offset 0 to 25
dv.setInt32(4, 25); // set big-endian int32 at byte offset 4 to 25
dv.setFloat32(8, 2.5, true); // set little-endian float32 at byte offset 8 to 2.5

エンディアンについての議論

エンディアンやバイトオーダーというのはマルチバイトの数値がコンピュータのメモリに格納される順序のことです。ビッグエンディアンは最上位バイトが最初に格納されるCPUアーキテクチャです。リトルエンディアンでは最下位バイトが最初に格納されます。CPUアーキテクチャによってどのエンディアンを選ぶかは完全に任意で、いずれか1つを選択すると良い理由があります。実際には、ビッグエンディアンとリトルエンディアンの両方のデータをサポートするように設定できるCPUもあります。

なぜエンディアンついて心配をする必要があるのでしょうか?理由は簡単です。ディスクやネットワークからデータを読み書きするときエンディアンを指定する必要があります。これはCPUのエンディアンに関係なく正しくデータが解釈されていることを保証します。ますますネットワーク化された世界では、ネットワーク上のサーバや他のクライアントから流れてくるバイナリデータに対して全てのビッグまたはリトルエンディアンのデバイスで適切に動作することが不可欠です。

DataViewのインターフェースは特にファイルやネットワークからのデータの読み書きするために設計されています。DataViewは指定されたエンディアンのデータ上で操作します。全ての値の全てのアクセスで、ビッグエンディアン、リトルエンディアンのどちらを使うのかは決めておかなければいけません。動作しているブラウザのCPUのエンディアンを問わずバイナリデータを読み書きする場合は一貫性と正しい結果を保証しなければいけません。

通常、アプリケーションがサーバからバイナリデータを読み込むときに、アプリケーション内で使用するためのデータ構造に変換するためには一度全体をスキャンする必要があります。DataViewはこの変換処理で使用するべきものです。XMLHttpRequest、FileReaderや他のinput/output APIでマルチバイトの型付き配列ビュー(Int16Array, Uint16Arrayなど)を使用することはお勧めしません。なぜなら型付き配列ビューはCPUネイティブのエンディアンを使用するからです。これについては後で詳しく説明します。

2つの簡単な例を見てみましょう。Windows BMPフォーマットはWindows初期の画像を格納するための標準的なフォーマットとして使用されていました。リンク先のドキュメントによると、ファイル内の整数は全てリトルエンディアンで格納されていると書かれています。次の例はDataStream.jsを用いてBMPファイルのヘッダーを読み込むコードのスニペットの最初の部分です。

function parseBMP(arrayBuffer) {
  var stream = new DataStream(arrayBuffer, 0,
    DataStream.LITTLE_ENDIAN);
  var header = stream.readUint8Array(2);
  var fileSize = stream.readUint32();
  // Skip the next two 16-bit integers
  stream.readUint16();
  stream.readUint16();
  var pixelOffset = stream.readUint32();
  // Now parse the DIB header
  var dibHeaderSize = stream.readUint32();
  var imageWidth = stream.readInt32();
  var imageHeight = stream.readInt32();
  // ...
}

これは別の例です。WebGL samples projectHigh Dynamic Range rendering demoからのものです。このデモではハイダイナミックレンジテクスチャを表す生のリトルエンディアンの浮動小数点数データをダウンロードし、WebGLにデータを渡す必要があります。これは全てのCPUアーキテクチャで浮動小数点数の値を適切に解釈するコードのスニペットです。変数“arrayBuffer”はXMLHttpRequestを介してダウンロードされたArrayBufferとします:

var arrayBuffer = ...;
var data = new DataView(arrayBuffer);
var tempArray = new Float32Array(
  data.byteLength / Float32Array.BYTES_PER_ELEMENT);
var len = tempArray.length;
// Incoming data is raw floating point values
// with little-endian byte ordering.
for (var jj = 0; jj < len; ++jj) {
  tempArray[jj] =
    data.getFloat32(jj * Float32Array.BYTES_PER_ELEMENT, true);
}
gl.texImage2D(...other arguments...,
  gl.RGB, gl.FLOAT, tempArray);

大まかには次のとおりです。Webサーバからバイナリデータを受信し、DataViewを通します。個々の数値を読み込み、JavaScriptオブジェクト(構造化された小さなデータの場合)や型付き配列ビュー(大きなデータブロックの場合)に格納します。これによりあなたのコードが全ての種類のCPUで正しく動作することが保証されます。また、ファイルに書き込んだりネットワークで転送するためにDataViewを使用します。そして、ファイルを生成したり使用するために忘れずにsetメソッド(setUint16、setFloat32など)に適切にlittleEndian引数を与える必要があります。

覚えておいて欲しいのですが、ネットワーク上でやり取りされる全てのデータは暗黙的にフォーマットとエンディアン(少なくとも任意のマルチバイトの値)を持っています。アプリケーションがネットワーク経由で送信する全てのデータの形式を明確に定義、検証する必要があります。

型付き配列を使用するブラウザAPI

これより型付き配列を使用している別のブラウザAPIの概要について説明します。型付き配列はWebGL,Canvas,Web Audio API,XMLHttpRequests,WebSockets,Web Workers,Media Source API,File APIsで使用されています。このリストから効率的な方法でデータを渡すだけでなく、パフォーマンス重視のマルチメディアでの動作にも適していることがわかります。

WebGL

型付き配列が最初に使用されたのはWebGLです。バッファと画像のデータを受け渡しするために使用されました。WebGLバッファオブジェクトの内容を設定するためにはgl.bufferData()を型付き配列と共に使用します。

var floatArray = new Float32Array([1,2,3,4,5,6,7,8]);
gl.bufferData(gl.ARRAY_BUFFER, floatArray);

型付き配列はテキスチャデータを渡すためにも使用されます。これは型付き配列を使用してテクスチャの内容を渡す基本的な例です。

var pixels = new Uint8Array(16*16*4); // 16x16 RGBA image
gl.texImage2D(
  gl.TEXTURE_2D, // target
  0, // mip level
  gl.RGBA, // internal format
  16, 16, // width and height
  0, // border
  gl.RGBA, //format
  gl.UNSIGNED_BYTE, // type
  pixels // texture data
);

また、WebGLのコンテキストからピクセルデータを読み込むためには型付き配列が必要です。

var pixels = new Uint8Array(320*240*4); // 320x240 RGBA image
gl.readPixels(0, 0, 320, 240, gl.RGBA, gl.UNSIGNED_BYTE, pixels);

Canvas 2D

最近、Canvas ImageDataオブジェクトは型付き配列の仕様で動作するようになりました。現在、型付き配列で表されたcanvas要素上のピクセルデータを取得することができます。canvas要素周辺をいじる必要がなくcanvasのピクセル列を生成したり編集することができるのでこれは便利です。

var imageData = ctx.getImageData(0,0, 200, 100);
var typedArray = imageData.data // data is a Uint8ClampedArray

XMLHttpRequest2

XMLHttpRequestと型付き配列の関係が強まりました。現在、JavaScriptの文字列をパースせず直接型付き配列を受信することができます。これはネットワークから取得したデータを直接マルチメディア関連のAPIに渡したり、取得したバイナリファイルをパースする処理を本当に綺麗に行うことができます。

あなたがしなくてはいけないことはXMLHttpRequestオブジェクトのresponseTypeプロパティに'arraybuffer'を設定することだけです。

xhr.responseType = 'arraybuffer';

ネットワークからデータをダウンロードするときはエンディアンの問題を思い出してください!上記、エンディアンの章を参照してください。

File APIs

FileReaderではArrayBufferとしてファイルの内容を読み込むことができます。読み込んだ内容を操作するために型付き配列ビューやDataViewを使用することができます。

reader.readAsArrayBuffer(file);

ここだけでなく、エンディアンのことについて心にとめておく必要があります。詳細はエンディアンの章をチェックしてください。

Transferableオブジェクト

postMessage内のTransferableオブジェクトはバイナリデータを他のウィンドウやWeb Workerに高速で渡すことができます。TransferableとしてWorkerにオブジェクトを送信したとき、オブジェクトは送信側からはアクセスできなくなり、受信したWorkerが所有権を取得します。これは受信側に型付き配列の所有権を転送するだけで、データ自身はコピーされないような高度に最適化された実装が行われています。

Web WorkerでTransferableオブジェクトを使用するにはworker上でwebkitPostMessageメソッドを使う必要があります。webkitPostMessageメソッドはpostMessageのように動作します。しかし、1つだけでなく2つの引数を取ります。2つめの引数にはworkerに転送したいオブジェクトの配列を指定します。

worker.webkitPostMessage(oneGBTypedArray, [oneGBTypedArray]);

workerから転送したオブジェクトを取得するために、workerは同じ方法を使ってメインスレッドに転送することができます。

webkitPostMessage({results: grand, youCanHaveThisBack: oneGBTypedArray}, [oneGBTypedArray]);

コピーなしです、素晴らしい!

Media Source API

最近ではメディア要素もMedia Source APIからいくつかの型付き配列の恩恵を得ました。webkitSourceAppendメソッドを使ってvideo要素に直接ビデオデータを含んだ型付き配列を渡すことができます。これはvideo要素の既存のビデオの後にビデオデータを追加することができます。SourceAppendは素晴らしいです。単一のvideo要素を使用して複数のビデオの再生、インターステーシャル、プレイリスト、ストリーミングなどを行うことができます。

video.webkitSourceAppend(uint8Array);

Binary WebSockets

また、全てのデータを文字列化することを避けるためにWebSocketで型付き配列を使用することができます。効率的なプロトコルを書くこととネットワークトラフィックを最小化することに対して最適な方法です。

socket.binaryType = 'arraybuffer';

ふー(一息)!APIのレビューがまとまりました。次は、型付き配列を扱うサードパーティライブラリについて見ていきましょう。

Third-party libraries

jDataView

jDataViewは全てのブラウザのためのDataViewの実装です。DataViewはWebkitのみの機能でしたが、現在は他のほとんどのブラウザでサポートされています。モジラの開発者チームはDataViewをFirefoxでも使えるようにするためにパッチをしている途中です。

Chrome開発関係のチームのEric Bidelmanはsmall MP3 ID3 tag reader exampleでjDataViewを使用しています。これはブログ記事からの使用例です:

var dv = new jDataView(arraybuffer);

// "TAG" starts at byte -128 from EOF.
// See http://en.wikipedia.org/wiki/ID3
if (dv.getString(3, dv.byteLength - 128) == 'TAG') {
  var title = dv.getString(30, dv.tell());
  var artist = dv.getString(30, dv.tell());
  var album = dv.getString(30, dv.tell());
  var year = dv.getString(4, dv.tell());
} else {
  // no ID3v1 data found.
}

stringencoding

型付き配列内の文字列を操作するのは少々面倒です。しかし、stringencoding libraryを使用すると楽に操作できます。Typed Array string encoding specの提案を実装していて、どのようなものか感触をつかむのにも良い方法です。

これはstringencodingの基本的な使用例です:

var uint8array = new TextEncoder(encoding).encode(string);
var string = new TextDecoder(encoding).decode(uint8array);

BitView.js

私は以前BitView.jsという型付き配列のための小さなビット操作ライブラリを書きました。名前のとおり、ビットで動作するのを除いてDataViewと同じように動作します。BitViewではビットオフセットを与えてArrayBufferからビットの値を読み書きすることができます。また、任意のビットオフセットで6ビットや12ビットの整数を格納、ロードすることができます。

ディスプレイは長い方の辺でも大抵は4096ピクセル以下です。12ビットの整数はスクリーンの座標を扱うのに適しています。32ビットの整数の代わりに12ビットの整数を使用すると62%のサイズ削減になります。極端な例ですが、私は64ビットの座標を使用したシェイプファイルを扱っていました。しかし、モデルはスクリーンサイズで表示させるだけだったのでそのような精度は必要ありませんでした。6ビット差分を用いて元の座標を12ビットベースの座標に変換するとファイルサイズを10分の1に削減出来ました。デモはここから見ることができます。これはBitView.jsの基本的な使用例です:

var bv = new BitView(arrayBuffer);
bv.setBit(4, 1); // Set fourth bit of arrayBuffer to 1.
bv.getBit(17); // Get 17th bit of arrayBuffer.

bv.getBit(50*8 + 3); // Get third bit of 50th byte in arrayBuffer.

bv.setInt6(3, 18); // Write 18 as a 6-bit int to bit position 3 in arrayBuffer.
bv.getInt12(9); // Read a 12-bit int from bit position 9 in arrayBuffer.

DataStream.js

型付き配列の最もエキサイティングなことの1つはJavaScriptでより簡単にバイナリファイルを扱うことができる点です。文字列を1文字ずつパースし、手動でバイナリの数値に変換する代わりに、現在ではXMLHttpRequestでArrayBufferを取得し、直接DataViewで処理することができます。これより、例えばMP3ファイルをロードしてオーディオプレーヤーで使うためのメタデータタグを読み込むようなことが簡単になります。また、シェイプファイルをロードしてWebGLモデルに変換したり、JPEGからEXIFデータを読み込んでスライドショーアプリに表示するようなことも簡単に行えるようになるでしょう。

ただ、ArrayBuffer XHRsではバッファから構造体のようなデータを読み込もうとするときに少々面倒です。DataViewはエンディアンセーフに数値を読み込むのに、型付き配列は単一型のネイティブなエンディアンの数値の配列を読み込むのに適しています。不足しているとすれば、エンディアンセーフな方法でデータの配列や構造体を読み込む方法です。DataStream.jsを見てみましょう。

DataStream.jsはArrayBufferからスカラ値、文字列、配列、構造体をファイルのような方法で読み書きする型付き配列のライブラリです。

ArrayBufferから浮動小数点数の配列を読み込む例です:

// without DataStream.js
var dv = new DataView(buffer);
var f32 = new Float32Array(buffer.byteLength / 4);
var littleEndian = true;
for (var i = 0; i<f32.length; i++) {
  f32[i] = dv.getFloat32(i*4, littleEndian);
}

// with DataStream.js
var ds = new DataStream(buffer);
ds.endianness = DataStream.LITTLE_ENDIAN;
var f32 = ds.readFloat32Array(ds.byteLength / 4);

DataStream.jsはより複雑なデータを読み込むときには本当に便利です。JPEGマーカーを読み込むメソッドがあったとします:

// without DataStream.js
var dv = new DataView(buffer);
var objs = [];
for (var i=0; i<buffer.byteLength;) {
  var obj = {};
  obj.tag = dv.getUint16(i);
  i += 2;
  obj.length = dv.getUint16(i);
  i += 2;
  obj.data = new Uint8Array(obj.length - 2);
  for (var j=0; j<obj.data.length; j++,i++) {
    obj.data[j] = dv.getUint8(i);
  }
  objs.push(obj);
}

// with DataStream.js
var ds = new DataStream(buffer);
ds.endianness = ds.BIG_ENDIAN;
var objs = [];
while (!ds.isEof()) {
  var obj = {};
  obj.tag = ds.readUint16();
  obj.length = ds.readUint16();
  obj.data = ds.readUint8Array(obj.length - 2);
  objs.push(obj);
}

または、DataStream.readStructメソッドでも構造体を読み込むことができます。readStructメソッドは構造体を定義する配列(構造体メンバの型を含んでいます)を引数に取ります。なお、コールバック関数を使用して、複合的な型やデータの配列、ネストされた構造体を扱ったりすることができます。

// with DataStream.readStruct
ds.readStruct([
  'objs', ['[]', [ // objs: array of tag,length,data structs
    'tag', 'uint16',
    'length', 'uint16',
    'data', ['[]', 'uint8', function(s,ds){ return s.length - 2; }], // get length with a function
  '*'] // read in as many struct as there are
]);

見てわかるように、構造体の定義は[名前、型]のペアのフラットな配列です。ネストされた構造体は型の部分に配列を指定することによって定義されます。配列(構造体の一部として読み込むための)は3つの要素を持つ配列で定義されます。2つ目の要素は配列の要素の型、3個目は配列の長さです(数値か前のフィールドの参照かコールバック関数)。最初の要素は配列の定義では使用されません。

指定できる型については以下を参照してください:

数値型

サフィックスなしの数値型はDataStreamのエンディアンを使用します。
明示的にエンディアンを指定するには、
リトルエンディアンでは'le'、ビッグエンディアンでは'ge'というサフィックスをつけます。
例) 'int32be'はビッグエンディアンの32ビット整数です。

  'uint8' -- 8ビット符号なし整数
  'uint16' -- 16ビット符号なし整数
  'uint32' -- 32ビット符号なし整数
  'int8' -- 8ビット整数
  'int16' -- 16ビット整数
  'int32' -- 32ビット整数
  'float32' -- 32ビット浮動小数点数
  'float64' -- 64ビット浮動小数点数

文字列型

  'cstring' -- ゼロ終端ASCII文字列
  'string:N' -- N文字のASCII文字列
  'string,CHARSET:N' -- エンコードがCHARSETであるNバイトの文字列
  'u16string:N' -- DataStreamのエンディアンのN文字のUCS-2文字列
  'u16stringle:N' -- リトルエンディアンのN文字のUCS-2文字列
  'u16stringbe:N' -- ビッグエンディアンのN文字のUCS-2文字列

複合型

  [name, type, name_2, type_2, ..., name_N, type_N] -- 構造体

  function(dataStream, struct) {} -- データを読み込んだり返したりするコールバック関数

  {get: function(dataStream, struct) {}, set: function(dataStream, struct) {}}
  -- データを読み書きするためのGetter/setter関数です。
     同じ構造体定義内で読み書きの両方をするときに便利です。

  ['', type, length] -- 与えられた型(type)と長さ(length)を持つ配列です。
                        lengthは数値や前に読み込まれたフィールドを参照する文字列や
                        コールバック関数(function(struct, dataStream, type){})を
                        指定することができます。
                        もし、lengthに‘*’がセットされている場合、
                        DataStreamの読み込みが失敗するまで要素を読み込み続けます。

JPEGのメタデータを読み込む例をここから見ることができます。デモではJPEGファイル(一部EXIFのパースも含まれます)のタグレベルの構造を読み込むためにDataStream.jsを使用しています。また、jpg.jsのようにJPEGイメージをJavaScriptでデコードして表示するライブラリもあります。

型付き配列の歴史

型付き配列はWebGLの初期の実装段階に登場しました。JavaScriptの配列をグラフィックドライバに渡すときにパフォーマンスの問題を引き起すことがわかっていました。JavaScriptの配列を使用するとWebGLへの束縛はネイティブの配列を割り当てて、JavaScriptの配列を走査し、そして全ての配列内のJavaScriptオブジェクトをネイティブの型にキャストする必要がありました。

データ変換のボトルネックを解決するためにモジラのVladimir VukicevicはCanvasFloatArrayを書きました。これはJavaScriptインターフェースのC言語スタイルの浮動小数点数の配列です。現在、JavaScriptのCanvasFloatArrayを編集したり、束縛の中で何か特別なことをしなくても直接WebGLに渡すことができます。CanvasFloatArrayはWebGLFloatArray、Float32Arrayへと名前が変更されました。そして、バックエンドのArrayBuffer、バッファにアクセスするためのFloat32Arrayビューに分けられました。型は他の整数や浮動小数点数のサイズや符号付き/符号なしのものが加えられました。

設計の考慮事項

はじめから型付き配列の設計はネイティブライブラリにバイナリデータを効率的に渡す必要性に牽引されました。このような理由から、型付き配列ビューはホストCPUのネイティブエンディアンのデータ上で動作します。これらの決定はJavaScriptがグラフィックカードに頂点データを送信するときなどの操作時に最大のパフォーマンスを達成することを可能にします。

DataViewは特にファイル、ネットワークI/Oのように常に指定されたエンディアンを持つデータのために設計されています。そして、最大のパフォーマンスを得るためのアライメントがされてない場合があります。

設計はインメモリデータアセンブリ(型付き配列ビュー)とI/O(DataView)の間で分けられています。モダンJavaScriptエンジンは型付き配列ビューを重点的に最適化して、数値演算において高いパフォーマンスを実現しています。現在の型付き配列ビューのパフォーマンスレベルはこの設計上の決定によって可能になりました。

参考

Comments

0