Quando é bom usar matrizes paralelas?

14

Eu estive correndo em código (novo código) que usa o que eu chamo de 'Parallel Arrays' ou Listas. Ou seja, existem 2 matrizes que contêm dados relacionados e são vinculadas por sua posição (índice) na matriz.

Considero isso confuso e propenso a todos os tipos de erros. A solução que eu normalmente proponho é criar um objeto chamado Companycom os campos CompanyId e CompanyName.

Um exemplo muito real:

List<string> companyNames;
List<int> companyIds;

//...They get populated somewhere and we then process

for(var i=0; i<companyNames.Count; i++)
{
    UpdateCompanyName(companyIds[i],companyNames[i]);
}

Essas matrizes paralelas são consideradas más práticas ?

GER
fonte
9
Simplesmente mais uma prova de que nenhuma linguagem foi inventada na qual você não pode escrever Fortran.
andy manga
3
Pode haver benefícios (bastante significativos) de armazenamento em cache para algo assim (embora você precise de matrizes contíguas e não de listas vinculadas), e isso se tornou um pouco popular na programação de jogos relacionados ao "design orientado a dados". No entanto, isso parece não se aplicar ao seu caso. Não parece que você está criando um código crítico de desempenho.
Derek Elkins saiu de SE
2
@DerekElkins ... Interessante que o seu comentário siga um comparando isso com o código Fortran. As versões anteriores do Fortran não tinham suporte para estruturas definidas pelo usuário e, mesmo após a adição do código idiomático, o Fortran usa várias matrizes de propriedades e não matrizes de estruturas. E isso geralmente é creditado como parte do motivo pelo qual o Fortran é frequentemente considerado o idioma mais rápido.
Jules
3
Um pensamento tangencial para esta pergunta: muitas linguagens funcionais incentivam ativamente o trabalho com essas listas. Eles têm uma função, geralmente chamada zip, que os converte em uma lista de tuplas. Seu código se parece com C #. A versão mais recente do C # adicionou suporte para tuplas de primeira classe. Gostaria de saber se, portanto, eles adicionaram uma função zip em algum lugar que poderia colocar suas listas em uma estrutura útil automaticamente para você?
Jules
4
Bem, às vezes há razões para usar duas matrizes intencionalmente, mas em 99% de todos os casos que eu vi isso, a única razão para isso foi a preguiça do autor original de introduzir uma estrutura de dados abrangente.
Doc Brown

Respostas:

23

Aqui estão algumas razões pelas quais alguém pode usar matrizes parrel:

  1. Em um idioma que não suporta classes ou estruturas
  2. Para evitar o bloqueio de threads quando segmentos individuais estão apenas modificando uma das colunas
  3. Quando o método de persistência força essas coisas a serem armazenadas separadamente e você as reconstitui.
  4. Eles podem consumir menos memória se as estruturas forem preenchidas. (não aplicável a esses tipos de dados em c #)
  5. Quando partes dos dados precisam ser mantidas próximas para fazer uso eficiente do cache da CPU (não seria útil no código acima).
  6. Uso de códigos op de dados múltiplos de instrução única (SIMD). (não aplicável a este código ou a todas as strings)

Não vejo nenhuma razão convincente para fazer isso neste caso ... e provavelmente existem melhores opções em todas as opções acima ou não são tão úteis em um idioma de alto nível.

TheCatWhisperer
fonte
3
Eles também podem consumir menos memória se as estruturas forem preenchidas. Várias matrizes grandes, alocadas de forma inteligente, podem consumir menos memória que uma matriz de estruturas.
21875 Frank Hileman #
4
4. Quando partes dos dados precisam ser mantidas próximas para fazer uso eficiente do cache da CPU. (Necessário em casos raros).
Blrfl 2/17/17
@ Frank Hileman, Whilie Eu acho que a resposta de TheCatWhisperer está completamente correta, seu comentário é, na verdade, o melhor motivo para escolher essa abordagem. Se o consumo de memória for crítico, a sobrecarga de memória no preenchimento de estruturas pode ser significativa, especialmente se houver um grande número de reproduções.
Vladimir Stokic 2/17/17
Suas sugestões foram adicionadas à resposta
TheCatWhisperer 2/17/17
Re (2), como é isso? Posso escrever um programa com uma única matriz de estruturas e um bloqueio por campo com a mesma facilidade com que posso escrever um com várias matrizes e um bloqueio por matriz.
Solomon Slow
7

Fui culpado de usar matrizes paralelas . Às vezes, você está tanto na estrutura que não quer pensar em como abstraí-la. A abstração pode ser um pouco mais difícil de refatorar, de modo que você reluta em lançá-la diretamente até provar o que realmente precisa.

Nesse ponto, vale a pena considerar a refatoração para abstrair os detalhes. Freqüentemente, a maior razão pela qual estou relutante em fazer isso é que é difícil pensar em um bom nome.

Se você pode ver uma boa maneira de abstrair matrizes paralelas, faça-o sempre. Mas não se paralise, recusando tocá-los. Às vezes, um pouco de código sujo é o melhor trampolim para um ótimo código.

candied_orange
fonte
6

Às vezes, esse padrão também é chamado de Estrutura de matrizes (ao contrário de Matriz de estruturas) e é extremamente útil ao vetorizar código. Em vez de escrever um cálculo que é executado em uma única estrutura e vetorizá-lo, você escreve o cálculo como faria normalmente, exceto com as intrínsecas do SSE, para que seja executado em 4 estruturas em vez de uma. Isso geralmente é mais fácil e quase sempre mais rápido. O formato SoA torna isso muito natural. Também melhora o alinhamento, o que torna as operações de memória SSE mais rápidas.

Dan
fonte
Sim, essa abordagem é usada ao fazer o aprendizado de máquina na GPU. É habitual separar os campos de muitos exemplos separados, agrupar todos os valores de cada campo em um tensor separado e passar esses tensores para serem computados em massa para produzir uma lista de previsões.
Reintegrar Monica