目录
本文介绍通过mmap映射预留的内存,预留内存通过cmdline中增加mem参数来实现。
测试环境为飞腾D2000。
单板内存性能
以mbw,单进程测试性能带宽,此测试占用完整一个CPU核,但内存带宽并没有压满,仅仅测试单个CPU读写内存的带宽,以此为基准,对比查看应用在使用内存时的性能。
./mbw -q -n 10 5000
Method: MEMCPY | 3624.776 MiB/s |
Method: DUMB | 1052.553 MiB/s |
Method: MCBLOCK | 3614.032 MiB/s |
即,3.6MB/ms的拷贝速率。
cache与否
通过mmap接口映射一段内存,映射后的地址是带cache,还是不带cache呢?
走读代码 : kernel\drivers\char\mem.c
mmap_mem->phys_mem_access_prot->uncached_access
这里可以看到在内核以外的地址,例如通过cmdline中增加mem限制以上的内存,通过mmap映射后,默认是关闭cache的。
而对于巨页等内核可见的内存,通过mmap映射则cache为打开。比如DPDK使用的 mmap映射。
/* map the segment, and populate page tables,
* the kernel fills this segment with zeros. we don't care where
* this gets mapped - we already have contiguous memory areas
* ready for us to map into.
*/
virtaddr = mmap(NULL, hugepage_sz, PROT_READ | PROT_WRITE,
MAP_SHARED | MAP_POPULATE, fd, 0);
if (virtaddr == MAP_FAILED) {
EAL_LOG(DEBUG, "%s(): mmap failed: %s", __func__,
strerror(errno));
close(fd);
goto out;
}
map_locked参数与mlock
通常的内存使用如上图,进程创建时建立页表,以映射虚拟地址和物理地址的关系,例如此时进程1占用了此块内存。
而当进程1,不活跃时,其部分数据内存被认为不活跃,则会被swap磁盘中,并且对应页表会设置为缺页状态,也就是无实际对应的物理内存。
而进程2此时创建,建立页表,将自己的虚拟地址映射到了原先进程1的内存。
mlock 可以使进程1一直占用此内存,减少了swap耗时和缺页处理的耗时。
malloc申请内存
1) 我们用malloc申请两段内存,进行数据拷贝。
src=(unsigned int *)malloc(0x2000000);
dst=(unsigned int *)malloc(0x2000000);
拷贝耗时
malloc Read back: 0x0, copy (32)M 14173.000000 us
2.28MB/ms
2) mlock dst内存
if (mlock(dst, copy_size) == 0) {
printf("Memory locked malloc successfully\n");
} else {
perror("mlock malloc failed");
}
此时拷贝耗时
malloc Read back: 0x0, copy (32)M 7110.000000 us
4.56MB/ms
拷贝方法 | 速率 | 耗时(32M数据) |
mbw测试 | 3.6MB/ms | |
memcpy | 2.28MB/ms | 14MS |
memcpy+mlock dst地址 | 4.56MB/ms | 7MS |
mlock确认生效
通过查看进程status,可以看到进程的确lock了 32MB的内存。
cat /proc/2877/status
Name: mmapresv
VmSize: 198540 kB
VmLck: 32772 kB
mmap映射保留内存
if (mlock(mem, reserved_size) == 0) {
printf("Memory locked successfully\n");
} else {
perror("mlock failed");
}
mem地址是通过mmap映射保留内存的虚拟地址,虽然上述mlock执行成功,但实际查看进程的lock内存,值为0,也就是说实际上lock功能并没有对此时mmap应用场景生效。
cat /proc/2830/status
Name: mmapresv
VmSize: 198540 kB
VmLck: 0 kB
开关cache的耗时对比
mmap将保留的物理地址映射为带cache与不带cache。进行拷贝测试
关cache
Read back: 0x0, copy (32)M 364115.000000 us
开cache
Read back: 0x0, copy (32)M 17313.000000 us
开cache,并且clean cache
Read back: 0x0, copy (32)M 43951.000000 us
32M 数据拷贝如下. 关cache即 源和目的都关;开则源和目的都开。
关cache | 364MS | 90KB/ms |
开cache | 17MS | 1.88MB/ms |
开cache,且clean cache | 44MS | 745KB |
总结
期望通过mmap实现对保留物理内存的映射,并完成数据的拷贝。但是简单映射性能比较差。
1)关闭cache后,再让CPU读写,速率只有100MB/s左右。这种方式虽然和外设DMA打交道时简单,但是到CPU 读写数据时,速率太慢。
因素 | 影响 |
---|---|
失去 Cache 加速 | 每次读写直接访问内存(DRAM),延迟从 ~10ns(L1 Cache)升至 ~100ns |
总线带宽利用率低 | 无预取(Prefetch)和突发传输(Burst),总线效率下降 |
流水线停滞 | CPU 无法并行执行指令(需等待内存读写完成) |
ARM 架构限制 | AArch64 的普通加载/存储指令(如 LDR/STR )在 Non-Cacheable 模式下性能极低 |
理论带宽对比
模式 | 预期带宽(Cortex-A72 示例) | 实测典型值 |
---|---|---|
With Cache | 10-20 GB/s | 2000-5000 MB/s |
No Cache | 1-5% of peak | 50-200 MB/s |
# 使用 perf 统计缓存命中率
perf stat -e cache-misses,cache-references,L1-dcache-load-misses ./your_program
1,000,000 cache-misses # 缓存未命中率接近 100%
2)开cache映射
主要两个问题:
1) 不能被mlock 锁定成功。导致拷贝优化不上来。
2) cache在用户态的clean与invalid操作。