型付き配列 - ブラウザのバイナリデータ

Ilmari Heikkinen

はじめに

ブラウザに追加された型付き配列は比較的最近のもので、WebGL でバイナリデータを効率的に処理する必要性から生まれました。型付き配列は、C での配列の仕組みと同様に、型付きビューがあるメモリのスラブです。型付き配列は未加工のメモリを基盤としているため、JavaScript エンジンはデータをネイティブ表現に慎重に変換することなく、メモリをネイティブ ライブラリに直接渡すことができます。そのため、バイナリ データを扱う WebGL などの API にデータを渡す場合、型付き配列は JavaScript 配列よりもはるかに優れたパフォーマンスを発揮します。

型付き配列ビューは、ArrayBuffer のセグメントに対する単一型配列のように機能します。通常の数値型にはすべて、Float32Array、Float64Array、Int32Array、Uint8Array などのわかりやすい名前のビューがあります。Canvas の ImageData のピクセル配列型を置き換える特別なビュー Uint8ClampedArray もあります。

DataView は 2 番目のタイプのビューで、異種データを処理するためのものです。DataView オブジェクトには、配列のような API ではなく、任意のバイト オフセットで任意のデータ型を読み取り、書き込むための get/set API が用意されています。DataView は、ファイル ヘッダーなどの構造体に似たデータの読み取りと書き込みに最適です。

型付き配列の使用の基本

型付き配列ビュー

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

// Typed array views work pretty much like normal arrays.
var f64a = new Float64Array(8);
f64a[0] = 10;
f64a[1] = 20;
f64a[2] = f64a[0] + f64a[1];

型付き配列ビューにはいくつかの種類があります。これらはすべて同じ API を共有しているため、いずれかの方法を把握すれば、すべての使用方法もほぼ理解できます。以下の例では、既存の型付き配列ビューをそれぞれ 1 つずつ作成します。

// Floating point arrays.
var f64 = new Float64Array(8);
var f32 = new Float32Array(16);

// Signed integer arrays.
var i32 = new Int32Array(16);
var i16 = new Int16Array(32);
var i8 = new Int8Array(64);

// Unsigned integer arrays.
var u32 = new Uint32Array(16);
var u16 = new Uint16Array(32);
var u8 = new Uint8Array(64);
var pixels = new Uint8ClampedArray(64);

最後の 1 つは少し特殊なもので、入力値を 0 ~ 255 の範囲でクランプします。これは、8 ビット範囲のオーバーフローを回避するために画像処理の計算を手動でクランプする必要がないため、Canvas 画像処理アルゴリズムには特に便利です。

たとえば、Uint8Array に保存されている画像にガンマ係数を適用する方法は次のとおりです。あまりきれいではない:

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

Uint8ClampedArray を使用すると、手動クランプをスキップできます。

pixels[i] *= gamma;

型付き配列ビューを作成するには、ArrayBuffer を作成してから、それを指すビューを作成することもできます。外部データを取得する API は通常 ArrayBuffer を扱うため、この方法で型付き配列ビューを取得します。

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);

同じ 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 を使用することです。8 ビットの符号なし整数を含むヘッダー、2 つの 16 ビット整数、32 ビット浮動小数点数のペイロード配列が続くファイル形式があるとします。型付き配列ビューでこれを読み取ることは可能ですが、少し面倒です。DataView を使用すると、ヘッダーを読み取り、浮動小数点数配列に型付き配列ビューを使用できます。

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);
}

上記の例では、読み取った値はすべてビッグ エンディアンです。バッファ内の値がリトル エンディアンの場合、オプションの lowEndian パラメータをゲッターに渡すことができます。

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

型付き配列ビューは常にネイティブ バイト順になります。これは処理を高速化するためです。エンディアンが問題になる場合は、DataView を使用してデータを読み書きする必要があります。

DataView には、バッファに値を書き込むメソッドもあります。これらのセッターは、ゲッターと同じように「set」にデータ型を続ける形で名前が付けられます。

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 アーキテクチャで使用されるエンディアンは完全に任意です。どちらかを選択する理由は十分にあります。実際、一部の CPU は、ビッグエンディアン データとリトルエンディアン データの両方をサポートするように構成できます。

