2012-01-03 26 views
6

Tengo un problema que me ha estado molestando por unos días. Intenté buscar el problema en Google, pero hasta ahora no he podido encontrar ninguna solución, ni siquiera una sola persona con el mismo problema.C# System.Buffer.BlockCopy ¿Problema de memoria?

Parece que el método C# System.Buffer.BlockCopy te deja con algún tipo de fantasmas de memoria. Tengo, por ejemplo, este método:

private float[,] readFloatArray2 (byte[] b) { 
    int floatSize = sizeof(float); 
    float[,] v = new float[2, (b.Length/2)/floatSize]; 
    System.Buffer.BlockCopy(b, 0, v, 0, b.Length); 

    return v; 
} 

para convertir una matriz de bytes en una matriz de flotación 2D. Los datos se leen previamente de una secuencia. He localizado el problema para que sea el método System.Buffer.BlockCopy.

Si elimino el comando BlockCopy, la memoria utilizada por la aplicación tendrá la mitad del tamaño. esto significa que no es mi culpa que la matriz de bytes todavía esté viva. porque sin el comando BlockCopy, la matriz de bytes muere correctamente. la matriz flotante se crea de todos modos (con o sin la información copiada).

No estoy seguro si esto es un problema del comando BlockCopy o del GC porque también he intentado llamar a System.GC.Collect(); después de BlockCopy y luego también funciona perfectamente (sé que no deberías hacer esto ... por eso estoy pidiendo consejo aquí).

Tampoco me molestaría en preguntar, pero el problema involucra varios cientos de megas.

Además de los problemas de memoria, el método funciona perfectamente bien. ¿Alguien sabe qué causa el problema de memoria?

saludos y gracias de antemano oli

PS: Estoy utilizando .NET4.0 con Visual Studio 2010 PRO y Win7 ... no saben si esto es relevante o no.

+2

Si dice que la memoria se recoge correctamente con un 'GC.Collect', todo está bien. 'b' * * será eventualmente recolectado por un GC normal cuando llegue el momento. –

+0

Si está trabajando en datos de audio, usaría una matriz dentada de la forma 'float [len] [channelCount]'. De esta forma, puede tratar los canales por separado, lo que a veces es útil. – CodesInChaos

+1

"la matriz flotante se crea de todos modos" Solo la mitad de verdad. No necesariamente necesita memoria física todavía. Las páginas de memoria que son todas 0 y nunca se han escrito en ellas, están optimizadas por el administrador de memoria de Windows. – CodesInChaos

Respuesta

1

BlockCopy no tiene la implementación de .NET administrada. Internamente, invoca la API de victoria externa.

[SecuritySafeCritical] 
public static extern void BlockCopy(Array src, int srcOffset, Array dst, int dstOffset, int count); 
+0

bien ... y cómo lidiar con este problema? ¿que sugieres? –

+0

Nada. GC se encargará eventualmente cuando necesite la memoria extra. –

1

Buffer.BlockCopy es más que basados ​​en índices basados ​​en bytes. Le sugiero que use Array.Copy, que básicamente hace lo mismo. BlockCopy es solo un poco más rápido.

es necesario convertir el byte [] para flotar [] first.Have un vistazo a la continuación de ese

static float[] ConvertByteArrayToFloat(byte[] bytes) 
{ 
    if(bytes == null) 
     throw new ArgumentNullException("bytes"); 

    if(bytes.Length % 4 != 0) 
     throw new ArgumentException 
       ("bytes does not represent a sequence of floats"); 

    return Enumerable.Range(0, bytes.Length/4) 
        .Select(i => BitConverter.ToSingle(bytes, i * 4)) 
        .ToArray(); 
} 
+0

Pero Array.Copy no funciona con diferentes tipos de datos. No puedo usarlo para copiar de byte [] a float [,]. –

+0

He editado mi respuesta –

+0

sí ... vale ... está genial, pero tengo una matriz de bytes 1D en una matriz de flotación 2D (flotación [,]). Pero tu sugerencia es genial. No sabía esto. (Codificando en C# por 4 meses ahora) –

2

He localizado el problema a ser el método System.Buffer.BlockCopy. Si elimino el comando BlockCopy, la memoria utilizada por la aplicación será la mitad del tamaño. esto significa que no es mi culpa que la matriz de bytes todavía esté viva. Porque sin el comando BlockCopy, la matriz de bytes muere correctamente.

No estoy de acuerdo con esta conclusión. Veo varias fases: existe

gama
  1. El byte y está lleno de datos
  2. Se asigna la red de flotadores, pero no está lleno de datos todavía.
  3. se llena el conjunto de flotador con los datos
  4. La matriz de bytes no se hace referencia más, pero no se ha recogido todavía
  5. La matriz de bytes que se ha recogido.

La vida de la matriz de bytes no está influenciada por BlockCopy.

Paso 2 reservas y compromete a la memoria virtual. Entonces, el tamaño de compromiso crece en este paso. Pero dado que el contenido de la matriz nunca se ha escrito en, y consiste completamente en 00 bytes, el administrador de memoria de Windows no asigna ninguna memoria física para ello. Simplemente observa que estas páginas consisten completamente en 00 s.

La memoria física de la matriz flotante solo se asigna en el paso 3. Obtendrá el mismo efecto si agrega un bucle que inicializara todos los campos de la matriz.


Aparte del problema real, que también tienen algunas sugerencias de diseño:

  1. tampones reutilización. El GC es bueno para objetos pequeños de vida corta, pero muy malo para objetos grandes de corta duración. Esto significa que no debe usar funciones que asignen y devuelvan matrices grandes. En lugar de tomarlos como un parámetro, por lo que una matriz existente se puede reutilizar.
  2. Si está utilizando el trabajo con datos de audio (parece probable), yo no usaría un sólido matriz 2D. En su lugar, usaría una matriz de matrices. Donde la matriz interna representa las muestras en un solo buffer, y la matriz externa representa los buffers. Esto tiene dos ventajas:

    • Puede escribir fácilmente código que solo opera en un solo canal.
    • Las matrices 2D sólidas tardan en indexarse, por lo que a menudo también es más rápido.
  3. ¿De verdad quieres leer todos los datos a la vez? Había leído en trozos de algunos kilobytes.
+0

Soy bioinformática, así que no, no trabajo con archivos de audio. Más como con los espectros de MS. Necesito los datos completos para el procesamiento y, por lo tanto, las sugerencias de diseño 1.y 2. no son una opción (el paso 1 no es una opción porque la longitud de los bytes leídos difiere drásticamente (de 32 bytes a> 10.000.000)). La sugerencia de diseño 3 tampoco es una opción porque tengo que procesar diferentes partes de los datos más de una vez para resaltar diferentes aspectos. volver a cargar todo sobre la marcha, por lo tanto, no es posible porque necesito todo el conjunto de información hasta el final del proceso. pero gracias de todos modos :) –

+0

Oh ... y para la sugerencia 3, no leo TODO de una vez. Leo sobre espectros. Y un tamaño de espectros puede diferir de 32B a> 10MB. Y tengo alrededor de 80,000 espectros para leer. Cada uno de esos es necesario para el procesamiento. –

1

No estoy muy seguro de si esto es un problema de la orden BlockCopy o la GC porque yo también he intentado llamar System.GC.Collect(); después de BlockCopy y luego también funciona perfectamente (sé que no deberías hacer esto ... por eso estoy pidiendo consejo aquí). Tampoco me molestaría en preguntar si no se trata de varios cientos de MB de los que estamos hablando.

La recolección de basura se ejecuta cuando hay una necesidad de más memoria para la generación particular, o de LOH. Como regla, Garbage Collection no se ejecutará solo porque hay basura para recolectar, y como regla, esto es algo bueno (realmente no nos cuesta nada tener gigabytes de memoria oficialmente "en uso" que no estamos usando). siempre que GC pueda obtenerlo cuando lo necesitemos).

Hay ocasiones en las que llamar al GC.Collect() tiene sentido en un programa real, y esta puede ser una de ellas, por lo que si "funciona a la perfección", no me preocuparía demasiado que sea contra el mejor- practica para el 99.9% del código. La razón por la cual es una "mejor práctica" en lugar de una regla rígida es que a veces estamos en el caso del 0.1% y lo que normalmente es la mejor práctica ya no es la mejor práctica.

Además, si se puede predecir de antemano el tamaño máximo de las matrices (o en su defecto, de tan sólo las matrices de bytes de origen), entonces primer acercamiento CodeInChaos' puede trabajar. En realidad, no duele usar 10,000,000 de bytes para procesar 32, siempre que en algún momento use esos 10,000,000. Reutilizar esos 10,000,000 hace un ahorro muy real durante la vida del proceso.

Cuestiones relacionadas