2010-12-09 8 views
5

dieron un poco de código simplematriz cuestión de metadatos (líneas de caché)

Int32[] tmpInt = new Int32[32]; 

      long lStart = DateTime.Now.Ticks; 


      Thread t1 = new Thread(new ThreadStart(delegate() 
      { 
       for (Int32 i = 0; i < 100000000; i++) 
        Interlocked.Increment(ref tmpInt[5]); 
      })); 

      Thread t2 = new Thread(new ThreadStart(delegate() 
      { 
       for (Int32 i = 0; i < 100000000; i++) 
        Interlocked.Increment(ref tmpInt[20]); 
      })); 


      t1.Start(); 
      t2.Start(); 

      t1.Join(); 
      t2.Join(); 

      Console.WriteLine(((DateTime.Now.Ticks - lStart)/10000).ToString()); 

Esto toma ~ 3 segundos en mi core 2 duo. Si cambio el índice en t1 a tmpInt [4], tarda ~ 5.5 segundos.

De todos modos, la primera línea de caché termina en el índice 4. Siendo que una línea de caché es de 64bytes y 5 int32s son solo 20 bytes, eso significa que hay 44 bytes de metadatos y relleno antes de la matriz real.

Otro conjunto de valores que probé donde 5 y 21. 5 y 21 toman ~ 3 segundos, pero 5 y 20 tardan ~ 5.5 segundos, pero eso se debe a que el índice 20 comparte la misma línea de caché que el índice 5, ya que espaciado dentro de los mismos 64 bytes.

Así que mi pregunta es, ¿cuántos datos se reserva .Net antes de una matriz y esta cantidad cambia entre los sistemas de 32 bits y 64 bits?

Gracias :-)

+3

Parece que saca muchas conclusiones firmes de un código simple. Otros factores podrían estar involucrados. –

+0

Este es un comportamiento esperado de la coherencia de la memoria caché al intentar actualizar líneas sucias.Además, no solo es un comportamiento esperado de lo que estoy buscando, sino que no puedo pensar en otros procesos que causarían un gran cambio en la diferencia para las operaciones atómicas que están dentro de los mismos 64 bytes. – Bengie

+5

También un comentario sobre el código, use 'System.Diagnostics.Stopwatch' al código de tiempo. 'DateTime' simplemente no es confiable y es un mal hábito (incluso si te acerca en este ejemplo). –

Respuesta

4

Cuando la CPU intenta cargar la matriz y sufre una caché pierdan obtiene el bloque de memoria que contiene la matriz, pero no necesariamente A partir de IT. .NET no garantiza que su matriz esté alineada en caché.

Para responder a su pregunta, los 44 bytes de relleno son principalmente otros datos de la página asociada que pasó a estar en la misma línea de caché.

editar: http://msdn.microsoft.com/en-us/magazine/cc163791.aspx Parece indicar que una matriz tiene 16 bytes de almacenamiento adicional. 4 bytes son el índice de bloque de sincronización, 4 bytes se utilizan para los metadatos de manejador de tipo, y el resto es el objeto mismo.

Como comentario adicional, es difícil decir exactamente que compartir falsamente es responsable de su retraso aquí. Es probable que tenga en cuenta los tiempos, pero debe usar un buen generador de perfiles para examinar la tasa de error de caché. Si salta alto para su caso dado, puede estar bastante seguro de que está viendo compartir falsamente en el juego.

+0

Encontré una máquina que podría usar con el generador de perfiles VS2010. La sobrecarga de sincronización para la prueba con una extensión de más de 16 bytes fue consistentemente un poco más baja, pero no tanto como esperaba. Lo que sí encontré fue que el tiempo de bloqueo de fallas de la página Kernel era más de 10 veces superior para la prueba donde esperaba compartir la línea de caché. – Bengie

+0

Me parece raro que haya sido categorizado en "error de página", pero este fue un sistema con 4 GB de memoria RAM y todas las demás aplicaciones cerradas. No se estaba produciendo ningún intercambio. – Bengie

+1

Otra nota interesante es que no importa cuántas veces corra, tmpInt [4] parece estar en una línea de caché diferente que tmpInt [5]. La asignación de memoria siempre debe asignarse alineada al comienzo de una línea de caché por asignación. – Bengie

0

Además de la respuesta aquí: https://stackoverflow.com/a/1589806/543814

Mis pruebas indican lo que esperaba, en 32 bits [64 bits]:

  • bloque de sincronización: 4B [8B]
  • tipo matriz de punteros: 4B [8B]
  • Tamaño de matriz: 4B [4B]
  • tipo Element puntero: 4B [8B] (matrices de referencia única)

En conclusión, hay 4 posibilidades:

12 bytes (32-bit value array) 
16 bytes (32-bit reference array) 
20 bytes (64-bit value array) 
28 bytes (64-bit reference array) 

Algo que echaba de menos en el pasado: en una máquina de 64 bits con la configuración de proyecto 'prefieren de 32 bits 'habilitado (predeterminado), ¡Aplica 32 bits!