エンディアンを考慮する必要があるのはなぜですか?理由は単純です。ディスクまたはネットワークからデータを読み書きする場合は、データのエンディアンスを指定する必要があります。これにより、データを処理する CPU のエンディアン形式に関係なく、データが正しく解釈されます。ネットワーク化が進む世界では、ネットワーク上のサーバーや他のピアから受信するバイナリ データを処理する必要がある可能性のある、ビッグエンディアンまたはリトルエンディアンなど、あらゆる種類のデバイスを適切にサポートすることが重要です。

DataView インターフェースは、ファイルとネットワークとの間でデータを読み書きするように特別に設計されています。DataView は、指定されたエンディアンのデータに対して動作します。バイナリ データの読み取りや書き込みを行うたびに、大か小のエンディアンを指定し、ブラウザが実行されている CPU のエンディアンに関係なく、一貫した正しい結果が得られるようにする必要があります。

通常、アプリケーションがサーバーからバイナリデータを読み取る場合、アプリケーションが内部で使用するデータ構造に変換するために、データを一度スキャンする必要があります。このフェーズでは DataView を使用する必要があります。XMLHttpRequest、FileReader、その他の入出力 API を介してフェッチされたデータに対してマルチバイト型配列ビュー(Int16Array、Uint16Array など)を直接使用するのはおすすめしません。この型付き配列ビューは、CPU のネイティブ エンディアンを使用するためです。詳しくは後で説明します。

簡単な例をいくつか見てみましょう。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 サンプル プロジェクトハイ ダイナミック レンジ レンダリング デモに掲載されています。このデモでは、ハイ ダイナミック レンジ テクスチャを表す未加工のリトル エンディアン浮動小数点データをダウンロードして、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);

大まかな目安としては、ウェブサーバーからバイナリ データを受信したら、DataView で 1 回スキャンします。個々の数値を読み取り、JavaScript オブジェクト(少量の構造化データの場合)または型付き配列ビュー(大きなデータブロックの場合)といった他のデータ構造に保存します。これにより、あらゆる種類の CPU でコードが適切に機能するようになります。また、DataView を使用してファイルまたはネットワークにデータを書き込み、作成または使用しているファイル形式を生成するために、さまざまな set メソッドに littleEndian 引数を適切に指定してください。

ネットワークを介して送信されるすべてのデータには、(少なくともマルチバイト値の場合)暗黙的に形式とエンディアンがあります。アプリケーションがネットワーク経由で送信するすべてのデータの形式を明確に定義し、記録してください。

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

ここでは、現在型付き配列を使用しているさまざまなブラウザ API について簡単に説明します。現在のクロップには、WebGL、Canvas、Web Audio API、XMLHttpRequest、WebSocket、Web Worker、Media Source API、File API が含まれます。API のリストから、型付き配列はパフォーマンスに敏感なマルチメディア処理や、データを効率的にやり取りするのに適していることがわかります。

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 オブジェクトが Typed Arrays 仕様に対応しました。これにより、キャンバス要素上のピクセルの Typed Arrays 表現を取得できるようになりました。キャンバス要素を操作せずにキャンバス ピクセル配列を作成、編集できるため、これは便利です。

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';

ネットワークからデータをダウンロードする際は、エンディアンの問題に注意する必要があります。上記のエンディアンの説明をご覧ください。

ファイル API

FileReader は、ファイルの内容を ArrayBuffer として読み取ることができます。次に、型付き配列ビューと DataView をバッファに接続して、その内容を操作できます。

reader.readAsArrayBuffer(file);

ここでもエンディアンに留意する必要があります。詳しくは、エンディアンに関するセクションをご覧ください。

移行可能なオブジェクト

