Quais são as diferenças entre matrizes multidimensionais double[,]
e array de matrizes double[][]
em C #?
Se houver alguma diferença, qual é o melhor uso para cada um?
Quais são as diferenças entre matrizes multidimensionais double[,]
e array de matrizes double[][]
em C #?
Se houver alguma diferença, qual é o melhor uso para cada um?
double[,]
é uma matriz retangular, enquantodouble[][]
é conhecida como "matriz irregular". A primeira terá o mesmo número de "colunas" para cada linha, enquanto a segunda (potencialmente) terá um número diferente de "colunas" para cada linha.Respostas:
A matriz de matrizes (matrizes irregulares) é mais rápida que as matrizes multidimensionais e pode ser usada com mais eficiência. Matrizes multidimensionais têm uma sintaxe melhor.
Se você escrever um código simples usando matrizes irregulares e multidimensionais e depois inspecionar o conjunto compilado com um desmontador de IL, verá que o armazenamento e a recuperação de matrizes irregulares (ou unidimensionais) são instruções simples de IL, enquanto as mesmas operações para matrizes multidimensionais são o método invocações sempre mais lentas.
Considere os seguintes métodos:
A IL será a seguinte:
Ao usar matrizes irregulares, você pode executar facilmente operações como troca e redimensionamento de linha. Talvez, em alguns casos, o uso de matrizes multidimensionais seja mais seguro, mas mesmo o Microsoft FxCop diz que matrizes irregulares devem ser usadas em vez de multidimensionais quando você a usa para analisar seus projetos.
fonte
Uma matriz multidimensional cria um bom layout de memória linear, enquanto uma matriz irregular implica vários níveis extras de indireção.
A procura do valor
jagged[3][6]
em uma matriz irregularvar jagged = new int[10][5]
funciona da seguinte maneira: Consulte o elemento no índice 3 (que é uma matriz) e procure o elemento no índice 6 nessa matriz (que é um valor). Para cada dimensão, neste caso, há uma pesquisa adicional (esse é um padrão de acesso à memória caro).Uma matriz multidimensional é apresentada linearmente na memória, e o valor real é encontrado pela multiplicação dos índices. No entanto, dada a matriz
var mult = new int[10,30]
, aLength
propriedade dessa matriz multidimensional retorna o número total de elementos, ou seja, 10 * 30 = 300.A
Rank
propriedade de uma matriz irregular é sempre 1, mas uma matriz multidimensional pode ter qualquer classificação. OGetLength
método de qualquer matriz pode ser usado para obter o comprimento de cada dimensão. Para a matriz multidimensional neste exemplo,mult.GetLength(1)
retorna 30.A indexação da matriz multidimensional é mais rápida. por exemplo, considerando a matriz multidimensional neste exemplo
mult[1,7]
= 30 * 1 + 7 = 37, obtenha o elemento nesse índice 37. Esse é um melhor padrão de acesso à memória porque apenas um local de memória está envolvido, que é o endereço base da matriz.Uma matriz multidimensional, portanto, aloca um bloco de memória contínuo, enquanto uma matriz irregular não precisa ser quadrada, por exemplo
jagged[1].Length
, não precisa ser igualjagged[2].Length
, o que seria verdadeiro para qualquer matriz multidimensional.atuação
Em termos de desempenho, matrizes multidimensionais devem ser mais rápidas. Muito mais rápido, mas devido a uma implementação CLR muito ruim, eles não são.
A primeira linha são tempos de matrizes irregulares, a segunda mostra matrizes multidimensionais e a terceira, bem, é assim que deve ser. O programa é mostrado abaixo, FYI, este foi testado em execução mono. (O tempo das janelas é muito diferente, principalmente devido às variações de implementação do CLR).
Nas janelas, o tempo das matrizes irregulares é muito superior, quase o mesmo que a minha própria interpretação de como deve ser a matriz multidimensional, consulte 'Single ()'. Infelizmente, o compilador JIT do Windows é realmente estúpido, e isso infelizmente torna essas discussões de desempenho difíceis, existem muitas inconsistências.
Estes são os horários que obtive nas janelas, o mesmo negócio aqui, a primeira linha são matrizes irregulares, a segunda multidimensional e a terceira a minha própria implementação da multidimensional, observe o quanto isso é mais lento nas janelas em comparação com mono.
Código fonte:
fonte
Simplificando, matrizes multidimensionais são semelhantes a uma tabela no DBMS.
Matriz de matriz (matriz irregular) permite que cada elemento mantenha outra matriz do mesmo tipo de comprimento variável.
Portanto, se você tiver certeza de que a estrutura dos dados se parece com uma tabela (linhas / colunas fixas), poderá usar uma matriz multidimensional. Matriz irregular são elementos fixos e cada elemento pode conter uma matriz de comprimento variável
Por exemplo, Psuedocode:
Pense no exposto acima como uma tabela 2x2:
Pense no exposto como cada linha com número variável de colunas:
fonte
Prefácio: Este comentário pretende abordar a resposta fornecida pelo okutane , mas devido ao sistema de reputação boba da SO, não posso publicá-lo onde ele pertence.
Sua afirmação de que uma é mais lenta que a outra por causa das chamadas de método não está correta. Um é mais lento que o outro por causa de algoritmos de verificação de limites mais complicados. Você pode verificar isso facilmente observando, não o IL, mas o assembly compilado. Por exemplo, na minha instalação 4.5, acessar um elemento (via ponteiro no edx) armazenado em uma matriz bidimensional apontada pelo ecx com índices armazenados no eax e edx é assim:
Aqui, você pode ver que não há sobrecarga nas chamadas de método. A verificação de limites é muito complicada, graças à possibilidade de índices diferentes de zero, que é uma funcionalidade que não é oferecida com matrizes irregulares. Se removermos os sub, cmp e jmps para os casos diferentes de zero, o código será praticamente resolvido
(x*y_max+y)*sizeof(ptr)+sizeof(array_header)
. Esse cálculo é tão rápido (uma multiplicação pode ser substituída por uma mudança, já que essa é a razão pela qual escolhemos bytes para serem dimensionados como potências de dois bits) como qualquer outra coisa para acesso aleatório a um elemento.Outra complicação é que há muitos casos em que um compilador moderno otimiza a verificação de limites aninhados para acesso ao elemento enquanto itera sobre uma matriz de dimensão única. O resultado é um código que basicamente apenas avança um ponteiro de índice sobre a memória contígua da matriz. A iteração ingênua sobre matrizes multidimensionais geralmente envolve uma camada extra de lógica aninhada; portanto, é menos provável que um compilador otimize a operação. Portanto, mesmo que a sobrecarga de verificação de limites do acesso a um único elemento seja amortizada em tempo de execução constante em relação às dimensões e tamanhos da matriz, um caso de teste simples para medir a diferença pode demorar muito mais para ser executado.
fonte
Gostaria de atualizar isso, porque no .NET Core as matrizes multidimensionais são mais rápidas que as matrizes irregulares . Fiz os testes de John Leidegren e esses são os resultados da visualização 2. do .NET Core 2.0. Aumentei o valor da dimensão para tornar menos visíveis as possíveis influências de aplicativos em segundo plano.
Eu olhei para desmontagens e foi isso que eu achei
jagged[i][j][k] = i * j * k;
34 instruções necessárias para executarmulti[i, j, k] = i * j * k;
11 instruções necessárias para executarsingle[i * dim * dim + j * dim + k] = i * j * k;
necessárias 23 instruções para executarNão consegui identificar por que matrizes unidimensionais ainda eram mais rápidas que multidimensionais, mas meu palpite é que isso tem a ver com alguma otimização feita na CPU
fonte
Matrizes multidimensionais são matrizes de dimensão (n-1).
Assim
int[,] square = new int[2,2]
é a matriz quadrada 2x2,int[,,] cube = new int [3,3,3]
é uma matriz cubo-quadrada 3x3. Proporcionalidade não é necessária.Matrizes irregulares são apenas uma matriz de matrizes - uma matriz em que cada célula contém uma matriz.
Portanto, o MDA é proporcional, o JD pode não ser! Cada célula pode conter uma matriz de comprimento arbitrário!
fonte
Isso pode ter sido mencionado nas respostas acima, mas não explicitamente: com uma matriz irregular, você pode usar
array[row]
para referenciar uma linha inteira de dados, mas isso não é permitido para matrizes multi-d.fonte
Além das outras respostas, observe que uma matriz multidimensional é alocada como um grande objeto robusto no heap. Isso tem algumas implicações:
<gcAllowVeryLargeObjects>
matrizes multidimensionais muito antes que o problema ocorra, se você usar apenas matrizes irregulares.fonte
Estou analisando arquivos .il gerados pelo ildasm para criar um banco de dados de declarações, classes, métodos e procedimentos armazenados para uso na conversão. Me deparei com o seguinte, que interrompeu minha análise.
O livro Expert .NET 2.0 IL Assembler, de Serge Lidin, Apress, publicado em 2006, capítulo 8, Tipos e assinaturas primitivas, pp. 149-150, explica.
<type>[]
é denominado vetor de<type>
,<type>[<bounds> [<bounds>**] ]
é denominado uma matriz de<type>
**
meios podem ser repetidos,[ ]
meios opcionais.Exemplos: Vamos
<type> = int32
.1)
int32[...,...]
é uma matriz bidimensional de limites e tamanhos inferiores indefinidos2)
int32[2...5]
é uma matriz unidimensional do limite inferior 2 e tamanho 4.3)
int32[0...,0...]
é uma matriz bidimensional de limites inferiores 0 e tamanho indefinido.Tom
fonte