Come gestire le operazioni con array di grandi dimensioni in Javascript?

5

Ho due Float32Arrays ognuno dei quali è 1,6 * 10 ^ 7 di lunghezza (array in virgola mobile). Usando JS li recupero dal server e li aggiungo elemento per elemento. La mia pagina web smette di rispondere e viene visualizzato il seguente messaggio di errore. C'è un modo per gestire enormi matrici sul lato client senza usare JS o JS?

    
posta gaurav tripathi 13.08.2016 - 22:49
fonte

4 risposte

3

Non ho privilegi per commentare. Quindi, lo aggiungo come risposta qui.

Il mio browser (firefox 47) è stato in grado di impostare valori per 50.000.000 di elementi di un Float32Array, uno per uno, al di sotto di un secondo (non è tuttavia un inserimento / aggiunta). Ha esaurito la memoria oltre quello.

Presumo che il collo di bottiglia (avviso del browser) abbia più a che fare con il recupero / elaborazione lenti di elementi, pochi alla volta, e mantenere occupato il thread del browser principale.

Se hai davvero bisogno che vengano raccolti / elaborati / inseriti tanti dati, mentre allo stesso tempo lasci che il browser risponda all'utente, potresti prendere in considerazione il multi-threading in HTML5 (o le librerie javascript fornite da Mozilla e altri). Ti aiuterà a scaricare il lavoro impegnato in un thread in background.

    
risposta data 14.08.2016 - 01:00
fonte
1

Utilizza richieste di intervallo per ottenere i dati dal server chunk di chunk e rimandalo al server chunk-by-chunk. Un esempio di utilizzo delle richieste di intervallo, basato su questa domanda SO è mostrata di seguito:

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

Tuttavia, per far funzionare questo metodo, è necessario configurare i server per accettare richieste di intervallo. Quindi, basato su questo altro post SO , puoi ottenere la dimensione del file remoto. Tuttavia, questo richiederà al tuo server di impostare l'intestazione content-length sulla dimensione del file. Quindi, mettendo tutto insieme in un parser piccolo, ottieni qualcosa del genere (il seguente frammento di codice è completamente funzionale tranne che richiede getPartialSegment sopra):

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

Detto questo, devo solo dire che se si aggiungono solo gli elementi, il server verrà tassato molto meno per aggiungerli sul server anziché sul client. Anch'io sono molto entusiasta di spostare il maggior numero possibile di elaborazioni sul client, ma l'invio di dati attraverso una rete solo per aggiunta è molto meno efficiente che aggiungendolo senza inviarlo attraverso una rete. Non sarei sorpreso se internamente aggiungesse un puntatore che è usato per leggere / scrivere tutti i byte nei dati alla richiesta di rete, il che significa che inviarlo attraverso la rete per ottenere aggiunto induce 3 aggiunte aggiuntive (minimo assoluto assoluto, sebbene probabilmente più in realtà) per byte nel tuo array sul server che avrebbe potuto essere solo una singola aggiunta per elemento sul server.

    
risposta data 10.07.2017 - 19:52
fonte
0

La risposta è che non puoi elaborare tutte le cose insieme, perché è troppo.

Dovresti ripensare a quello che stai facendo, alcune possibili soluzioni sono:

  • dividi i dati in chunk
  • riduci le dimensioni con i filtri
  • pre-elaborazione sul lato server
  • ... sii creativo
risposta data 21.08.2016 - 12:32
fonte
0

Ciò di cui hai bisogno è elaborare grandi dati relativi in un ambiente con poca memoria e basse prestazioni. La soluzione generale a questo sta usando i flussi. In questi flussi metti in memoria solo uno o pochi pezzi, lo elabora e libera la memoria. Quindi non avrai bisogno di molta memoria e potenza di elaborazione per fare il lavoro.

È necessario eseguire lo streaming dei dati dal server, creare un flusso di elaborazione sul client e visualizzare il blocco di dati per blocco, probabilmente pixel per pixel. Immagino che avrai bisogno di un algoritmo di zoom, che dovrebbe essere in grado di utilizzare gli stessi flussi.

Se hai una soluzione funzionante, puoi provare a renderla più veloce, ad es. utilizzando più connessioni websocket, più flussi di visualizzazione, una memoria lato client per memorizzare dati, preparazione dati lato server, web worker, ecc ... Penso che questo progetto sarà molto più lungo di quanto non ci si aspettasse ... Non dimenticare condividere la soluzione con la community di js! : -)

    
risposta data 22.08.2016 - 15:23
fonte

Leggi altre domande sui tag