性能: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。

第一个memcpys和后面的memcpys的区别在于DataDest的初始化。

正如您已经看到的,当您消除memset(DataSrc, 0, N) 时,第一个memcpy 会更慢,因为也必须分配源页面。当您同时初始化DataSrc DataDest 时,例如

memset(DataSrc, 0, N);
memset(DataDest, 0, N);

所有memcpys 将以大致相同的速度运行。

对于第二个问题:当您使用memset 初始化分配的内存时,所有页面将连续布局。另一方面,当您在复制时分配内存时,源页面和目标页面将交错分配,这可能会有所不同。

【讨论】:

很棒的答案@Olaf Dietsche!【参考方案2】:

这很可能是由于您的 VM 子系统中的延迟分配造成的。通常,当您分配大量内存时,只有前 N 页实际分配并连接到物理内存。当您访问超过前 N 个页面时,会生成页面错误,并且会根据“按需”分配和连接更多页面。

至于问题的第二部分,我相信一些虚拟机实现实际上会跟踪归零页面并专门处理它们。尝试将 DataSrc 初始化为实际(例如随机)值并重复测试。

【讨论】:

+1 - 'Dirtying'(写入)所有页面事先确实应该让事情变得清晰,可以尝试calloc():***.com/q/1538420/1175253 @Sam:在我修复之前,该链接问题的最佳答案是不正确的;大多数主流操作系统上的calloc 从内核中获取零页面,因此它们仍然是延迟分配的,并且在读取或写入时会出现页面错误。

以上是关于性能:memset的主要内容,如果未能解决你的问题,请参考以下文章

卓越性能和高性能区别

卓越性能和高性能区别

性能测试到底该怎么做?

性能测试包括哪些方面

系统性能监控

性能专题:一文搞懂性能测试常见指标