性能:memset
Posted
技术标签:
【中文标题】性能:memset【英文标题】:Performance: memset 【发布时间】:2014-07-06 13:20:04 【问题描述】:我有执行此操作的简单 C 代码(伪代码):
#define N 100000000
int *DataSrc = (int *) malloc(N);
int *DataDest = (int *) malloc(N);
memset(DataSrc, 0, N);
for (int i = 0 ; i < 4 ; i++)
StartTimer();
memcpy(DataDest, DataSrc, N);
StopTimer();
printf("%d\n", DataDest[RandomInteger]);
我的电脑:Intel Core i7-3930,配备 4x4GB DDR3 1600 内存,运行 RedHat 6.1 64 位。
第一个 memcpy()
以 1.9 GB/秒的速度出现,而接下来的三个以 6.2 GB/秒的速度出现。
缓冲区大小 (N
) 太大,这不是由缓存效应引起的。所以,我的第一个问题:
memcpy()
这么慢?也许malloc()
直到你使用它才完全分配内存?
如果我消除memset()
,那么第一个memcpy()
的运行速度约为 1.5 GB/秒,
但接下来的三个以 11.8 GB/秒的速度运行。几乎 2 倍的加速。我的第二个问题:
memset()
,为什么 memcpy()
会快 2 倍?
【问题讨论】:
如果你从一个未初始化的源中 memcpy 不是 UB 吗?您使用什么编译器进行了哪些优化?通过将数据大小增加 10 倍或更多,使计时更可靠。 @usr 数据是随机的,没有ub,只要你不以可能引入ub的方式使用数据。示例中没有可以执行此操作的代码。 顺便说一句:11.8GB/s 的总线速度对我来说似乎有点太快了。 @usr 读取未初始化的变量不会触发 ub,错误地使用该值会触发。例如,使用该值访问数组偏移量将触发 ub。我猜技术上(标准)你是对的。 这可能是正确的,但 OP 特别提到了 gcc 和 linux。此外:int 没有可能的陷阱表示(并且从不使用 int,只复制)否则从未知磁盘文件中读取随机 dat 也可能导致问题。 【参考方案1】:正如其他人已经指出的那样,Linux 使用optimistic memory allocation strategy。
第一个memcpy
s和后面的memcpy
s的区别在于DataDest
的初始化。
正如您已经看到的,当您消除memset(DataSrc, 0, N)
时,第一个memcpy
会更慢,因为也必须分配源页面。当您同时初始化DataSrc
和 DataDest
时,例如
memset(DataSrc, 0, N);
memset(DataDest, 0, N);
所有memcpy
s 将以大致相同的速度运行。
对于第二个问题:当您使用memset
初始化分配的内存时,所有页面将连续布局。另一方面,当您在复制时分配内存时,源页面和目标页面将交错分配,这可能会有所不同。
【讨论】:
很棒的答案@Olaf Dietsche!【参考方案2】:这很可能是由于您的 VM 子系统中的延迟分配造成的。通常,当您分配大量内存时,只有前 N 页实际分配并连接到物理内存。当您访问超过前 N 个页面时,会生成页面错误,并且会根据“按需”分配和连接更多页面。
至于问题的第二部分,我相信一些虚拟机实现实际上会跟踪归零页面并专门处理它们。尝试将 DataSrc
初始化为实际(例如随机)值并重复测试。
【讨论】:
+1 - 'Dirtying'(写入)所有页面事先确实应该让事情变得清晰,可以尝试calloc()
:***.com/q/1538420/1175253
@Sam:在我修复之前,该链接问题的最佳答案是不正确的;大多数主流操作系统上的calloc
从内核中获取零页面,因此它们仍然是延迟分配的,并且在读取或写入时会出现页面错误。以上是关于性能:memset的主要内容,如果未能解决你的问题,请参考以下文章