магия, чёрная и белая

Prefetch есть. Он не может не есть.

Было

[code lang="cpp"]

#define DATA_SIZE (16*1024*1024)
#define DATA_MASK (DATA_SIZE-1)
#define BIG_PRIME (113)

double test1 ( double * sdata )
{
double t = 0;
int index = 0;
for ( int i=0; i {
double q = sdata[index];
index = (index + BIG_PRIME) & DATA_MASK;
t += sin(cos(sqrt(sin(cos(sqrt(q))))));
}
return t;
}

[/code]

Стало

[code lang="cpp"]

double test2 ( double * sdata )
{
double t = 0;
int index = 0;
for ( int i=0; i {
double q = sdata[index];
index = (index + BIG_PRIME) & DATA_MASK;
_mm_prefetch ( (const char *) (sdata + index), _MM_HINT_NTA );
t += sin(cos(sqrt(sin(cos(sqrt(q))))));
}

return t;
}

[/code]

У меня 5.36 и 4.56 sec соответственно.

А ещё циклы можно разворачивать.

[code lang="cpp"]

double test11 ( double * sdata )
{
double t = 0;
int index0 = 0;
int index1 = (index0 + BIG_PRIME) & DATA_MASK;
int index2 = (index1 + BIG_PRIME) & DATA_MASK;
int index3 = (index2 + BIG_PRIME) & DATA_MASK;
for ( int i=0; i {
double q1 = sdata[index0];
double q2 = sdata[index1];
double q3 = sdata[index2];
double q4 = sdata[index3];

index0 = (index3 + BIG_PRIME) & DATA_MASK;
index1 = (index0 + BIG_PRIME) & DATA_MASK;
index2 = (index1 + BIG_PRIME) & DATA_MASK;
index3 = (index2 + BIG_PRIME) & DATA_MASK;

t += sin(cos(sqrt(sin(cos(sqrt(q1))))));
t += sin(cos(sqrt(sin(cos(sqrt(q2))))));
t += sin(cos(sqrt(sin(cos(sqrt(q3))))));
t += sin(cos(sqrt(sin(cos(sqrt(q4))))));
}
return t;
}

[/code]

А то, что развернули - немедленно префетчить.

[code lang="cpp"]

double test22 ( double * sdata )
{
double t = 0;
int index0 = 0;
int index1 = (index0 + BIG_PRIME) & DATA_MASK;
int index2 = (index1 + BIG_PRIME) & DATA_MASK;
int index3 = (index2 + BIG_PRIME) & DATA_MASK;
for ( int i=0; i {
double q1 = sdata[index0];
double q2 = sdata[index1];
double q3 = sdata[index2];
double q4 = sdata[index3];

index0 = (index3 + BIG_PRIME) & DATA_MASK;
index1 = (index0 + BIG_PRIME) & DATA_MASK;
index2 = (index1 + BIG_PRIME) & DATA_MASK;
index3 = (index2 + BIG_PRIME) & DATA_MASK;

_mm_prefetch ( (const char *) (sdata + index0), _MM_HINT_T0 );
_mm_prefetch ( (const char *) (sdata + index1), _MM_HINT_T1 );
_mm_prefetch ( (const char *) (sdata + index2), _MM_HINT_T2 );
_mm_prefetch ( (const char *) (sdata + index3), _MM_HINT_NTA );

t += sin(cos(sqrt(sin(cos(sqrt(q1))))));
t += sin(cos(sqrt(sin(cos(sqrt(q2))))));
t += sin(cos(sqrt(sin(cos(sqrt(q3))))));
t += sin(cos(sqrt(sin(cos(sqrt(q4))))));
}
return t;
}

[/code]

У меня 4.43 и 4.64 sec соответственно.

Предсказать результаты не померяв на развернутом цикле у меня не получается. И на неразвёрнутом часто тоже.

Мораль: мерять, глядеть в дизассемблер, потом опять мерять.

P.S. В реальных условиях 4.56 может оказаться быстрее чем 4.43. Из-за ICache.

  • runnig

    я бы не разворачивал цикл так сильно, это плохо сказывается на кэше кода.
    после префетча мое второе правило оптимизации – минимальный размер исполняемого кода. яростное разворачивание циклов ведет к частым перезагрузкам кода из памяти.