我看到
vmovntdqa
非临时负载的内存 (WC) 读取性能不佳
在 Intel Xeon E-2224 系统上的指令,但在 AMD 上表现出色
EPYC 3151 系统。为什么会有如此巨大的差异,我能做些什么吗?
关于它?似乎指令根本没有按预期工作
英特尔系统。
我在连接到 PCI Express 的 FPGA 板上有 DDR 内存。我在用
mmap()
从用户空间访问所述 DDR 内存映射到的 PCI BAR。
BAR 被标记为可预取,正如预期的那样,Linux 提供了_wc
sysfs 下相应的资源文件。
这是我的基准测试结果:
System: Dell R240
CPU: Intel Xeon E-2224
Read speed (memcpy()): 13.7 MB/s
Read speed (streaming/NT load): 9.6 MB/s
System: Supermicro M11SDV-4C-LN4F
CPU: AMD EPYC 3151
Read speed (memcpy()): 6.8 MB/s
Read speed (streaming/NT load): 273.6 MB/s
Dell R250 (Intel Xeon E-2314) 和 SuperMicro X11SCA-F(英特尔至强 E-2224G)。
基准程序(C++、Linux)是:
/* iobench.cpp */
#include <fcntl.h>
#include <sys/mman.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <unistd.h>
#include <chrono>
#include <cassert>
#include <cstring>
#include <iostream>
/* streaming-load-memcpy.cpp (adapted from Mesa, see below) */
void util_streaming_load_memcpy(void* __restrict__ dst,
void* __restrict__ src,
size_t len);
int main(int argc, char** argv) {
const size_t sz = 4 * 1024 * 1024; /* 4 MB */
assert(argc == 2);
int sysfs_fd = open(argv[1], O_RDWR | O_CLOEXEC);
if (sysfs_fd == -1) assert_perror(errno);
void* src = mmap(nullptr, sz, PROT_READ | PROT_WRITE, MAP_SHARED, sysfs_fd, 0);
if (src == (void*)-1) assert_perror(errno);
if (close(sysfs_fd) == -1) assert_perror(errno);
char* dst = static_cast<char*>(aligned_alloc(4096, sz));
assert(dst != nullptr);
std::chrono::steady_clock::time_point begin;
std::chrono::steady_clock::time_point end;
begin = std::chrono::steady_clock::now();
util_streaming_load_memcpy(dst, src, sz);
// Or: memcpy(dst, src, sz); */
end = std::chrono::steady_clock::now();
float duration_sec = std::chrono::duration<float>(end - begin).count();
float speed = sz / duration_sec / 1024.0 / 1024.0;
std::cout << speed << " MB/s\n";
if (munmap(src, sz) == -1) assert_perror(errno);
}
util_streaming_load_memcpy()
改编自
Mesa 项目(需要做一些小的改动才能独立编译)。
g++ -mavx -O2 iobench.cpp streaming-load-memcpy.cpp -o iobench
./iobench /sys/bus/pci/devices/0000\:13\:00.0/resource2_wc
util_streaming_load_memcpy()
的流加载循环编译为:
...
1540: c4 e2 79 2a 1e vmovntdqa (%rsi),%xmm3
1545: c4 e2 79 2a 56 10 vmovntdqa 0x10(%rsi),%xmm2
154b: 48 83 c6 40 add $0x40,%rsi
154f: 48 83 c0 40 add $0x40,%rax
1553: c4 e2 79 2a 4e e0 vmovntdqa -0x20(%rsi),%xmm1
1559: c4 e2 79 2a 46 f0 vmovntdqa -0x10(%rsi),%xmm0
155f: c5 f9 7f 58 c0 vmovdqa %xmm3,-0x40(%rax)
1564: c5 f9 7f 50 d0 vmovdqa %xmm2,-0x30(%rax)
1569: c5 f9 7f 48 e0 vmovdqa %xmm1,-0x20(%rax)
156e: c5 f9 7f 40 f0 vmovdqa %xmm0,-0x10(%rax)
1573: 48 39 d6 cmp %rdx,%rsi
1576: 75 c8 jne 1540 <_Z26util_streaming_load_memcpyPvS_m+0x40>
...
我尝试了以下方法,但似乎没有任何有意义的区别:
mitigations=off
)vmovntdqa
与 ymm*
寄存器)/sys/kernel/debug/x86/pat_memtype_list
;内存列为write-combining
由于存在大量 CPU 漏洞,包括“MMIO Stale Data 漏洞”,我忍不住想,这是不是一些缓解措施的结果 CPU 微代码还是设计?微码不能轻易降级 低于固件加载的版本。