【JavaScript】TypedArrayとDataViewの使い分け

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の性能比較

測定条件
  • データサイズ:1G
  • 測定回数;10回
  • 操作:シーケンシャルな読み込みと書き込み
  • 読み込み書込みサイズ:Uint32(4byte)
  • その他:キャッシュクリアは無し
TypedArray
(Read)
DataView
(Read)
TypedArray
(Write)
DataView
(Write)
148147558938
単位は[ms]

読み込みに関しては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

コメント

タイトルとURLをコピーしました