2010-04-07 20 views
5

Necesito declarar matrices cuadradas en C# WinForms con más de 20000 elementos en una fila. Leí aproximadamente 2 GB de límite de tamaño de objeto .NET en 32 bits y también el mismo caso en el sistema operativo de 64 bits. Entonces, según entendí la única respuesta, es usar un código inseguro o una biblioteca separada construida con el compilador C++.C# gran tamaño matrices de 2 dim

El problema para mí vale la pena porque ushort [20000,20000] es más pequeño que 2GB pero en realidad no puedo asignar incluso 700MB de memoria. Mi límite es de 650 MB y no entiendo por qué: tengo WinXP de 32 bits con 3 GB de memoria. Intenté usar Marshal.AllocHGlobal (700 < < 20) pero arroja OutOfMemoryException, GC.GetTotalMemory devuelve 4.5MB antes de tratar de asignar memoria.

Solo encontré que muchas personas dicen usar código no seguro pero no puedo encontrar ejemplos de cómo declarar 2-dim array en el montón (cualquier pila no puede mantener una cantidad tan grande de datos) y cómo trabajar con punteros . ¿Es código puro de C++ dentro de {} corchetes inseguros?

PS. Por favor, no preguntes POR QUÉ necesito tantos arreglos ... pero si quieres, necesito analizar textos (por ejemplo, libros) y encontré muchos índices. Así que la respuesta es - matrices de relaciones entre las palabras

Edición: ¿Podría alguien proporcionar un pequeño ejemplo de trabajar con matrices usando punteros en el código inseguro. Sé que por debajo de 32 bits es imposible asignar más espacio, pero pasé mucho tiempo buscando en Google ese ejemplo y no encontré NADA

+0

¿Por qué? ¿Que estás tratando de hacer? – SLaks

+0

Pregunta relacionada http://stackoverflow.com/questions/1087982/ –

+0

Agregué algunos enlaces al código de muestra a mi respuesta. –

Respuesta

1

Estoy tan feliz! :) Recientemente jugué alrededor del problema del tema: intenté resolverlo usando la base de datos, pero solo encontré que esta manera está lejos de ser perfecta. Matrix [20000,20000] se implementó como una sola tabla. Incluso con índices configurados correctamente, el tiempo requerido solo para crear más de 400 millones de registros es de aproximadamente 1 hora en mi PC. No es crítico para mi Luego ejecuté el algoritmo para trabajar con esa matriz (¡necesito dos veces para unirme a la misma tabla!) Y después de que funcionó más de media hora, no dio ni un solo paso. Después de eso, entendí que la única forma es encontrar una manera de trabajar con dicha matriz en memoria solamente y volver a C# nuevamente.

Creé una aplicación piloto para probar el proceso de asignación de memoria y para determinar dónde exactamente el proceso de asignación deja de usar diferentes estructuras.

Como dije en mi primera publicación, es posible asignar usando arreglos de 2 dim solo alrededor de 650MB bajo WinXP de 32 bits. Los resultados después de usar Win7 y la compilación de 64 bits también fueron tristes, menos de 700MB.

I utilizarse ARRAYS Jagged [] [] en lugar de única matriz 2-dim [,] y los resultados se puede ver a continuación:

Compilado en modo de lanzamiento como aplicación de 32 bits - 32 bits Phys WinXP 3GB. mem. - 1.45GB Compilado en modo de lanzamiento como aplicación de 64 bits - 64 bits Win7 2 GB bajo VM - 7.5GB

--sources de aplicación que he utilizado para la prueba están unidos a este mensaje. No encuentro aquí cómo adjuntar archivos fuente así que simplemente describa la parte del diseño y coloque aquí el código manual. Crear aplicación WinForms. Ponga en forma dichos controles con los nombres predeterminados: 1 botón, 1 numérico Arriba y 1 cuadro de lista En el archivo .cs, agregue el código siguiente y ejecútelo.

private void button1_Click(object sender, EventArgs e) 
     { 
      //Log(string.Format("Memory used before collection: {0}", GC.GetTotalMemory(false))); 
      GC.Collect(); 
      //Log(string.Format("Memory used after collection: {0}", GC.GetTotalMemory(true))); 
      listBox1.Items.Clear(); 
      if (string.IsNullOrEmpty(numericUpDown1.Text)) { 
       Log("Enter integer value"); 
      }else{ 
       int val = (int) numericUpDown1.Value; 
       Log(TryAllocate(val)); 
      } 
     } 

     /// <summary> 
     /// Memory Test method 
     /// </summary> 
     /// <param name="rowLen">in MB</param> 
     private IEnumerable<string> TryAllocate(int rowLen) { 
      var r = new List<string>(); 
      r.Add (string.Format("Allocating using jagged array with overall size (MB) = {0}", ((long)rowLen*rowLen*Marshal.SizeOf(typeof(int))) >> 20)); 
      try { 
       var ar = new int[rowLen][]; 
       for (int i = 0; i < ar.Length; i++) { 
        try { 
         ar[i] = new int[rowLen]; 
        } 
        catch (Exception e) { 
         r.Add (string.Format("Unable to allocate memory on step {0}. Allocated {1} MB", i 
          , ((long)rowLen*i*Marshal.SizeOf(typeof(int))) >> 20)); 
         break; 
        } 
       } 
       r.Add("Memory was successfully allocated"); 
      } 
      catch (Exception e) { 
       r.Add(e.Message + e.StackTrace); 
      } 
      return r; 
     } 

     #region Logging 

     private void Log(string s) { 
      listBox1.Items.Add(s); 
     } 

     private void Log(IEnumerable<string> s) 
     { 
      if (s != null) { 
       foreach (var ss in s) { 
        listBox1.Items.Add (ss); 
       } 
      } 
     } 

     #endregion 