postMessage の transferable オブジェクトを使用すると、バイナリ データを他のウィンドウや Web Worker に渡す速度が大幅に向上します。オブジェクトを Transferable として Worker に送信すると、送信スレッドではオブジェクトにアクセスできなくなり、受信側の Worker がオブジェクトの所有権を取得します。これにより、送信されたデータがコピーされず、Typed Array の所有権のみがレシーバに転送される、高度に最適化された実装が可能になります。

Web ワーカーで Transferable オブジェクトを使用するには、ワーカーで webkitPostMessage メソッドを使用する必要があります。webkitPostMessage メソッドは postMessage と同じように動作しますが、1 つではなく 2 つの引数を受け取ります。追加される 2 番目の引数は、ワーカーに転送するオブジェクトの配列です。

worker.webkitPostMessage(oneGBTypedArray, [oneGBTypedArray]);

ワーカーからオブジェクトを取得するには、ワーカーが同じ方法でオブジェクトをメインスレッドに渡します。

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

コピーはゼロです。

Media Source API

最近、メディア要素にも Media Source API の形で、型付き配列の利点がもたらされました。webkitSourceAppend を使用して、動画データを含む Typed Array を動画要素に直接渡すことができます。これにより、video 要素は既存の動画の後に動画データを追加します。SourceAppend は、インタースティシャル、再生リスト、ストリーミングなど、1 つの動画要素を使用して複数の動画の再生を希望する場合に最適です。

video.webkitSourceAppend(uint8Array);

バイナリ WebSocket

WebSockets で型付き配列を使用すると、すべてのデータを文字列に変換する必要がなくなります。効率的なプロトコルを記述し、ネットワーク トラフィックを最小限に抑えるのに適しています。

socket.binaryType = 'arraybuffer';

ふう!これで API の審査は完了です。次に、型付き配列を処理するためのサードパーティ ライブラリについて説明します。

サードパーティ ライブラリ

jDataView

jDataView は、すべてのブラウザ用に DataView シムを実装します。DataView は以前は WebKit 専用の機能でしたが、現在は他のほとんどのブラウザでサポートされています。Mozilla のデベロッパー チームは、Firefox でも DataView を有効にするためのパッチをリリースする準備を進めています。

Chrome Developer Relations チームの Eric Bidelman は、jDataView を使用する小さな MP3 ID3 タグ リーダーの例を作成しました。以下に、ブログ投稿の使用例を示します。

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 ライブラリが役に立ちます。Stringencoding は、提案されている 型付き配列の文字列エンコード仕様を実装しているため、今後の機能の概要を把握するのにも適しています。

stringencoding の基本的な使用例を次に示します。

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

BitView.js

型付き配列用の BitView.js という小さなビット操作ライブラリを作成しました。名前が示すように、ビットを使用する点を除き、DataView とよく似ています。BitView を使用すると、ArrayBuffer 内の特定のビットオフセットにあるビットの値を取得して設定できます。BitView には、任意のビット オフセットで 6 ビットおよび 12 ビットの整数を保存および読み込むメソッドもあります。

ディスプレイの長辺は 4, 096 ピクセル未満であることが多いため、12 ビットの整数は画面座標の操作に適しています。32 ビットの int ではなく 12 ビットの int を使用すると、サイズが 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 文字ずつ解析して手動で文字を 2 進数に変換する代わりに、XMLHttpRequest で ArrayBuffer を取得し、DataView を使って直接処理できるようになりました。これにより、たとえば MP3 ファイルを読み込んだり、オーディオ プレーヤーで使用するメタデータタグを読み取ったりするのが簡単になります。または、シェープファイルを読み込んで WebGL モデルに変換します。また、JPEG から EXIF タグを読み取って、スライドショー アプリに表示することもできます。

ArrayBuffer XHR の問題は、バッファから構造体のようなデータを読み取るのが少し面倒なことです。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
]);

ご覧のとおり、構造体の定義は [name, type] ペアのフラットな配列です。ネストされた構造体は、型の配列を持つことで実現します。配列は、3 要素の配列を使用して定義されます。2 番目の要素は配列要素の型で、3 番目の要素は配列の長さ(数値、以前に読み取ったフィールドへの参照、またはコールバック関数)です。配列定義の最初の要素は使用されません。

