对于涉及主内存带宽的测试,结果应以允许与系统的已知峰值DRAM带宽直接比较的单位表示。对于Core i7-8565U的典型配置,这是2个通道* 8字节/传输* 24亿个传输/秒= 38.4 GB / s(另请参阅以下第(6)项。)
- 对于涉及在存储器层次结构中任何地方进行数据传输的测试,结果应包括对“内存占用量”大小(访问的不同缓存行地址数量乘以缓存行大小)的清晰描述,以及重复次数的转移。您的代码在这里很容易阅读,并且大小对于主内存测试是完全合理的。
Whiskey Lake i7-8565U
我正在尝试学习如何手动编写基准测试(不使用任何基准测试框架),该示例以定期和非临时性写入WB存储器的内存复制例程为例,并希望进行某种形式的回顾。 。
声明:
void *avx_memcpy_forward_llss(void *restrict, const void *restrict, size_t);
void *avx_nt_memcpy_forward_llss(void *restrict, const void *restrict, size_t);
定义:
avx_memcpy_forward_llss:
shr rdx, 0x3
xor rcx, rcx
avx_memcpy_forward_loop_llss:
vmovdqa ymm0, [rsi + 8*rcx]
vmovdqa ymm1, [rsi + 8*rcx + 0x20]
vmovdqa [rdi + rcx*8], ymm0
vmovdqa [rdi + rcx*8 + 0x20], ymm1
add rcx, 0x08
cmp rdx, rcx
ja avx_memcpy_forward_loop_llss
ret
avx_nt_memcpy_forward_llss:
shr rdx, 0x3
xor rcx, rcx
avx_nt_memcpy_forward_loop_llss:
vmovdqa ymm0, [rsi + 8*rcx]
vmovdqa ymm1, [rsi + 8*rcx + 0x20]
vmovntdq [rdi + rcx*8], ymm0
vmovntdq [rdi + rcx*8 + 0x20], ymm1
add rcx, 0x08
cmp rdx, rcx
ja avx_nt_memcpy_forward_loop_llss
ret
基准代码:
#include <stdio.h>
#include <inttypes.h>
#include <unistd.h>
#include <fcntl.h>
#include <string.h>
#include <immintrin.h>
#include <x86intrin.h>
#include "memcopy.h"
#define BUF_SIZE 128 * 1024 * 1024
_Alignas(64) char src[BUF_SIZE];
_Alignas(64) char dest[BUF_SIZE];
static inline void warmup(unsigned wa_iterations, void *(*copy_fn)(void *, const void *, size_t));
static inline void cache_flush(char *buf, size_t size);
static inline void generate_data(char *buf, size_t size);
uint64_t run_benchmark(unsigned wa_iteration, void *(*copy_fn)(void *, const void *, size_t)){
generate_data(src, sizeof src);
warmup(4, copy_fn);
cache_flush(src, sizeof src);
cache_flush(dest, sizeof dest);
__asm__ __volatile__("mov $0, %%rax\n cpuid":::"rax", "rbx", "rcx", "rdx", "memory");
uint64_t cycles_start = __rdpmc((1 << 30) + 1);
copy_fn(dest, src, sizeof src);
__asm__ __volatile__("lfence" ::: "memory");
uint64_t cycles_end = __rdpmc((1 << 30) + 1);
return cycles_end - cycles_start;
}
int main(void){
uint64_t single_shot_result = run_benchmark(1024, avx_memcpy_forward_llss);
printf("Core clock cycles = %" PRIu64 "\n", single_shot_result);
}
static inline void warmup(unsigned wa_iterations, void *(*copy_fn)(void *, const void *, size_t)){
while(wa_iterations --> 0){
copy_fn(dest, src, sizeof src);
copy_fn(dest, src, sizeof src);
copy_fn(dest, src, sizeof src);
copy_fn(dest, src, sizeof src);
copy_fn(dest, src, sizeof src);
copy_fn(dest, src, sizeof src);
copy_fn(dest, src, sizeof src);
copy_fn(dest, src, sizeof src);
}
}
static inline void generate_data(char *buf, size_t sz){
int fd = open("/dev/urandom", O_RDONLY);
read(fd, buf, sz);
}
static inline void cache_flush(char *buf, size_t sz){
for(size_t i = 0; i < sz; i+=_SC_LEVEL1_DCACHE_LINESIZE){
_mm_clflush(buf + i);
}
}
结果:
avx_memcpy_forward_llss
中位数:44479368核心周期
UPD:时间
real 0m0,217s
user 0m0,093s
sys 0m0,124s
avx_nt_memcpy_forward_llss
中位数:24053086核心周期
UPD:时间
real 0m0,184s
user 0m0,056s
sys 0m0,128s
UPD:使用 所以我在内存复制例程实现之间的核心周期几乎相差2倍。我将其解释为在将常规存储到WB存储器的情况下,我们有RFO请求在IOM / 3.6.12(强调我的)中指定的总线带宽上竞争:taskset -c 1 ./bin
]运行基准测试时得到的结果>
尽管完整的64字节总线写入的数据带宽是由于
非临时存储是总线写入WB存储器的两倍,传输8字节的数据块浪费了总线请求带宽并交付了大大降低了数据带宽。问题1:
单发情况下如何进行基准分析?由于性能启动开销和预热迭代开销,性能计数器似乎没有用。
问题2:这样的基准是否正确?我一开始就考虑cpuid
,以便开始使用干净的CPU资源进行测量,以避免由于先前的飞行指令而导致停顿。我添加了内存碎片作为编译屏障,并添加了lfence
以免rdpmc
被执行。
Whiskey Lake i7-8565U我正在尝试学习如何手动编写基准测试(不使用任何基准测试框架),该示例具有常规和...的内存复制例程示例。] >> [[< [
对于涉及主内存带宽的测试,结果应以允许与系统的已知峰值DRAM带宽直接比较的单位表示。对于Core i7-8565U的典型配置,这是2个通道* 8字节/传输* 24亿个传输/秒= 38.4 GB / s(另请参阅以下第(6)项。)
使用RDPMC指令的任何测试都必须绑定到单个逻辑处理器。结果的显示方式应向读者确认已采用这种绑定。在Linux中强制执行此类绑定的常用方法包括使用“任务集”或“ numactl --physcpubind = [n]”命令,或使用单个允许的逻辑处理器对“ sched_setaffinity()”进行内联调用,或设置环境变量导致运行时库(例如OpenMP)将线程绑定到单个逻辑处理器。
假设第二种情况执行一个读取和一个流存储(每个128MiB),则相应的DRAM流量为25.7 GB / s + 25.7 GB / s = 51.3 GB / s =峰值的134%。
对于涉及主内存带宽的测试,结果应以允许与系统的已知峰值DRAM带宽直接比较的单位表示。对于Core i7-8565U的典型配置,这是2个通道* 8字节/传输* 24亿个传输/秒= 38.4 GB / s(另请参阅以下第(6)项。)