我记得假设在我的架构类中L1缓存命中是1个周期(即与寄存器访问时间相同),但在现代x86处理器上实际上是这样吗?
L1缓存命中多少个周期?它与寄存器访问相比如何?
performance x86 cpu-architecture micro-optimization cpu-cache
我正在玩这个答案的代码,稍微修改一下:
BITS 64
GLOBAL _start
SECTION .text
_start:
mov ecx, 1000000
.loop:
;T is a symbol defined with the CLI (-DT=...)
TIMES T imul eax, eax
lfence
TIMES T imul edx, edx
dec ecx
jnz .loop
mov eax, 60 ;sys_exit
xor edi, edi
syscall
Run Code Online (Sandbox Code Playgroud)
没有lfence我,我得到的结果与答案中的静态分析一致.
当我介绍一个单一 lfence我期望的CPU执行imul edx, edx的序列的第k个平行于迭代imul eax, eax的下一个(的序列K + 1个)迭代.
像这样的东西(调用一个的imul eax, eax序列和d的imul edx, edx一个): …
这三个片段的执行时间:
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) 我想用英特尔处理器实现以下操作的最大带宽.
for(int i=0; i<n; i++) z[i] = x[i] + y[i]; //n=2048
Run Code Online (Sandbox Code Playgroud)
其中x,y和z是浮点数组.我在Haswell,Ivy Bridge和Westmere系统上这样做.
我最初分配了这样的内存
char *a = (char*)_mm_malloc(sizeof(float)*n, 64);
char *b = (char*)_mm_malloc(sizeof(float)*n, 64);
char *c = (char*)_mm_malloc(sizeof(float)*n, 64);
float *x = (float*)a; float *y = (float*)b; float *z = (float*)c;
Run Code Online (Sandbox Code Playgroud)
当我这样做时,我获得了每个系统预期的峰值带宽的大约50%.
峰值计算为frequency * average bytes/clock_cycle.每个系统的平均字节/时钟周期为:
Core2: two 16 byte reads one 16 byte write per 2 clock cycles -> 24 bytes/clock cycle
SB/IB: two 32 byte reads and one 32 byte write per 2 clock cycles -> …Run Code Online (Sandbox Code Playgroud) 我正在试图分析一些x86二进制代码的"时序通道".我发布了一个问题来理解bsf/bsr操作码.
如此高级,这两个操作码可以被建模为"循环",它计算给定操作数的前导零和尾随零.该x86手册对这些操作码具有良好的形式化,如下所示:
IF SRC = 0
THEN
ZF ? 1;
DEST is undefined;
ELSE
ZF ? 0;
temp ? OperandSize – 1;
WHILE Bit(SRC, temp) = 0
DO
temp ? temp - 1;
OD;
DEST ? temp;
FI;
Run Code Online (Sandbox Code Playgroud)
但令我惊讶的是,bsf/bsr指令似乎有固定的cpu周期.根据我在这里找到的一些文档:https://gmplib.org/~tege/x86-timing.pdf,似乎它们总是需要8个CPU周期来完成.
所以这是我的问题:
我确认这些指令有固定的cpu周期.换句话说,无论给出什么操作数,它们总是花费相同的时间来处理,并且没有"时序通道".我在英特尔的官方文档中找不到相应的规格.
那么为什么有可能呢?显然这是一个"循环"或某种程度,至少是高级别的.背后的设计决策是什么?CPU流水线更容易?
Linux内核:4.10.0-20-generic(也在4.11.3上试过)
Ubuntu:17.04
我一直试图使用收集内存访问的统计信息perf stat.我能够收集内存存储的统计信息,但内存加载的计数返回0值.
以下是内存存储的详细信息: -
perf stat -e cpu/mem-stores/u ./libquantum_base.arnab 100
N = 100, 37 qubits required
Random seed: 33
Measured 3277 (0.200012), fractional approximation is 1/5.
Odd denominator, trying to expand by 2.
Possible period is 10.
100 = 4 * 25
Performance counter stats for './libquantum_base.arnab 100':
158,115,510 cpu/mem-stores/u
0.559922797 seconds time elapsed
Run Code Online (Sandbox Code Playgroud)
对于内存加载,我得到0计数,如下所示: -
perf stat -e cpu/mem-loads/u ./libquantum_base.arnab 100
N = 100, 37 qubits required
Random seed: …Run Code Online (Sandbox Code Playgroud) 在答案中,我已经声明未对齐访问的速度与对齐访问的速度几乎相同(在x86/x86_64上).我没有任何数字来支持这个陈述,所以我已经为它创建了一个基准.
你看到这个基准测试有什么缺陷吗?你可以改进它(我的意思是,增加GB /秒,所以它更好地反映了真相)?
#include <sys/time.h>
#include <stdio.h>
template <int N>
__attribute__((noinline))
void loop32(const char *v) {
for (int i=0; i<N; i+=160) {
__asm__ ("mov (%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x04(%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x08(%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x0c(%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x10(%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x14(%0), %%eax" : : "r"(v) :"eax");
__asm__ ("mov 0x18(%0), %%eax" : : "r"(v) :"eax"); …Run Code Online (Sandbox Code Playgroud) 我想知道是否有任何英特尔专家可以告诉我STD和STA在英特尔Skylake内核方面的区别。
在英特尔优化指南中,有一张图片描述了英特尔酷睿的“超标量端口”。
这是PDF。图片在第40页上。
。
这是第78页的另一张图片,该图片描述了“存储地址”和“存储数据”:
使用存储的数据地址准备存储转发和存储退出逻辑。
准备存储转发和存储退出逻辑以及要存储的数据。
考虑到Skylake可以在每个时钟周期执行一次#1 3x,但是在每个时钟周期只能执行一次#2,我很好奇这两者之间的区别。
在我看来,将存储转发到数据地址是“自然的”。但是我无法理解何时进行数据存储转发(又名:STD /端口4)。是否有任何组装/优化专家可以帮助我准确了解STD和STA之间的区别?
RESOURCE_STALLS.RSIntel Broadwell 硬件性能事件的描述如下:
此事件对由于保留站 (RS) 中缺少合格条目而导致的停顿周期进行计数。这可能是由于 RS 溢出,或由于 RS 阵列写入端口分配方案导致的 RS 解除分配(每个 RS 条目有两个写入端口而不是四个。因此,无法使用空条目,尽管 RS 并未真正满) . 这计算管道后端阻止来自前端的 uop 传递的周期。
这基本上说有两种情况会发生 RS 停顿事件:
在第一种情况下,“合格”是什么意思?这是否意味着并非所有条目都可以被各种 uop 占用?因为我的理解是,在现代微体系结构中,任何条目都可以被任何类型的 uop 使用。还有什么是 RS 阵列写入端口分配方案,即使并非所有条目都被占用,它如何导致 RS 停顿?这是否意味着 Haswell 中有四个写端口,而现在 Broadwell 中只有两个?即使手册没有明确说明,这两种情况是否适用于 Skylake 或 Haswell?
我在IvyBridge上.我发现jnz内循环和外循环中的性能行为不一致.
以下简单程序有一个固定大小为16的内部循环:
global _start
_start:
mov rcx, 100000000
.loop_outer:
mov rax, 16
.loop_inner:
dec rax
jnz .loop_inner
dec rcx
jnz .loop_outer
xor edi, edi
mov eax, 60
syscall
Run Code Online (Sandbox Code Playgroud)
perf工具显示外循环运行32c/iter.它表明jnz需要2个周期才能完成.
然后我在Agner的指令表中搜索,条件跳转有1-2"倒数吞吐量",注释"如果没有跳转就快".
在这一点上,我开始相信上述行为是以某种方式预期的.但为什么jnz在外循环中只需要1个循环来完成?
如果我.loop_inner完全删除部件,外部循环运行1c/iter.行为看起来不一致.
我在这里缺少什么?
perf上述程序的结果带命令:
perf stat -ecycles,branches,branch-misses,lsd.uops,uops_issued.any -r4 ./a.out
Run Code Online (Sandbox Code Playgroud)
是:
3,215,921,579 cycles ( +- 0.11% ) (79.83%)
1,701,361,270 branches ( +- 0.02% ) (80.05%)
19,212 branch-misses # 0.00% of all branches ( +- 17.72% ) (80.09%)
31,052 lsd.uops ( …Run Code Online (Sandbox Code Playgroud)
我无法计算出现代 PC 中 cpu 和 cpu 缓存之间的总线宽度。我在互联网上没有找到任何可靠的东西。我所拥有的只是Zen (AMD) 微架构的框图,其中表示 L1 和 L2 缓存每个周期可以传输 32B(256b)。我猜测总线宽度是 256 条线(假设单数据速率)。但是,存在双倍数据速率传输,例如内存控制器和 DDR 内存之间。
总结: