Изменить. Для справочных целей (если кто-то наткнулся на этот вопрос), Игорь Островский написал отличный пост промахи кэша. В нем обсуждаются несколько различных проблем и показаны номера примеров. Редактировать конец
Я провел некоторое тестирование <long story goes here>
, и мне интересно, отличается ли разница в производительности от ошибок в кэше памяти. Следующий код демонстрирует проблему и сводит ее к важной части синхронизации. Следующий код имеет пару циклов, которые посещают память в случайном порядке, а затем в порядке возрастания адресов.
Я запустил его на компьютере XP (скомпилирован с VS2005: cl/O2) и на Linux-боксе (gcc -Os). Оба произвели аналогичные времена. Эти времена находятся в миллисекундах. Я считаю, что все циклы работают и не оптимизированы (в противном случае он будет запускаться "мгновенно" ).
*** Testing 20000 nodes Total Ordered Time: 888.822899 Total Random Time: 2155.846268
Имеют ли эти цифры смысл? Разница в основном связана с промахом кэша L1 или что-то еще происходит? Доступ к памяти составляет 20 000 ^ 2, и если каждый из них был пропущен в кеше, то это около 3,2 наносекунды за промаху. Машина XP (P4), на которой я тестировал, составляет 3,2 ГГц, и я подозреваю (но не знаю) имеет кэш L1 32 Кбайт и 512 КБ L2. С 20 000 записей (80 КБ), я предполагаю, что нет значительного количества промахов L2. Таким образом, это будет (3.2*10^9 cycles/second) * 3.2*10^-9 seconds/miss) = 10.1 cycles/miss
. Это кажется мне большим. Может быть, это не так, или, может быть, моя математика плоха. Я попытался измерить промахи кэша с VTune, но у меня есть BSOD. И теперь я не могу его подключить к серверу лицензий (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 );
}