我使用英特尔®架构代码分析器(IACA)发现了一些意想不到的东西(对我而言).
以下指令使用[base+index]寻址
addps xmm1, xmmword ptr [rsi+rax*1]
Run Code Online (Sandbox Code Playgroud)
根据IACA没有微熔丝.但是,如果我用[base+offset]这样的
addps xmm1, xmmword ptr [rsi]
Run Code Online (Sandbox Code Playgroud)
IACA报告它确实融合了.
英特尔优化参考手册的第2-11节给出了以下"可以由所有解码器处理的微融合微操作"的示例
FADD DOUBLE PTR [RDI + RSI*8]
Run Code Online (Sandbox Code Playgroud)
和Agner Fog的优化装配手册也给出了使用[base+index]寻址的微操作融合的例子.例如,请参见第12.2节"Core2上的相同示例".那么正确的答案是什么?
当我遇到ADCX以前不知道的指令时,我正在浏览英特尔软件开发人员手册; 它的编码是66 0F 38 F6.它似乎几乎与ADC指令相同,所以为什么你会在以下时间使用ADCX:
ADC)是否有其他副作用或特殊情况ADCX证明有利于ADC?必须有一些很好的理由将其添加到指令库中.
这三个片段的执行时间:
pageboundary: dq (pageboundary + 8)
...
mov rdx, [rel pageboundary]
.loop:
mov rdx, [rdx - 8]
sub ecx, 1
jnz .loop
Run Code Online (Sandbox Code Playgroud)
还有这个:
pageboundary: dq (pageboundary - 8)
...
mov rdx, [rel pageboundary]
.loop:
mov rdx, [rdx + 8]
sub ecx, 1
jnz .loop
Run Code Online (Sandbox Code Playgroud)
还有这个:
pageboundary: dq (pageboundary - 4096)
...
mov rdx, [rel pageboundary]
.loop:
mov rdx, [rdx + 4096]
sub ecx, 1
jnz .loop
Run Code Online (Sandbox Code Playgroud)
对于第一个片段,在4770K上,每次迭代大约5个周期,对于第二个片段,每次迭代大约9个周期,然后是第三个片段的5个周期.它们都访问完全相同的地址,这是4K对齐的.在第二个片段中,只有地址计算跨越页面边界:rdx并且rdx + 8不属于同一页面,负载仍然是对齐的.如果偏移量很大,则会再次回到5个周期.
这种效果一般如何起作用?
通过ALU指令从加载路由结果,如下所示:
.loop:
mov rdx, …Run Code Online (Sandbox Code Playgroud) 我研究了Java内存模型并看到了重新排序问题.一个简单的例子:
boolean first = false;
boolean second = false;
void setValues() {
first = true;
second = true;
}
void checkValues() {
while(!second);
assert first;
}
Run Code Online (Sandbox Code Playgroud)
重新排序是非常不可预测和奇怪的.此外,它破坏了抽象.我认为处理器架构必须有充分的理由去做一些对程序员来说太不方便的事情. 这些原因是什么?
有很多关于如何处理重新排序的信息,但我找不到任何关于它为什么需要的信息.在任何地方,人们只会说"这是因为一些性能优势".例如,second之前存储的性能优势是first什么?
您能推荐一些关于此的文章,论文或书籍,或者自己解释一下吗?
java optimization multithreading cpu-architecture compiler-optimization
我正在使用一个非常简单的循环开始调查我的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 …
运行附加的示例程序时,该函数tan在上下文中的速度似乎是孤立时的两倍。这是我机器上的输出:
justtan(): ~16.062430 ns/iter
notan(): ~30.852820 ns/iter
withtan(): ~60.703100 ns/iter
empty(): ~0.355270 ns/iter
Run Code Online (Sandbox Code Playgroud)
鉴于它是和的组合,我预计withtan()约为 45ns 或更低。justtannotan
我正在使用 Intel i7-4980HQ CPU 运行 macOS 11.5.2。我的cc --version是Apple clang version 13.0.0 (clang-1300.0.29.3)。我已经检查过,以确保除了对 的调用之外,withtan和 的notan反汇编是相同的tan,并且 clang 使用 VEX 指令对循环进行自动矢量化。我还通过调试器检查了运行时调用的版本tan是否也利用 VEX 指令来避免 SSE-AVX2 转换损失。
我在Linux VM中编译并运行了该程序,并得到了类似的结果(在调试器中,tan也使用AVX/VEX)。此外,我通过cachegrind运行它,发现任何函数基本上都没有L1缓存未命中(0.00%),但是当通过cachegrind运行时,所有时间都正确地加起来。
这就是我运行可执行文件的方式:
cc -Wall -O3 -mavx2 -o main main.c && ./main
这是main.c:
justtan(): ~16.062430 ns/iter
notan(): ~30.852820 ns/iter
withtan(): ~60.703100 ns/iter …Run Code Online (Sandbox Code Playgroud) 通常有两种类型的SIMD指令:
A.使用对齐的内存地址的那些,如果地址未在操作数大小边界上对齐,则会引发一般保护(#GP)异常:
movaps xmm0, xmmword ptr [rax]
vmovaps ymm0, ymmword ptr [rax]
vmovaps zmm0, zmmword ptr [rax]
Run Code Online (Sandbox Code Playgroud)
B.以及使用未对齐内存地址的那些,不会引发此类异常:
movups xmm0, xmmword ptr [rax]
vmovups ymm0, ymmword ptr [rax]
vmovups zmm0, zmmword ptr [rax]
Run Code Online (Sandbox Code Playgroud)
但是我只是好奇,为什么我要用脚射击自己并使用第一组的对齐记忆指令呢?
我在某处读到x86指令中的有效地址(如LEA指令中)由"EU"计算.什么是欧盟?计算有效地址究竟涉及什么?
我只学习了MC68k指令集(UC博尔德首先教这个),我找不到一个好的x86网页,通过网络搜索.
我正在尝试mmap()为PCIe BAR 编写一个带有自定义函数的驱动程序,目标是使这个BAR可以在处理器缓存中缓存.我知道这不是实现最高带宽的最佳方式,并且写入顺序是不可预测的(在这种情况下也不是问题).
处理器是Sandy Bridge i7,PCIe器件是Altera Stratix IV dev.板.
首先,我尝试在CentOS 5(2.6.18)上进行.我改变了MTRR设置,以确保酒吧是不是不可缓存MTRR内,并使用io_remap_pfn_range()与_PAGE_PCD和_PAGE_PWT清除位.读取按预期工作:读取返回正确的值,第二次读取到同一地址不一定导致读取到PCIe(在FPGA中检查读取计数器).但是,写入导致系统冻结然后重新启动,而日志或屏幕上没有任何消息.
其次,我尝试在具有PAT支持的CentOS 6(2.6.32)上进行.结果是一样的:读取工作正常,写入会导致系统冻结并重新启动.有趣的是,非时间/写入组合完整高速缓存行写入(AVX/SSE)按预期工作,即它们总是进入FPGA并且FPGA观察完整的高速缓存行写入,读取之后返回正确的值.但是,简单的64位写入仍会导致系统冻结/重启.
我也尝试过ioremap_cache()然后iowrite32()在驱动程序代码中.结果是一样的.
我认为这是一个硬件问题,但如果有人可以分享有关正在发生的事情的任何想法,我将不胜感激.
编辑:我能够在CentOS 6上捕获MCE消息:机器检查异常:5 Bank 5:be2000000003110a.
我也尝试在2插槽Sandy Bridge(Romley)上使用相同的代码:读取和非临时写入行为是相同的,简单写入不会导致MCE /崩溃但对系统状态没有影响,即内存中的值不会改变.
此外,我在旧的2插槽Nehalem系统上尝试了相同的代码:简单的写入也会导致MCE,尽管代码不同.
首先,我在 IvyBridge 上进行了以下设置,我将在注释位置插入测量有效负载代码。前 8 个字节buf存储buf自身的地址,我用它来创建循环携带依赖:
section .bss
align 64
buf: resb 64
section .text
global _start
_start:
mov rcx, 1000000000
mov qword [buf], buf
mov rax, buf
loop:
; I will insert payload here
; as is described below
dec rcx
jne loop
xor rdi, rdi
mov rax, 60
syscall
Run Code Online (Sandbox Code Playgroud)
我插入到有效载荷位置:
mov qword [rax+8], 8
mov rax, [rax]
Run Code Online (Sandbox Code Playgroud)
perf显示循环为 5.4c/iter。有点理解,因为L1d延迟是4个周期。
我颠倒了这两条指令的顺序:
mov rax, [rax]
mov qword [rax+8], 8
Run Code Online (Sandbox Code Playgroud)
结果突然变成9c/iter。我不明白为什么。因为下一次迭代的第一条指令不依赖于当前迭代的第二条指令,所以这个设置应该和 case 1 没有区别。
我也用IACA工具对这两种情况进行静态分析,但是该工具不可靠,因为两种情况预测的结果都是5.71c/iter,与实验相矛盾。 …
x86 assembly micro-optimization microbenchmark micro-architecture