web-dev-qa-db-ja.com

JavaScriptで大規模な配列操作を処理する方法

2つのFloat32Arraysがあり、それぞれの長さが1.6 * 10 ^ 7です(浮動小数点配列)。 JSを使用してサーバーから取得し、要素ごとに追加します。 Webページが応答を停止し、次のエラーメッセージが表示されます。 JSまたはJSを使用せずにクライアント側で巨大な配列を処理する方法はありますか?

enter image description here

5
gaurav tripathi

コメントする権限がありません。それで、ここに答えとして追加します。

私のブラウザー(firefox 47)は、Float32Arrayの50,000,000要素の値を1秒以内に1つずつ設定できました(ただし、挿入/追加ではありません)。それを超えてメモリが不足しました。

私はあなたのボトルネック(ブラウザ警告)が要素のフェッチ/処理が遅いことと関係があり、一度に少数であり、メインのブラウザスレッドをビジー状態にしていると思います。

本当に多くのデータをフェッチ/処理/挿入する必要があり、同時にブラウザがユーザーに応答できるようにする場合は、HTML5(またはMozillaなどが提供するJavaScriptライブラリ)のマルチスレッドを検討することをお勧めします。忙しい作業をバックグラウンドスレッドにオフロードするのに役立ちます。

3
blackpen

範囲要求 を使用して、サーバーからデータを取得します チャンク単位 を使用して、サーバーにチャンク単位で送り返します。 this SO question に基づく範囲リクエストの使用例を以下に示します。

var chunkSize = 8388608; // for 8MB segments
function getPartialSegment(fileURL, chunkN, processData, whenError, prevreq){
    var xmlhttp = prevreq || new XMLHttpRequest();
    xmlhttp.open("GET", fileURL, true);
    xmlhttp.setRequestHeader(
        "Range",
        "bytes=" + (chunkN*chunkSize) + "-" + ((chunkN+1)*chunkSize - 1)
    );
    xmlhttp.responseType = 'arraybuffer';
    xmlhttp.onload = function(){
        var response = xmlhttp.response;
        if (response && (+xmlhttp.status === 200 || response.length > 0)){
            if (response.byteLength%4){ 
              if (!response.transfer){
              var responseUint8 = new Uint8Array(response),
                 tmpDataBuffer=new ArrayBuffer(Math.floor(response.byteLength/4)*4+4),
                  tmpDataUint8 = new Uint8Array(tmpDataBuffer)
                tmpDataUint8.set( responseUint8 );
                processData(new Float32Array(tmpDataBuffer), xmlhttp)
              } else processData(new Float32Array(ArrayBuffer.transfer(response, 
                Math.floor(response.byteLength/4)*4+4)),xmlhttp)
            } else processData(new Float32Array(response), xmlhttp);
        } else if (whenError instanceof Function)
            whenError(xmlhttp.status, xmlhttp);
    };
    xmlhttp.send( null );
};

ただし、この方法が機能するには、範囲リクエストを受け入れるようにサーバーを設定する必要があることに注意してください。次に、 この他のSO post に基づいて、リモートファイルサイズを取得できます。ただし、これには、サーバーがcontent-lengthヘッダーをファイルサイズです。すべてを小さなパーサーにまとめると、次のようになります(次のコードスニペットは、上記のgetPartialSegmentが必要なことを除いて完全に機能します)。

function proccessEntireFileInChunks(fileURL, processData, allDone, whenError){
    var xmlhttp=new XMLHttpRequest();
    xmlhttp.open("HEAD", fileURL, true);
    xmlhttp.onload = function(){
      var filelen = parseInt(xmlhttp.getResponseHeader("Content-Length"))
      if (xmlhttp.status === 200 || !isNaN(filelen)){
        var chunks  = Math.ceil(filelen / chunkSize),
            chunkN  = 0;
        if (!chunks) return allDone && allDone();

        getPartialSegment(fileURL, chunkN, function nextFile(response){
          processData(response, chunkN*chunkSize, filelen, xmlhttp);
          if (++chunkN !== chunks){
            getPartialSegment(fileURL, chunkN, nextFile, whenError, xmlhttp);
          } else return allDone && allDone();
        }, whenError, xmlhttp);
      } else if (whenError instanceof Function)
        whenError(xmlhttp.status, xmlhttp);
    };
    xmlhttp.send( null );
}

以上のことをすべて言ったとしても、要素を追加するだけの場合は、クライアントにではなくサーバーに追加するほうが、サーバーにかかる負荷が大幅に軽減されます。私もできるだけ多くの処理をクライアントに移動することに非常に熱心ですが、追加のためだけにネットワーク経由でデータを送信することは、ネットワーク経由で送信せずにデータを追加するよりもはるかに非効率です。内部的には、データのすべてのバイトの読み取り/書き込みに使用されるポインターをネットワーク要求に追加します。つまり、ネットワーク経由で送信して追加されると、3つの追加が追加されます(ただし、最低限、おそらくより現実的です)、サーバー上の要素の1バイトあたり、サーバー上の要素ごとに1回の追加だけで済みます。

必要なのは、低メモリ、低パフォーマンスの環境で比較的大きなデータを処理することです。これに対する一般的な解決策は、ストリームを使用することです。これらのストリームでは、メモリに1つまたは数個のチャンクのみを入れて処理し、メモリを解放します。そのため、作業に多くのメモリと処理能力は必要ありません。

サーバーからデータをストリーミングし、クライアントで処理ストリームを作成し、データをチャンクごとに、おそらくピクセルごとに表示する必要があります。同じストリームを使用できるズームアルゴリズムが必要になると思います。

実用的なソリューションがある場合は、それをより速くすることを試みることができます。複数のWebSocket接続、複数のディスプレイストリーム、データをキャッシュするクライアント側のストレージ、サーバー側でのデータ準備、Webワーカーなどを使用することにより、これは予想よりもはるかに長いプロジェクトになると思います...忘れないでくださいソリューションをjsコミュニティと共有します! :-)

0
inf3rno

答えはできないが多すぎるため、それらすべてをまとめて処理することです。

あなたは自分が何をしているかを考え直す必要があります。考えられる解決策は次のとおりです。

  • データをチャンクに分割する
  • フィルターでサイズを小さくする
  • サーバー側で事前に精巧化する
  • ... クリエイティブに
0
Motocarota