type に指定できる値は次のとおりです。

Number types

Unsuffixed number types use DataStream endianness.
To explicitly specify endianness, suffix the type with
'le' for little-endian or 'be' for big-endian,
e.g. 'int32be' for big-endian int32.

  'uint8' -- 8-bit unsigned int
  'uint16' -- 16-bit unsigned int
  'uint32' -- 32-bit unsigned int
  'int8' -- 8-bit int
  'int16' -- 16-bit int
  'int32' -- 32-bit int
  'float32' -- 32-bit float
  'float64' -- 64-bit float

String types

  'cstring' -- ASCII string terminated by a zero byte.
  'string:N' -- ASCII string of length N.
  'string,CHARSET:N' -- String of byteLength N encoded with given CHARSET.
  'u16string:N' -- UCS-2 string of length N in DataStream endianness.
  'u16stringle:N' -- UCS-2 string of length N in little-endian.
  'u16stringbe:N' -- UCS-2 string of length N in big-endian.

Complex types

  [name, type, name_2, type_2, ..., name_N, type_N] -- Struct

  function(dataStream, struct) {} -- Callback function to read and return data.

  {get: function(dataStream, struct) {}, set: function(dataStream, struct) {}}
  -- Getter/setter functions to reading and writing data. Handy for using the
     same struct definition for both reading and writing.

  ['', type, length] -- Array of given type and length. The length can be either
                        a number, a string that references a previously-read
                        field, or a callback function(struct, dataStream, type){}.
                        If length is set to '*', elements are read from the
                        DataStream until a read fails.

JPEG メタデータを読み取る実際の例については、こちらをご覧ください。このデモでは、DataStream.js を使用して JPEG ファイルのタグレベルの構造を読み取り(一部の EXIF 解析とともに)、jpg.js を使用して JPEG 画像をデコードして JavaScript で表示します。

型付き配列の歴史

型付き配列は、JavaScript 配列をグラフィック ドライバに渡すことがパフォーマンスの問題を引き起こしていることが判明した、WebGL の実装初期段階で始まりました。JavaScript 配列の場合、WebGL バインディングはネイティブ配列を割り当て、JavaScript 配列を走査して配列内のすべての JavaScript オブジェクトを必要なネイティブ型にキャストすることで、その配列を埋める必要がありました。

データ変換のボトルネックを解消するため、Mozilla の Vladimir Vukicevic 氏は、JavaScript インターフェースを備えた C スタイルの浮動小数点配列である CanvasFloatArray を作成しました。これで、バインディングで余分な作業を行うことなく、JavaScript で CanvasFloatArray を編集して WebGL に直接渡すことができます。その後の反復処理で、CanvasFloatArray は WebGLFloatArray に、さらに Float32Array に名前が変更され、バッファにアクセスするためのバッキング ArrayBuffer と型指定された Float32Array ビューに分割されました。また、他の整数と浮動小数点のサイズ、および符号付き / 符号なしのバリエーションの型も追加されました。

設計上の考慮事項

最初から、型付き配列の設計は、バイナリ データをネイティブ ライブラリに効率的に渡す必要性から生まれました。このため、型付き配列ビューは、ホスト CPU のネイティブ エンディアンネスでアライメントされたデータに対して動作します。これらの決定により、JavaScript は、頂点データをグラフィック カードに送信するなどのオペレーション中に最大のパフォーマンスを達成できます。

DataView は、ファイル I/O とネットワーク I/O 用に特別に設計されています。これらの I/O では、データには常に指定されたエンディアンが使用され、パフォーマンスを最大化するためにアライメントされない場合があります。

メモリ内データ アセンブリ(型付き配列ビューを使用)と I/O(DataView を使用)の設計分割は意識的なものです。最新の JavaScript エンジンは、型付き配列ビューを大幅に最適化し、それらを使用した数値演算で高いパフォーマンスを実現しています。型付き配列ビューの現在のパフォーマンスは、この設計上の決定によって実現されています。

参照