我正在尝试使用AVX -AVX2指令集来查看连续阵列上的流媒体性能.所以我有下面的例子,我做基本的内存读取和存储.
#include <iostream>
#include <string.h>
#include <immintrin.h>
#include <chrono>
const uint64_t BENCHMARK_SIZE = 5000;
typedef struct alignas(32) data_t {
double a[BENCHMARK_SIZE];
double c[BENCHMARK_SIZE];
alignas(32) double b[BENCHMARK_SIZE];
}
data;
int main() {
data myData;
memset(&myData, 0, sizeof(data_t));
auto start = std::chrono::high_resolution_clock::now();
for (auto i = 0; i < std::micro::den; i++) {
for (uint64_t i = 0; i < BENCHMARK_SIZE; i += 1) {
myData.b[i] = myData.a[i] + 1;
}
}
auto end = std::chrono::high_resolution_clock::now();
std::cout << (end - start).count() / …Run Code Online (Sandbox Code Playgroud) 请考虑以下简单代码:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <time.h>
#include <err.h>
int cpu_ms() {
return (int)(clock() * 1000 / CLOCKS_PER_SEC);
}
int main(int argc, char** argv) {
if (argc < 2) errx(EXIT_FAILURE, "provide the array size in KB on the command line");
size_t size = atol(argv[1]) * 1024;
unsigned char *p = malloc(size);
if (!p) errx(EXIT_FAILURE, "malloc of %zu bytes failed", size);
int fill = argv[2] ? argv[2][0] : 'x';
memset(p, fill, size);
int startms = cpu_ms();
printf("allocated %zu bytes …Run Code Online (Sandbox Code Playgroud) 英特尔PMU可用于测量每核读/写内存带宽使用情况吗?这里"存储器"意味着DRAM(即,不在任何高速缓存级别中命中).
较新的Linux内核具有sysfs可调参数/proc/sys/kernel/perf_event_paranoid,允许用户调整perf_events非root用户的可用功能,更高的数字更安全(提供相应更少的功能):
从内核文档中,我们对各种值有以下行为:
perf_event_paranoid:
控制非特权用户对性能事件系统的使用(无CAP_SYS_ADMIN).默认值为2.
-1:允许所有用户使用(几乎)所有事件在没有CAP_IPC_LOCK的perf_event_mlock_kb之后忽略mlock限制
> = 0:没有CAP_SYS_ADMIN的用户不允许使用ftrace函数跟踪点不允许没有CAP_SYS_ADMIN的用户访问原始跟踪点
> = 1:禁止没有CAP_SYS_ADMIN的用户访问CPU事件
> = 2:禁止没有CAP_SYS_ADMIN的用户进行内核分析
我1在我的perf_event_paranoid文件中应该"禁止CPU事件访问" - 但这究竟是什么意思?
普通读数意味着无法访问CPU性能计数器事件(例如Intel PMU事件),但似乎我可以访问那些就好了.例如:
$ perf stat sleep 1
Performance counter stats for 'sleep 1':
0.408734 task-clock (msec) # 0.000 CPUs utilized
1 context-switches # 0.002 M/sec
0 cpu-migrations # 0.000 K/sec
57 page-faults # 0.139 M/sec
1,050,362 cycles # 2.570 GHz
769,135 instructions # 0.73 insn per cycle
152,661 branches # 373.497 M/sec
6,942 …Run Code Online (Sandbox Code Playgroud) 我正在使用一个非常简单的循环开始调查我的Haswell端口0上的分支单元的功能:
BITS 64
GLOBAL _start
SECTION .text
_start:
mov ecx, 10000000
.loop:
dec ecx ;|
jz .end ;| 1 uOP (call it D)
jmp .loop ;| 1 uOP (call it J)
.end:
mov eax, 60
xor edi, edi
syscall
Run Code Online (Sandbox Code Playgroud)
使用perf我们看到循环以1c/iter运行
Performance counter stats for './main' (50 runs):
10,001,055 uops_executed_port_port_6 ( +- 0.00% )
9,999,973 uops_executed_port_port_0 ( +- 0.00% )
10,015,414 cycles:u ( +- 0.02% )
23 resource_stalls_rs ( +- 64.05% )
Run Code Online (Sandbox Code Playgroud)
我对这些结果的解释是:
但是,我们也可以看到RS永远不会满员.
它最多可以以2 uOPs/c的速率发送uOP,但理论上可以得到4 …
考虑以下循环:
.loop:
add rsi, OFFSET
mov eax, dword [rsi]
dec ebp
jg .loop
Run Code Online (Sandbox Code Playgroud)
where OFFSET是一些非负整数,并rsi包含指向该bss部分中定义的缓冲区的指针.此循环是代码中唯一的循环.也就是说,它在循环之前没有被初始化或触摸.据推测,在Linux上,缓冲区的所有4K虚拟页面都将按需映射到同一物理页面.因此,缓冲区大小的唯一限制是虚拟页面的数量.因此,我们可以轻松地尝试非常大的缓冲区.
该循环由4条指令组成.每个指令在Haswell的融合和未融合域中被解码为单个uop.连续的实例之间也存在循环携带的依赖关系add rsi, OFFSET.因此,在负载总是在L1D中命中的空闲条件下,循环应该每次迭代执行大约1个循环.对于小偏移(步幅),这要归功于基于IP的L1流预取器和L2流预取器.但是,两个预取程序只能在4K页面内预取,并且L1预取程序支持的最大步幅为2K.因此,对于小步幅,每4K页面应该有大约1 L1未命中.随着步幅的增加,L1未命中和TLB未命中的总数将增加,并且性能将相应地恶化.
下图显示了0到128之间步幅的各种有趣的性能计数器(每次迭代).请注意,所有实验的迭代次数都是常量.仅缓冲区大小更改以适应指定的步幅.此外,仅计算用户模式性能事件.
这里唯一奇怪的事情是退役的uops数量随着步伐的增加而增加.它从每次迭代3次(如预期)到步幅128的11次.为什么?
如下图所示,事情只会越来越大.在此图中,步幅范围为32到8192,增量为32字节.首先,退出指令的数量在步长4096字节处从4线性增加到5,之后它保持不变.负载微量的数量从1增加到3,并且每次迭代L1D负载命中的数量保持为1.对于所有步幅,只有L1D负载未命中数才对我有意义.
较大步幅的两个明显效果是:
taskset或重复了所有实验,nice并得到了相同的结果.为了进一步调查,下图显示了微代码辅助的uop数.每次迭代的微代码辅助微动的数量增加,直到它达到步幅4096的最大值,就像其他性能事件一样.对于所有步幅,每4K虚拟页面的微代码辅助微操作数为506."Extra UOPS"行显示退役的uop数减去3(每次迭代的预期uop数).
该图表示额外微量的数量略大于所有步幅的微码辅助微量的一半.我不知道这意味着什么,但它可能与页面走路有关,可能是观察到扰动的原因.
为什么即使每次迭代的静态指令数相同,每次迭代的退出指令和uop的数量也会增加?来自哪里的干扰?
下图显示了每次迭代的周期数与不同步幅的每次迭代的退役uop数.循环次数的增加比退役的次数增加得快得多.通过使用线性回归,我发现:
cycles = 0.1773 * stride + 0.8521
uops = 0.0672 * stride + 2.9277
Run Code Online (Sandbox Code Playgroud)
采用两种功能的衍生物:
d(cycles)/d(stride) = 0.1773
d(uops)/d(stride) = 0.0672
Run Code Online (Sandbox Code Playgroud)
这意味着循环次数增加0.1773,退役微量数量增加0.0672,步幅每增加1个字节.如果中断和页面错误确实是(唯一)扰动的原因,那么两个速率是否应该非常接近?
我使用Instrument的时间分析器来描述我的代码,并放大到反汇编,这里是结果的片段:
我不希望一条mov指令占23.3%的时间,而div指令几乎什么也没做.这让我相信这些结果是不可靠的.这是真的吗?或者我只是遇到了仪器错误?或者我是否需要使用一些选项来获得可靠的结果?
这个问题是否有任何参考?
当我运行时,perf list我看到一堆Hardware Cache Events,如下所示:
$ perf list | grep 'cache event'
L1-dcache-load-misses [Hardware cache event]
L1-dcache-loads [Hardware cache event]
L1-dcache-stores [Hardware cache event]
L1-icache-load-misses [Hardware cache event]
LLC-load-misses [Hardware cache event]
LLC-loads [Hardware cache event]
LLC-store-misses [Hardware cache event]
LLC-stores [Hardware cache event]
branch-load-misses [Hardware cache event]
branch-loads [Hardware cache event]
dTLB-load-misses [Hardware cache event]
dTLB-loads [Hardware cache event]
dTLB-store-misses [Hardware cache event]
dTLB-stores [Hardware cache event]
iTLB-load-misses [Hardware cache event]
iTLB-loads [Hardware cache event]
node-load-misses …Run Code Online (Sandbox Code Playgroud) 我试图理解 rdpmc 指令。因此,我有以下汇编代码:
segment .text
global _start
_start:
xor eax, eax
mov ebx, 10
.loop:
dec ebx
jnz .loop
mov ecx, 1<<30
; calling rdpmc with ecx = (1<<30) gives number of retired instructions
rdpmc
; but only if you do a bizarre incantation: (Why u do dis Intel?)
shl rdx, 32
or rax, rdx
mov rdi, rax ; return number of instructions retired.
mov eax, 60
syscall
Run Code Online (Sandbox Code Playgroud)
(实现是rdpmc_instructions()的翻译。)我认为这段代码应该在命中指令之前执行 2*ebx+3rdpmc条指令,所以我期望(在这种情况下)我应该得到 23 的返回状态。
如果我perf stat -e …
Mac OS 上有等效的性能统计吗?我想对 CLI 命令做同样的事情,但谷歌搜索没有产生任何结果。
intel-pmu ×10
x86 ×9
performance ×5
perf ×4
assembly ×3
profiling ×3
avx2 ×1
cpu-cache ×1
instruments ×1
linux ×1
linux-kernel ×1
macos ×1
xcode ×1