TypedArrayとDataViewの使い分け
①バイトオーダー(エンディアン)を意識する必要がある場合
→DataView
②Arrayライクなメソッドを使いたい場合
→TypedArray
③性能を重視する場合
→TypedArray
以上、第三部、完。
は、流石に簡略化しすぎですね。
次章以降に、TypedArray/DataViewのできることをまとめているので、もう少し深掘りしたい場合は見てみてください。
TypedArrayでできること
TypedArrayはバイナリ(ArrayBuffer型)のデータを配列のように扱うためのビューです。
配列(Array)なので、添字でのデータアクセスや、forEach/find/slice…などが使用可能です。
Uint8Array(1バイト単位の配列)やUint32Array(4バイト単位の配列)など、サイズ毎のコンストラクタが予め用意されているので、
何バイト単位で配列を操作したいかに合わせてTypedArrayを作成可能です。
サンプルコード
// TypedArrayで操作するバイナリデータ(ArrayBuffer)
let BUF_SIZE_BYTE = 1024;
let buf = new ArrayBuffer(BUF_SIZE_BYTE);
// 上記のbufを操作するTypedArrayを作成(これはUint32(4バイト幅)で扱う例)
const typedArray1 = new Uint32Array(buf);
// 配列なので添字アクセスでデータの読み書きが可能
typedArray1[5] = 12345;
console.log(typedArray1[5]);
// => 12345
// よくあるArrayのメソッド
console.log(typedArray1.indexOf(12345));
// => 5
DataViewでできること
DataViewはバイナリ(ArrayBuffer型)のデータをバイトオーダーに依存せず扱うためのビューです。
バイナリデータを読み書きする際に、明示的にリトルエンディアン/ビッグエンディアンを指定することが可能です。
※前述のTypedArrayは、データはそのままでしか扱えない(ホストバイトオーダーでしか扱えない)
setUint16(2バイト)やgetUint32(4バイト)など、サイズ単位のset/getのメソッドが用意されています。
※前述のTypedArrayと異なりArrayライクなメソッドは持っていない
サンプルコード
// DataViewで操作するバイナリデータ(ArrayBuffer)
let BUF_SIZE_BYTE = 1024;
let buf = new ArrayBuffer(BUF_SIZE_BYTE);
// 上記のbufを操作するDataViewを作成
let view = new DataView(buf);
let offset = 0;
let data = 9999;
// データの読み書き
// 第三引数がtrueの場合はリトルエンディアン指定
view.setUint32(offset, data, true);
console.log(view.getUint32(offset, true));
TypedArrayとDataViewの性能比較
TypedArray (Read) | DataView (Read) | TypedArray (Write) | DataView (Write) |
148 | 147 | 558 | 938 |
読み込みに関してはTypedArray, DataViewの間に、大きな差は見られませんでした。
一方、書き込み性能は、1.5倍強の差が生まれる結果となりました。
「TypedArrayの方が性能が良い」という噂は耳にしていたので、まずまず納得の結果です。
まあ、性能求めるならC/C++で書けっていう話ですが…
測定コード
<html>
<body>
<script type='text/javascript'>
// ここの値を変えて測定
const BUF_SIZE_BYTE = 1024 * 1024 * 1024;
const ARRAY_LEN = BUF_SIZE_BYTE / 4;
const LOOP_NUM = 10;
const UINT_MAX = 4294967295;
function measurement() {
let buf1 = new ArrayBuffer(BUF_SIZE_BYTE);
let buf2 = new ArrayBuffer(BUF_SIZE_BYTE);
let tmp = 0;
/* ==== TypedArray ==== */
// #1 Write
let startTime1 = performance.now();
const typedArray1 = new Uint32Array(buf2);
for (let i = 0; i < ARRAY_LEN; i++) {
typedArray1[i] = UINT_MAX;
}
let elapsedTime1 = Math.round(performance.now() - startTime1);
// #2 Read
let startTime2 = performance.now();
const typedArray2 = new Uint32Array(buf2);
for (let i = 0; i < ARRAY_LEN; i++) {
tmp = typedArray2[i];
// console.log(tmp);
}
let elapsedTime2 = Math.round(performance.now() - startTime2);
/* ==== DataView ==== */
// #3 Write
let startTime3 = performance.now();
let view1 = new DataView(buf1);
for (let i = 0; i < BUF_SIZE_BYTE; i = i + 4) {
view1.setUint32(i, UINT_MAX);
}
let elapsedTime3 = Math.round(performance.now() - startTime3);
// #4 Read
let startTime4 = performance.now();
let view2 = new DataView(buf1);
for (let i = 0; i < BUF_SIZE_BYTE; i = i + 4) {
tmp = view2.getUint32(i);
// console.log(tmp);
}
let elapsedTime4 = Math.round(performance.now() - startTime4);
/* ==== Result ==== */
console.log(`${elapsedTime1},${elapsedTime2},${elapsedTime3},${elapsedTime4}`);
}
console.log('TypedArray(Write)[ms],TypedArray(Read)[ms],DataView(Write)[ms],DataView(Read)[ms]');
for(let i = 0; i < LOOP_NUM; i++) {
measurement();
}
</script>
</body>
</html>
参考
[Mozillaのドキュメント]
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/DataView
https://developer.mozilla.org/ja/docs/Web/JavaScript/Reference/Global_Objects/TypedArray
[その他参考にした記事]
https://qiita.com/megadreams14/items/dded3cf770010bb8ff08
コメント