El problema está resuelto para mí. Chicos, gracias de antemano!

5

¿Por qué demandar una enorme matriz 2-D? Puede simular esto con, por ejemplo, una matriz dentada - ushort[][] - casi tan rápido, y no alcanzará el mismo límite de objeto individual. No obstante, deberá cubos-o-RAM, por supuesto, por lo que se da a entender x64 ...

 ushort[][] arr = new ushort[size][]; 
     for(int i = 0 ; i < size ; i++) { 
      arr[i] = new ushort[size]; 
     } 

Además de lo cual - es posible que desee mirar a escasos-arrays, ETA-vectores, y todo eso.

+0

También traté de usar array irregular de la misma manera, pero obtuve OutOfMemoryException - No sé en qué paso - Creo que algo cerca de 650MB :) – Cheburek

+0

@ 4eburek - curioso, pero difícil para nosotros para reproducir su configuración, por desgracia. Deberías poder obtener un poco más que eso, pero cambiar a x64 sería mejor aquí. –

+0

@Marc: las matrices dentadas son más rápidas que las matrices 2D –

0

Si matriz dispersa no se aplica, es probablemente mejor que sólo lo hacen en C/C++ con APIs de la plataforma relacionados con la memoria de archivos de mapeado: http://en.wikipedia.org/wiki/Memory-mapped_file

+1

.NET Framework 4.0 viene con clases para usar archivos mapeados de memoria. http://msdn.microsoft.com/en-us/library/system.io.memorymappedfiles(VS.100).aspx – dtb

+0

@dtb: Creo que es mucho más lento que usar una base de datos relacional, pero de todos modos, gracias. – Cheburek

+0

@dtb: ¡qué bueno saber! – Codism

0

Si usted explicó lo que está tratando de hacerlo sería más fácil ayuda. Tal vez hay mejores formas de asignar tanta cantidad de memoria a la vez.

Re-diseño es también la elección número uno en esta gran entrada de blog:

BigArray, getting around the 2GB array size limit

Las opciones sugeridas en este artículo son:

+0

Gracias por su respuesta. Sí, ya vi BigArray pero no lo intenté, parece que usa matrices irregulares. Como escribí en el comentario a la respuesta anterior, he intentado usar matrices irregulares, son un poco más lentas en el escenario de la inicialización, pero aun así no puedo asignar toda la memoria requerida, arroja la excepción OutOfMemory. Lo último que vino a la cabeza, tal vez algo malo con mi instancia de sistema operativo y la necesidad de probar con otra computadora. Porque no puedo explicar por qué cuando otros hombres hablan de 2 GB, incluso no puedo asignar 700Mb – Cheburek

4

La razón por la que no puede acercarse ni siquiera a la asignación de 2 Gb en Windows de 32 bits es que las matrices en el CLR están dispuestas en la memoria contigua. En Windows de 32 bits tiene un espacio de direcciones tan restringido que no encontrará nada como un orificio de 2 Gb en el espacio de direcciones virtuales del proceso. Sus experimentos sugieren que la mayor región de espacio de direcciones disponible es de 650Mb. Pasar a Windows de 64 bits debería al menos permitirle usar una asignación de 2Gb completa.

Tenga en cuenta que la limitación del espacio de direcciones virtuales en Windows de 32 bits no tiene nada que ver con la cantidad de memoria física que tiene en su computadora, en su caso 3Gb. En cambio, la limitación está causada por la cantidad de bits que usa la CPU para direccionar direcciones de memoria. Windows de 32 bits utiliza, como era de esperar, 32 bits para acceder a cada dirección de memoria, lo que da un espacio total de memoria direccionable de 4 GB. De manera predeterminada, Windows mantiene 2Gb por sí mismo y otorga 2Gb al proceso en ejecución actualmente, por lo que puede ver por qué el CLR no encontrará nada parecido a una asignación de 2Gb. Con algunos trucos, puede cambiar la asignación de OS/usuario para que Windows solo conserve 1Gb por sí mismo y le proporcione el proceso en ejecución 3Gb que podría ser útil. Sin embargo, con las ventanas de 64 bits, la memoria direccionable asignada a cada proceso salta hasta 8 Terabytes, por lo que aquí el CLR casi con certeza podrá usar asignaciones completas de 2 Gb para las matrices.

+0

Intenté usar Windows 7 de 64 bits sin éxito. Pero fue en una PC virtual bajo el mismo WinXP, por lo que no puedo garantizar que los resultados sean correctos – Cheburek

+0

No es un entorno con el que esté familiarizado, sin embargo, hay que verificar que su código esté compilado de manera que se ejecute como un ejecutable x64 nativo. en Windows de 64 bits. Esta página de MSDN http://msdn.microsoft.com/en-us/library/ms241064%28VS.80%29.aspx proporciona más información. –

+0

Debajo de win7 usé VS2010 y en la configuración gestioné el modo de lanzamiento seleccionado y el tipo de procesadores x64. Esa es la respuesta rápida, pero necesito verificar si hay todos los pasos necesarios para compilar bajo la plataforma de 64 bits – Cheburek

Cuestiones relacionadas