Como lidar com operações de matriz grande em Javascript?

5

Eu tenho dois Float32Arrays cada um dos quais é 1,6 * 10 ^ 7 de comprimento (matriz de ponto flutuante). Usando JS, eu os recupero do servidor e os adiciono elemento por elemento. Minha página da Web pára de responder e recebo a seguinte mensagem de erro. Existe uma maneira de lidar com matrizes enormes no lado do cliente sem usar JS ou com JS?

    
por gaurav tripathi 13.08.2016 / 22:49
fonte

4 respostas

3

Eu não tenho previleges para comentar. Então, estou adicionando isso como uma resposta aqui.

Meu navegador (firefox 47) conseguiu definir valores para 50.000.000 de elementos de um Float32Array, um por um, menos de um segundo (não é um insert / append). Ficou sem memória além disso.

Assumo que o seu gargalo (aviso do navegador) tem mais a ver com a busca / processamento lento de elementos, poucos de cada vez, e manter o encadeamento do navegador principal ocupado.

Se você realmente precisar de muitos dados para serem buscados / processados / inseridos, enquanto ao mesmo tempo deixar que o navegador permaneça responsivo ao usuário, você pode querer considerar o multi-threading em HTML5 (ou bibliotecas javascript fornecidas pelo Mozilla e outros). Isso ajudará você a descarregar o trabalho ocupado em um segmento de segundo plano.

    
por 14.08.2016 / 01:00
fonte
1

Use solicitações de intervalo para obter os dados do servidor chunk by chunk e enviá-lo de volta ao servidor chunk-by-chunk. Um exemplo do uso de solicitações de intervalo, com base em esta SO pergunta é mostrada abaixo:

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

No entanto, observe que para esse método funcionar, você precisará configurar seus servidores para aceitar solicitações de intervalo. Em seguida, com base nessa outra postagem do SO , você pode obter o tamanho do arquivo remoto. No entanto, isso exigirá que seu servidor defina o cabeçalho content-length para o tamanho do arquivo. Portanto, juntando tudo em um pequeno analisador, você obtém algo assim (o seguinte fragmento de código é totalmente funcional, exceto que requer o getPartialSegment acima):

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 );
}
Tendo dito tudo isso, eu só tenho que dizer que se você só vai adicionar os elementos, então vai sobrecarregar o servidor muito menos para adicioná-los no servidor em vez de no cliente. Também estou muito entusiasmado em mover o máximo possível do processamento para o cliente, mas enviar dados por uma rede apenas para adição é muito menos eficiente do que adicioná-los sem enviá-los por uma rede. Eu não ficaria surpreso se internamente, ele adiciona um ponteiro que é usado para ler / gravar todos os bytes nos dados para a solicitação de rede, significando que enviá-lo pela rede para ser adicionado induz 3 adições extras (mínimo absoluto, embora provavelmente mais na realidade) por byte em sua matriz no servidor que poderia ter sido apenas uma única adição por elemento no servidor.

    
por 10.07.2017 / 19:52
fonte
0

A resposta é que você não pode processar todas essas coisas juntas, porque é demais.

Você deve repensar o que está fazendo, algumas possíveis soluções são:

  • divida os dados em partes
  • reduza o tamanho com filtros
  • pré-elaborado no lado do servidor
  • ... seja criativo
por 21.08.2016 / 12:32
fonte
0

O que você precisa é processar big data relativo em um ambiente com pouca memória e baixo desempenho. A solução geral para isso é usar fluxos. Nesses fluxos, você coloca apenas um ou alguns fragmentos na memória, processa-os e libera a memória. Então você não precisará de muita memória e capacidade de processamento para fazer o trabalho.

Você precisa transmitir os dados do servidor, criar um fluxo de processamento no cliente e exibir o bloco de dados por bloco, provavelmente pixel por pixel. Eu acho que você vai precisar de um algoritmo de zoom, que deve ser capaz de usar os mesmos fluxos.

Se você tem uma solução de trabalho, pode tentar torná-la mais rápida, por exemplo usando várias conexões websocket, vários fluxos de exibição, um armazenamento do lado do cliente para dados em cache, preparação de dados no lado do servidor, web workers, etc ... Eu acho que este será um projeto muito mais longo do que o esperado ... Não se esqueça para compartilhar a solução com a comunidade js! : -)

    
por 22.08.2016 / 15:23
fonte