Jaki jest koszt Miss L1 Cache?

Edytować: Dla celów odniesienia (jeśli ktoś natknie się na to pytanie), Igor Ostrovsky napisałwspaniały post o brakach pamięci podręcznej. Omawia kilka różnych problemów i pokazuje przykładowe numery.Zakończ edycję

Zrobiłem kilka testów<long story goes here> i zastanawiam się, czy różnica w wydajności jest spowodowana brakiem pamięci podręcznej. Poniższy kod demonstruje problem i sprowadza go do krytycznej części czasu. Poniższy kod zawiera kilka pętli, które odwiedzają pamięć w kolejności losowej, a następnie w rosnącej kolejności adresów.

Uruchomiłem go na komputerze XP (skompilowanym z VS2005: cl / O2) i na Linux-ie (gcc –Os). Oba wyprodukowały podobne czasy. Czasy te są w milisekundach. Uważam, że wszystkie pętle są uruchomione i nie są zoptymalizowane (w przeciwnym razie uruchomiłoby się to „natychmiast”).

*** Testing 20000 nodes
Total Ordered Time: 888.822899
Total Random Time: 2155.846268

Czy te liczby mają sens? Czy różnica wynika przede wszystkim z chybienia pamięci podręcznej L1 lub czy dzieje się coś innego? Dostępnych jest 20 000 ^ 2 dostępów do pamięci, a jeśli każdy z nich był brakiem pamięci podręcznej, to jest to około 3,2 nanosekundy na miss. Maszyna XP (P4), na której testowałem, to 3,2 GHz i podejrzewam (ale nie wiem), że ma pamięć podręczną L1 o pojemności 32 KB i L2 o pojemności 512 KB. Przy 20 000 wpisów (80 KB) zakładam, że nie ma znacznej liczby braków L2. Więc tak będzie(3.2*10^9 cycles/second) * 3.2*10^-9 seconds/miss) = 10.1 cycles/miss. Wydaje mi się to wysokie. Może tak nie jest, a może moja matematyka jest zła. Próbowałem pomijać pamięć podręczną w VTune, ale dostałem BSOD. A teraz nie mogę go połączyć z serwerem licencji (grrrr).

typedef struct stItem
{
   long     lData;
   //char     acPad[20];
} LIST_NODE;



#if defined( WIN32 )
void StartTimer( LONGLONG *pt1 )
{
   QueryPerformanceCounter( (LARGE_INTEGER*)pt1 );
}

void StopTimer( LONGLONG t1, double *pdMS )
{
   LONGLONG t2, llFreq;

   QueryPerformanceCounter( (LARGE_INTEGER*)&t2 );
   QueryPerformanceFrequency( (LARGE_INTEGER*)&llFreq );
   *pdMS = ((double)( t2 - t1 ) / (double)llFreq) * 1000.0;
}
#else
// doesn't need 64-bit integer in this case
void StartTimer( LONGLONG *pt1 )
{
   // Just use clock(), this test doesn't need higher resolution
   *pt1 = clock();
}

void StopTimer( LONGLONG t1, double *pdMS )
{
   LONGLONG t2 = clock();
   *pdMS = (double)( t2 - t1 ) / ( CLOCKS_PER_SEC / 1000 );
}
#endif



long longrand()
{
   #if defined( WIN32 )
   // Stupid cheesy way to make sure it is not just a 16-bit rand value
   return ( rand() << 16 ) | rand();
   #else
   return rand();
   #endif
}

// get random value in the given range
int randint( int m, int n )
{
   int ret = longrand() % ( n - m + 1 );
   return ret + m;
}

// I think I got this out of Programming Pearls (Bentley).
void ShuffleArray
(
   long *plShuffle,  // (O) return array of "randomly" ordered integers
   long lNumItems    // (I) length of array
)
{
   long i;
   long j;
   long t;

   for ( i = 0; i < lNumItems; i++ )
      plShuffle[i] = i;

   for ( i = 0; i < lNumItems; i++ )
      {
      j = randint( i, lNumItems - 1 );

      t = plShuffle[i];
      plShuffle[i] = plShuffle[j];
      plShuffle[j] = t;
      }
}



int main( int argc, char* argv[] )
{
   long          *plDataValues;
   LIST_NODE     *pstNodes;
   long          lNumItems = 20000;
   long          i, j;
   LONGLONG      t1;  // for timing
   double dms;

   if ( argc > 1 && atoi(argv[1]) > 0 )
      lNumItems = atoi( argv[1] );

   printf( "\n\n*** Testing %u nodes\n", lNumItems );

   srand( (unsigned int)time( 0 ));

   // allocate the nodes as one single chunk of memory
   pstNodes = (LIST_NODE*)malloc( lNumItems * sizeof( LIST_NODE ));
   assert( pstNodes != NULL );

   // Create an array that gives the access order for the nodes
   plDataValues = (long*)malloc( lNumItems * sizeof( long ));
   assert( plDataValues != NULL );

   // Access the data in order
   for ( i = 0; i < lNumItems; i++ )
      plDataValues[i] = i;

   StartTimer( &t1 );

   // Loop through and access the memory a bunch of times
   for ( j = 0; j < lNumItems; j++ )
      {
      for ( i = 0; i < lNumItems; i++ )
         {
         pstNodes[plDataValues[i]].lData = i * j;
         }
      }

   StopTimer( t1, &dms );
   printf( "Total Ordered Time: %f\n", dms );

   // now access the array positions in a "random" order
   ShuffleArray( plDataValues, lNumItems );

   StartTimer( &t1 );

   for ( j = 0; j < lNumItems; j++ )
      {
      for ( i = 0; i < lNumItems; i++ )
         {
         pstNodes[plDataValues[i]].lData = i * j;
         }
      }

   StopTimer( t1, &dms );
   printf( "Total Random Time: %f\n", dms );

}

questionAnswers(8)

yourAnswerToTheQuestion