¿Cuáles son las diferencias entre las matrices multidimensionales double[,]
y las matrices de matrices double[][]
en C #?
Si hay una diferencia, ¿cuál es el mejor uso para cada uno?
¿Cuáles son las diferencias entre las matrices multidimensionales double[,]
y las matrices de matrices double[][]
en C #?
Si hay una diferencia, ¿cuál es el mejor uso para cada uno?
double[,]
es una matriz rectangular, mientras quedouble[][]
se conoce como una "matriz irregular". La primera tendrá el mismo número de "columnas" para cada fila, mientras que la segunda tendrá (potencialmente) un número diferente de "columnas" para cada fila.Respuestas:
Las matrices de matrices (matrices irregulares) son más rápidas que las matrices multidimensionales y se pueden usar de manera más efectiva. Las matrices multidimensionales tienen una sintaxis más agradable.
Si escribe un código simple usando matrices irregulares y multidimensionales y luego inspecciona el ensamblaje compilado con un desensamblador IL, verá que el almacenamiento y la recuperación de matrices irregulares (o unidimensionales) son instrucciones IL simples, mientras que las mismas operaciones para matrices multidimensionales son método invocaciones que siempre son más lentas.
Considere los siguientes métodos:
Su IL será la siguiente:
Al usar matrices irregulares, puede realizar fácilmente operaciones tales como cambio de fila y cambio de tamaño de fila. Quizás en algunos casos el uso de matrices multidimensionales sea más seguro, pero incluso Microsoft FxCop dice que se deben usar matrices irregulares en lugar de multidimensionales cuando se usa para analizar sus proyectos.
fuente
Una matriz multidimensional crea un diseño de memoria lineal agradable, mientras que una matriz irregular implica varios niveles adicionales de indirección.
Buscar el valor
jagged[3][6]
en una matriz irregularvar jagged = new int[10][5]
funciona de la siguiente manera: busque el elemento en el índice 3 (que es una matriz) y busque el elemento en el índice 6 en esa matriz (que es un valor). Para cada dimensión en este caso, hay una búsqueda adicional (este es un patrón de acceso a memoria costoso).Una matriz multidimensional se presenta linealmente en la memoria, el valor real se encuentra multiplicando los índices. Sin embargo, dada la matriz
var mult = new int[10,30]
, laLength
propiedad de esa matriz multidimensional devuelve el número total de elementos, es decir, 10 * 30 = 300.La
Rank
propiedad de una matriz dentada es siempre 1, pero una matriz multidimensional puede tener cualquier rango. ElGetLength
método de cualquier matriz se puede usar para obtener la longitud de cada dimensión. Para la matriz multidimensional en este ejemplo,mult.GetLength(1)
devuelve 30.La indexación de la matriz multidimensional es más rápida. Por ejemplo, dada la matriz multidimensional en este ejemplo
mult[1,7]
= 30 * 1 + 7 = 37, obtenga el elemento en ese índice 37. Este es un mejor patrón de acceso a la memoria porque solo está involucrada una ubicación de memoria, que es la dirección base de la matriz.Por lo tanto, una matriz multidimensional asigna un bloque de memoria continua, mientras que una matriz irregular no tiene que ser cuadrada, por ejemplo
jagged[1].Length
, no tiene que ser igualjagged[2].Length
, lo que sería cierto para cualquier matriz multidimensional.Actuación
En cuanto al rendimiento, las matrices multidimensionales deberían ser más rápidas. Mucho más rápido, pero debido a una implementación CLR realmente mala no lo son.
La primera fila son tiempos de matrices irregulares, la segunda muestra matrices multidimensionales y la tercera, bueno, así es como debería ser. El programa se muestra a continuación, para su información, esto se probó con mono. (Los tiempos de Windows son muy diferentes, principalmente debido a las variaciones de implementación de CLR).
En Windows, los tiempos de las matrices irregulares son muy superiores, casi lo mismo que mi propia interpretación de cómo debería ser la búsqueda de matrices multidimensionales, ver 'Single ()'. Lamentablemente, el compilador JIT de Windows es realmente estúpido, y esto desafortunadamente dificulta estas discusiones de rendimiento, hay demasiadas inconsistencias.
Estos son los tiempos que obtuve en Windows, el mismo trato aquí, la primera fila son matrices irregulares, la segunda es multidimensional y la tercera es mi propia implementación de multidimensional, tenga en cuenta cuánto más lento es esto en Windows en comparación con mono.
Código fuente:
fuente
En pocas palabras, las matrices multidimensionales son similares a una tabla en DBMS.
La matriz de matriz (matriz irregular) le permite hacer que cada elemento contenga otra matriz del mismo tipo de longitud variable.
Por lo tanto, si está seguro de que la estructura de datos se parece a una tabla (filas / columnas fijas), puede usar una matriz multidimensional. La matriz dentada son elementos fijos y cada elemento puede contener una matriz de longitud variable
Por ejemplo, Psuedocode:
Piense en lo anterior como una tabla de 2x2:
Piense en lo anterior como cada fila que tiene un número variable de columnas:
fuente
Prefacio: Este comentario está destinado a abordar la respuesta proporcionada por okutane , pero debido al sistema de reputación tonto de SO, no puedo publicarlo donde pertenece.
Su afirmación de que uno es más lento que el otro debido a las llamadas al método no es correcta. Uno es más lento que el otro debido a los algoritmos de comprobación de límites más complicados. Puede verificar esto fácilmente mirando no el IL, sino el ensamblado compilado. Por ejemplo, en mi instalación 4.5, el acceso a un elemento (a través del puntero en edx) almacenado en una matriz bidimensional apuntada por ecx con índices almacenados en eax y edx se ve así:
Aquí, puede ver que no hay sobrecarga por las llamadas a métodos. La comprobación de límites es muy complicada gracias a la posibilidad de índices distintos de cero, que es una funcionalidad que no se ofrece con matrices irregulares. Si eliminamos los sub, cmp y jmps para los casos distintos de cero, el código prácticamente se resuelve
(x*y_max+y)*sizeof(ptr)+sizeof(array_header)
. Este cálculo es casi tan rápido (una multiplicación podría ser reemplazada por un cambio, ya que esa es la razón por la que elegimos bytes para ser dimensionados como potencias de dos bits) como cualquier otra cosa para el acceso aleatorio a un elemento.Otra complicación es que hay muchos casos en los que un compilador moderno optimizará la comprobación de límites anidados para el acceso a elementos mientras itera sobre una matriz de una sola dimensión. El resultado es un código que básicamente solo avanza un puntero de índice sobre la memoria contigua de la matriz. La iteración ingenua sobre matrices multidimensionales generalmente implica una capa adicional de lógica anidada, por lo que es menos probable que un compilador optimice la operación. Por lo tanto, a pesar de que la sobrecarga de verificación de límites de acceso a un solo elemento se amortiza en tiempo de ejecución constante con respecto a las dimensiones y tamaños de la matriz, un caso de prueba simple para medir la diferencia puede tardar mucho más en ejecutarse.
fuente
Me gustaría actualizar esto, porque en .NET Core las matrices multidimensionales son más rápidas que las matrices irregulares . Ejecuté las pruebas de John Leidegren y estos son los resultados en .NET Core 2.0 preview 2. Aumenté el valor de la dimensión para que las posibles influencias de las aplicaciones en segundo plano sean menos visibles.
Investigué los desensamblajes y esto es lo que encontré
jagged[i][j][k] = i * j * k;
necesitaba 34 instrucciones para ejecutarmulti[i, j, k] = i * j * k;
necesitaba 11 instrucciones para ejecutarsingle[i * dim * dim + j * dim + k] = i * j * k;
necesitaba 23 instrucciones para ejecutarNo pude identificar por qué las matrices unidimensionales aún eran más rápidas que las multidimensionales, pero supongo que tiene que ver con alguna optimización realizada en la CPU
fuente
Las matrices multidimensionales son matrices (n-1) de dimensión.
Entonces
int[,] square = new int[2,2]
es matriz cuadrada 2x2,int[,,] cube = new int [3,3,3]
es un cubo - matriz cuadrada 3x3. No se requiere proporcionalidad.Las matrices irregulares son solo matrices de matrices, una matriz donde cada celda contiene una matriz.
Así que MDA son proporcionales, ¡JD puede no serlo! ¡Cada celda puede contener una matriz de longitud arbitraria!
fuente
Esto podría haber sido mencionado en las respuestas anteriores, pero no explícitamente: con una matriz irregular puede usar
array[row]
para referir una fila completa de datos, pero esto no está permitido para matrices multi-d.fuente
Además de las otras respuestas, tenga en cuenta que una matriz multidimensional se asigna como un gran objeto grueso en el montón. Esto tiene algunas implicaciones:
<gcAllowVeryLargeObjects>
matrices multidimensionales mucho antes de que surja el problema si solo usa matrices irregulares.fuente
Estoy analizando archivos .il generados por ildasm para construir una base de datos de ensamblajes, clases, métodos y procedimientos almacenados para usar para hacer una conversión. Encontré lo siguiente, que rompió mi análisis.
Explica el libro Expert .NET 2.0 IL Assembler, de Serge Lidin, Apress, publicado en 2006, Capítulo 8, Tipos y firmas primitivas, págs. 149-150.
<type>[]
se denomina Vector de<type>
,<type>[<bounds> [<bounds>**] ]
se denomina una serie de<type>
**
los medios pueden repetirse, los[ ]
medios son opcionales.Ejemplos: Let
<type> = int32
.1)
int32[...,...]
es una matriz bidimensional de límites y tamaños inferiores indefinidos2)
int32[2...5]
es una matriz unidimensional de límite inferior 2 y tamaño 4.3)
int32[0...,0...]
es una matriz bidimensional de límites inferiores 0 y tamaño indefinido.Tom
fuente