VS2010 memcpy 快速魔法

发布于 2024-12-03 10:16:00 字数 2849 浏览 2 评论 0原文

你好(很抱歉我的英语不好),对于一些可移植性问题,我需要自己编写一个内存复制函数。但我的最佳尝试比 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 相比)。

谁能回答我的错误在哪里?

聚苯乙烯 这是我的 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");

}

Hello (and sorry for my bad english), for some portability issues i need to write myself a memory copy function. But my best attempt do this is 40-70% slower than visual studio's 2010 standard memcpy. And i don't know why. Next you can see my main copy loop, that copy all 128 byte chunks of data (all other code from function is limited in number of operations and can be assumed as 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

And next i found in standard 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

As you can see this cycle is almost identical to my own, but my function is getting slower and slower (in comparison to std memcpy) with increasing amount of data to copy.

Can anyone answer where is my mistake?

P.S.
That's my code from 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");

}

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

一枫情书 2024-12-10 10:16:00

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

It's because the second memcpy is accessing cache warmed data (warmed by the first memcpy). You copy within a 5MB region, and then you copy within it again - your L3 cache is likely 6MB-12MB. Try switch the order of the copies, and see what results you get. :-)

盛夏尉蓝 2024-12-10 10:16:00

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

通常,在测量此类代码的性能时,您应该对多次运行进行平均,并通过使用比缓存大得多的数据集或故意小到足以适合缓存的数据集进行测试,并在之前预热缓存,小心避免缓存效应。测试。

You might be seeing cache effects. Depending on the size of your cache you could be copying a subset of the mas array with the cache cold in your first test with your memcpy function and then seeing a warm cache when you test the built in memcpy.

Generally when measuring the performance of code like this you should average over many runs and be careful to avoid cache effects by testing with a data set much larger than your cache or with one that is deliberately small enough to fit in cache and warming the cache before testing.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文