¿Cuándo está bien usar matrices paralelas?

14

Me he encontrado con un código (nuevo código) que usa lo que llamo 'Arreglos Paralelos' o Listas. Lo que significa que hay 2 matrices que contienen datos relacionados y están vinculados por su posición (índice) en la matriz.

Considero esto confuso y propenso a todo tipo de errores. La solución que normalmente propongo es crear un objeto llamado Companycon los campos CompanyId y CompanyName.

Un ejemplo muy 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]);
}

¿Estas matrices paralelas se consideran una mala práctica ?

GER
fuente
99
Simplemente una prueba más de que no se ha inventado ningún idioma en el que no se pueda escribir Fortran.
Andy mango
3
Puede haber beneficios (bastante significativos) de almacenamiento en caché al hacer algo como esto (aunque necesita matrices contiguas no listas enlazadas), y esto se ha vuelto algo popular en la programación de juegos relacionada con el "diseño orientado a datos". Sin embargo, esto no parece aplicarse a su caso. No parece que esté haciendo un código crítico de rendimiento.
Derek Elkins salió del SE
2
@DerekElkins ... Es interesante que su comentario siga a uno comparando esto con el código Fortran. Las primeras versiones de Fortran carecían de soporte para estructuras definidas por el usuario, e incluso después de que se agregó el código idiomático de Fortran usa múltiples matrices de propiedades, no matrices de estructuras. Y esto a menudo se acredita como parte de la razón por la que Fortran a menudo se considera el idioma más rápido.
Jules
3
Un pensamiento tangencial a esta pregunta: muchos lenguajes funcionales fomentan activamente el trabajo con tales listas. Tienen una función, generalmente llamada zip, que los convierte en una lista de tuplas. Su código se parece a C #. La última versión de C # ha agregado soporte para tuplas de primera clase. Me pregunto si, por lo tanto, han agregado una función zip en algún lugar que pueda poner sus listas en una estructura útil para usted automáticamente.
Jules
44
Bueno, a veces hay razones para usar dos matrices intencionalmente, pero en el 99% de los casos que he visto esto, la única razón para ello fue la pereza del autor original para introducir una estructura de datos que abarca.
Doc Brown

Respuestas:

23

Aquí hay algunas razones por las cuales alguien podría usar matrices parrel:

  1. En un lenguaje que no admite clases o estructuras
  2. Para evitar el bloqueo de subprocesos cuando los subprocesos individuales solo modifican una de las columnas
  3. Cuando el método de persistencia obliga a estas cosas a almacenarse por separado y las está reconstituyendo.
  4. Pueden consumir menos memoria si las estructuras están acolchadas. (no aplicable para estos tipos de datos en C #)
  5. Cuando partes de los datos deben mantenerse juntas para hacer un uso eficiente de la memoria caché de la CPU (no sería de ayuda en el código anterior).
  6. Uso de códigos de operación de Datos múltiples de instrucción única (SIMD). (no aplicable para este código o cadenas)

No veo ninguna razón convincente para hacer esto en este caso ... y es probable que haya mejores opciones en todo lo anterior o que no sean tan útiles en un lenguaje de alto nivel.

TheCatWhisperer
fuente
3
También pueden consumir menos memoria si las estructuras están acolchadas. Varios arreglos grandes, asignados de manera inteligente, pueden consumir menos memoria que un conjunto de estructuras.
Frank Hileman
44
4. Cuando partes de los datos deben mantenerse juntas para hacer un uso eficiente del caché de la CPU. (Necesario en casos raros.)
Blrfl
@ Frank Hileman, Whilie Creo que la respuesta de TheCatWhisperer es completamente correcta, su comentario es, en realidad, la mejor razón para elegir este enfoque. Si el consumo de memoria es crítico, la sobrecarga de memoria en el relleno de estructuras puede ser significativa, especialmente si hay grandes números en juego.
Vladimir Stokic
Agregó sus sugerencias a la respuesta
TheCatWhisperer
Re (2), ¿Cómo es eso? Puedo escribir un programa con una sola matriz de estructuras y un bloqueo por campo tan fácilmente como puedo escribir uno con múltiples matrices y un bloqueo por matriz.
Solomon Slow
7

He sido culpable de usar matrices paralelas . A veces estás tan metido en la estructura que no quieres pensar en cómo abstraerla. La abstracción puede ser un poco más difícil de refactorizar, por lo que eres reacio a lanzarla directamente hasta que hayas demostrado lo que realmente necesitas.

En ese punto, vale la pena considerar la refactorización para abstraer los detalles. A menudo, la razón principal por la que me resisto a hacerlo es que es difícil pensar en un buen nombre.

Si puede ver una buena manera de abstraer las matrices paralelas, hágalo siempre. Pero no se paralice negándose a tocarlos. A veces, un pequeño código sucio es el mejor trampolín para un gran código.

naranja confitada
fuente
6

Este patrón a veces también se llama Estructura de matrices (en oposición a la matriz de estructuras), y es extremadamente útil al vectorizar código. En lugar de escribir un cálculo que se ejecute en una sola estructura y vectorizar bits del mismo, debe escribir el cálculo como lo haría normalmente, excepto con intrínsecos SSE para que se ejecute en 4 estructuras en lugar de una. Esto suele ser más fácil y casi siempre más rápido. El formato SoA lo hace muy natural. También mejora la alineación, lo que acelera las operaciones de memoria SSE.

Dan
fuente
Sí, este enfoque se usa cuando se hace aprendizaje automático en la GPU. Es habitual separar los campos de muchos ejemplos separados, empaquetar todos los valores de cada campo en un tensor separado y pasar esos tensores para que se calculen en masa para producir una lista de predicciones.
Vuelva a instalar Mónica