Linux中使用mmap映射物理内存进行用户空间的memcpy性能差

9
在我的计算机上安装了192GB RAM,但在开机时,Linux内核通过mem=4G memmap=188G$4G将位于硬件地址0x100000000以上的188GB RAM保留。数据采集内核模块使用DMA将数据积累到这个大区域中作为环形缓冲区。用户空间应用程序将该环形缓冲区mmap到用户空间,然后在准备就绪时从当前位置复制块以进行处理。
使用memcpy从mmap'ed区域复制这些16MB块的性能不如我所预期。似乎性能取决于开机时保留的内存大小(稍后会mmap到用户空间)。 http://www.wurmsdobler.org/files/resmem.zip 包含一个实现mmap文件操作的内核模块源代码。
module_param(resmem_hwaddr, ulong, S_IRUSR);
module_param(resmem_length, ulong, S_IRUSR);
//...
static int resmem_mmap(struct file *filp, struct vm_area_struct *vma) {
remap_pfn_range(vma, vma->vm_start,
    resmem_hwaddr >> PAGE_SHIFT,
    resmem_length, vma->vm_page_prot);
return 0;
}

还有一个测试应用程序,本质上执行以下操作(去除检查):

#define BLOCKSIZE ((size_t)16*1024*1024)
int resMemFd = ::open(RESMEM_DEV, O_RDWR | O_SYNC);
unsigned long resMemLength = 0;
::ioctl(resMemFd, RESMEM_IOC_LENGTH, &resMemLength);
void* resMemBase = ::mmap(0, resMemLength, PROT_READ | PROT_WRITE, MAP_SHARED, resMemFd, 4096);
char* source = ((char*)resMemBase) + RESMEM_HEADER_SIZE;    
char* destination = new char[BLOCKSIZE];
struct timeval start, end;
gettimeofday(&start, NULL);
memcpy(destination, source, BLOCKSIZE);
gettimeofday(&end, NULL);
float time = (end.tv_sec - start.tv_sec)*1000.0f + (end.tv_usec - start.tv_usec)/1000.0f;
    std::cout << "memcpy from mmap'ed to malloc'ed: " << time << "ms (" << BLOCKSIZE/1000.0f/time << "MB/s)" << std::endl;

我已经在SuperMicro 1026GT-TF-FM109上,使用Ubuntu 10.04.4和Linux 2.6.32进行了16MB数据块的memcpy测试,测试包括不同大小的保留内存(resmem_length):

|      |         1GB           |          4GB           |         16GB           |        64GB            |        128GB            |         188GB
|run 1 | 9.274ms (1809.06MB/s) | 11.503ms (1458.51MB/s) | 11.333ms (1480.39MB/s) |  9.326ms (1798.97MB/s) | 213.892ms (  78.43MB/s) | 206.476ms (  81.25MB/s)
|run 2 | 4.255ms (3942.94MB/s) |  4.249ms (3948.51MB/s) |  4.257ms (3941.09MB/s) |  4.298ms (3903.49MB/s) | 208.269ms (  80.55MB/s) | 200.627ms (  83.62MB/s)

我的观察结果如下:
  1. 从第一次到第二次运行,从mmap映射到malloc分配的memcpy似乎受益于内容可能已经被缓存。

  2. 使用memcpy时,当内存大于64GB时会出现显著的性能下降。

我想理解其中的原因。也许Linux内核开发人员组中有人认为:64GB对于任何人来说都足够了(这听起来熟悉吗?)
此致, 彼得
2个回答

2

根据SuperMicro的反馈,性能下降是由NUMA(非均匀内存访问)引起的。 SuperMicro 1026GT-TF-FM109使用X8DTG-DF主板,其中心是一颗Intel 5520 Tylersburg芯片组,连接着两个Intel Xeon E5620 CPU,每个CPU都有96GB的RAM。

如果我将我的应用程序锁定在CPU0上,我可以观察到不同的memcpy速度,这取决于保留了哪个内存区域并相应地执行了mmap。如果保留的内存区域是在远端CPU上,则mmap需要一些时间才能完成其工作,并且任何后续对“远程”区域进行的memcpy都会消耗更多的时间(数据块大小= 16MB):

resmem=64G$4G   (inside CPU0 realm):   3949MB/s  
resmem=64G$96G  (outside CPU0 realm):    82MB/s  
resmem=64G$128G (outside CPU0 realm):  3948MB/s
resmem=92G$4G   (inside CPU0 realm):   3966MB/s            
resmem=92G$100G (outside CPU0 realm):    57MB/s   

它几乎有意义。 只有第三种情况,64G$128,也就是最上面的64GB也能产生良好的结果。 这与理论有些矛盾。

问候, 彼得


如果每个NUMA域有96 GiB的RAM,则“64g$128G”测试将分别位于第一个NUMA域和第二个NUMA域的一半。最后一个测试(“92G$100G”)可能过于危险 - 固件通常会为各种事情保留一些RAM,并且顶部的RAM可能用于诸如SMM状态保存区之类的东西,而不是随意软件可以破坏的自由/可用RAM。 - Brendan

1

你的CPU可能没有足够的缓存来有效处理它。要么使用更低的内存,要么购买一个具有更大缓存的CPU。


你好Ignacio,也许你是对的。这台计算机配备了两个Intel Xeon E5620 2.4GHz四核心处理器,每个处理器都有12MB L3缓存和1066MHz内存速度。 - PeterW
我的简单看法是,对于第一次读取操作,RAM 中的内容将被缓存,而第二个请求将直接从缓存中提供,只要数据量适合缓存。我认为传输的数据量会影响 memcpy,在我的情况下小于 12MB,但不会影响内存的总大小或数据在 RAM 中的位置。 - PeterW
进一步的测试表明,相同的性能下降也适用于较小的数据块,例如1MB。它似乎只取决于启动时保留的内存量,即不超过64GB。 - PeterW

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接