VS2010 memcpy快速魔术

时间:2011-09-08 22:19:31

标签: visual-studio-2010 visual-c++ optimization assembly memcpy

您好(对不起我的英语不好),对于一些可移植性问题,我需要自己写一个内存复制功能。但我最好的尝试是这比visual studio的2010标准memcpy慢40-70%。我不知道为什么。接下来你可以看到我的主复制循环,它复制了所有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

正如您所看到的,这个循环与我自己的循环几乎相同,但随着要复制的数据量不断增加,我的函数变得越来越慢(与std 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");

}

2 个答案:

答案 0 :(得分:5)

这是因为第二个memcpy正在访问缓存加热的数据(由第一个memcpy加热)。您在5MB区域内复制,然后再次在其中复制 - 您的L3缓存可能是6MB-12MB。尝试切换副本的顺序,看看你得到了什么结果。 : - )

答案 1 :(得分:3)

您可能会看到缓存效果。根据缓存的大小,您可以使用memcpy函数在第一次测试中使用缓存冷复制mas数组的子集,然后在测试内置memcpy时查看热缓存。

通常在测量这样的代码性能时,你应该平均多次运行,并且要小心避免缓存效应,方法是使用比缓存大得多的数据集进行测试,或者使用故意小到足以适应缓存和变暖的数据集测试前的缓存。

相关问题