VS2010 быстрая магия memcpy

Привет (и извините за мой плохой английский), для некоторых проблем с переносимостью мне нужно написать себе функцию копирования памяти. Но моя лучшая попытка сделать это на 40-70% медленнее, чем стандартный memcpy Visual Studio 2010 года. И я не знаю, почему. Далее вы можете увидеть мой основной цикл копирования, который копирует все 128-байтовые фрагменты данных (весь остальной код из функции ограничен по количеству операций и может быть принят как O (1))

MOVDQA XMM0,DQWORD PTR DS:[ESI]
MOVDQA XMM1,DQWORD PTR DS:[ESI+10]
MOVDQA XMM2,DQWORD PTR DS:[ESI+20]
MOVDQA XMM3,DQWORD PTR DS:[ESI+30]
MOVDQA DQWORD PTR DS:[EDI],XMM0
MOVDQA DQWORD PTR DS:[EDI+10],XMM1
MOVDQA DQWORD PTR DS:[EDI+20],XMM2
MOVDQA DQWORD PTR DS:[EDI+30],XMM3
MOVDQA XMM4,DQWORD PTR DS:[ESI+40]
MOVDQA XMM5,DQWORD PTR DS:[ESI+50]
MOVDQA XMM6,DQWORD PTR DS:[ESI+60]
MOVDQA XMM7,DQWORD PTR DS:[ESI+70]
MOVDQA DQWORD PTR DS:[EDI+40],XMM4
MOVDQA DQWORD PTR DS:[EDI+50],XMM5
MOVDQA DQWORD PTR DS:[EDI+60],XMM6
MOVDQA DQWORD PTR DS:[EDI+70],XMM7
LEA ESI,[ESI+80]
LEA EDI,[EDI+80]
DEC ECX
JNE SHORT 002410B9

А дальше я нашел в стандартном memcpy

MOVDQA XMM0,DQWORD PTR DS:[ESI]
MOVDQA XMM1,DQWORD PTR DS:[ESI+10]
MOVDQA XMM2,DQWORD PTR DS:[ESI+20]
MOVDQA XMM3,DQWORD PTR DS:[ESI+30]
MOVDQA DQWORD PTR DS:[EDI],XMM0
MOVDQA DQWORD PTR DS:[EDI+10],XMM1
MOVDQA DQWORD PTR DS:[EDI+20],XMM2
MOVDQA DQWORD PTR DS:[EDI+30],XMM3
MOVDQA XMM4,DQWORD PTR DS:[ESI+40]
MOVDQA XMM5,DQWORD PTR DS:[ESI+50]
MOVDQA XMM6,DQWORD PTR DS:[ESI+60]
MOVDQA XMM7,DQWORD PTR DS:[ESI+70]
MOVDQA DQWORD PTR DS:[EDI+40],XMM4
MOVDQA DQWORD PTR DS:[EDI+50],XMM5
MOVDQA DQWORD PTR DS:[EDI+60],XMM6
MOVDQA DQWORD PTR DS:[EDI+70],XMM7
LEA ESI,[ESI+80]
LEA EDI,[EDI+80]
DEC EDX
JNE SHORT 6B150A72

Как видите, этот цикл почти идентичен моему, но моя функция становится все медленнее и медленнее (по сравнению со стандартным memcpy) с увеличением объема копируемых данных.

Кто-нибудь может ответить, где моя ошибка?

P.S. Это мой код из main()

void main(void){    

LARGE_INTEGER freq;
QueryPerformanceFrequency(&freq);

int* mas = new int[10000000];
for(int i = 0; i < 10000000; ++i)
    mas[i] = i;

LARGE_INTEGER mmcpy = { 0 };
LARGE_INTEGER mmsse = { 0 };

for(int i = 0; i < 10000; ++i)
{
    LARGE_INTEGER beforeMemcpy_sse, afterMemcpy_sse;
    QueryPerformanceCounter(&beforeMemcpy_sse);
    TestMemcpy_sse(mas, (char*)mas + 300000, 4400000);
    QueryPerformanceCounter(&afterMemcpy_sse);

    LARGE_INTEGER beforeMemcpy, afterMemcpy;
    QueryPerformanceCounter(&beforeMemcpy);
    memcpy(mas, (char*)mas + 300000, 4400000);
    QueryPerformanceCounter(&afterMemcpy);

    mmcpy.QuadPart += afterMemcpy.QuadPart - beforeMemcpy.QuadPart ;
    mmsse.QuadPart += afterMemcpy_sse.QuadPart - beforeMemcpy_sse.QuadPart;
}

delete[] mas;

/*printf("Memcpy Time: %f\n", (afterMemcpy.QuadPart - beforeMemcpy.QuadPart) / (float)freq.QuadPart);
printf("SSE Memcpy Time: %f\n\n", (afterMemcpy_sse.QuadPart - beforeMemcpy_sse.QuadPart) / (float)freq.QuadPart);*/

printf("Memcpy Time: %f\n", mmcpy.QuadPart / ((float)freq.QuadPart * 10000));
printf("SSE Memcpy Time: %f\n\n", mmsse.QuadPart / ((float)freq.QuadPart * 10000));

system("pause");

}

person ForceKeeper    schedule 08.09.2011    source источник
comment
Нам определенно нужно знать больше о контексте. Где/как это называется?   -  person Mysticial    schedule 09.09.2011
comment
Хорошо, посмотрите в конце моего отредактированного поста   -  person ForceKeeper    schedule 09.09.2011
comment
Каковы результаты ваших измерений? И копирование всего один раз не очень хорошо для проверки любой производительности. Вы должны скопировать еще несколько тысяч раз, а затем сообщить нам результат.   -  person RedX    schedule 09.09.2011
comment
@RedX О, я отредактировал свою основную функцию, чтобы запускать 10000 раз каждую функцию копирования памяти и получать следующий усредненный результат (пожалуйста, проверьте мою основную функцию, я новичок в этой 64-битной математике) ( Memcpy Time: 0.000268 ; SSE Memcpy Время: 0.000268 ) ******** а почему при нескольких ручных запусках проги без цикла у меня результат, говорящий о mmcpy гораздо большей производительности?   -  person ForceKeeper    schedule 09.09.2011
comment
подсказки предварительной выборки могут помочь улучшить вашу производительность. Кроме того, вы должны выровнять ветки, это ускорит их работу.   -  person Necrolis    schedule 09.09.2011
comment
@Necrolis ветки данных или кода?   -  person ForceKeeper    schedule 09.09.2011
comment
@forcekeeper: Intel рекомендует выровнять цели ветки кода (iirc соответствует размеру слова процессора, но лучше проверить это в руководстве Intel по оптимизации), это делает сам код более удобным для кэширования.   -  person Necrolis    schedule 09.09.2011


Ответы (2)


Возможно, вы видите эффекты кеша. В зависимости от размера вашего кеша вы можете копировать подмножество массива mas с холодным кешем в своем первом тесте с помощью функции memcpy, а затем видеть теплый кеш при тестировании встроенного memcpy.

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

person mattnewport    schedule 08.09.2011

Это связано с тем, что второй memcpy обращается к данным с подогревом кеша (подогретым первым memcpy). Вы копируете в области 5 МБ, а затем снова копируете в нее — ваш кеш L3, вероятно, составляет 6–12 МБ. Попробуйте поменять порядок копий и посмотреть, какие результаты вы получите. :-)

person Crowley9    schedule 08.09.2011