在谈论ifs的表现时,我们通常会谈论错误预测如何阻止管道.我看到的推荐解决方案是:
我找不到的是我们是否能尽早计算出病情,以便在可能的情况下提供帮助.所以,而不是:
... work
if (a > b) {
... more work
}
Run Code Online (Sandbox Code Playgroud)
做这样的事情:
bool aGreaterThanB = a > b;
... work
if (aGreaterThanB) {
... more work
}
Run Code Online (Sandbox Code Playgroud)
这样的事情可能会完全避免这个条件的停顿(取决于管道的长度和我们可以放在bool和if之间的工作量)?这并不一定是因为我写的,但有什么办法,以评估条件语句早,所以CPU不必尝试和预测的分支?
此外,如果这有帮助,编译器可能会做什么呢?
language-agnostic performance cpu-architecture compiler-optimization branch-prediction
所以calloc()通过向操作系统询问一些虚拟内存来工作.操作系统正在使用MMU进行操作,并巧妙地响应虚拟内存地址,该地址实际上映射到一个写满了零的只读页面.当程序尝试写入该页面中的任何位置时,会发生页面错误(因为您无法写入只读页面),会创建该页面的副本,并且您的程序的虚拟内存将映射到这些全新的副本零.
既然Meltdown是一个东西,操作系统已被修补,因此不再可能跨内核用户边界推测性地执行.这意味着每当用户代码调用内核代码时,它就会有效地导致管道停顿.通常,当管道在循环中停顿时,它对性能造成破坏性,因为CPU最终会浪费时间等待数据,无论是来自缓存还是主存储器.
calloc(),并且重新映射到新的CoW页面时,是否正在执行内核代码?calloc()分配4GiB的内存,然后在紧密的循环中用一些任意值(比如说,0xFF而不是0x00)初始化它,我的(英特尔)CPU每次写入新页面时都会触及推测边界吗?performance memory-management cpu-architecture calloc page-fault
我是一名学生,最近在研究超线程。我对这个功能有点困惑——L1 数据缓存上下文模式。
在架构优化手册中,描述了L1缓存可以在两种模式下运行:
一级缓存可以根据上下文 ID 位以两种模式运行:
共享模式:L1 数据缓存由两个逻辑处理器完全共享。
自适应模式:在自适应模式下,使用页目录的内存访问在共享 L1 数据缓存的逻辑处理器之间被完全映射。
但是,我很好奇缓存如何根据描述在自适应模式下进行分区。
我正在阅读一些论文,他们要么可以互换使用存储缓冲区和存储队列,要么与不同的结构有关,而我无法跟进.这就是我认为的商店队列:
我不确定存储缓冲区是什么,但我认为只是一些缓冲区空间来保持退役存储指令的数据等待写入内存(同样,L1).
现在,这就是为什么我感到困惑.在此论文中,指出"我们提出可扩展存储缓冲器[SSB],这使私人/推测值直接进入L1高速缓存,从而避免不可缩放缔搜索常规存储缓冲器的".我认为他们所讨论的不可扩展的关联式可搜索传统结构就是我所知道的商店队列,因为他们也说
SSB通过将处理器可见/推测值直接转发到L1高速缓存的加载,消除了传统存储缓冲区的不可扩展的关联搜索.
正如我上面提到的,据我所知,数据转发到加载是通过存储队列完成的.在第一页的脚注中,也有人说
我们使用"存储队列"来指代在退役之前保存商店值的存储和"存储缓冲区"以在存储到存储器之前引用包含已退休存储值的存储.
这符合我上面解释的内容,但它与第一个引用中的"存储缓冲区"冲突.脚注对应于论文中的参考文献之一.他们说,在那篇参考文献中
存储缓冲区是存在于许多当前处理器中以实现以下一个或多个的机制:存储访问顺序,延迟隐藏和数据转发.
我再次认为实现这些机制的机制称为存储队列.他们后来在同一篇论文中说
通常使用非阻塞高速缓存和缓冲结构,例如写缓冲区,存储缓冲区,存储队列和加载队列.
因此,他们分别提到存储缓冲区和存储队列,但稍后不再提及存储队列.他们说
存储缓冲区维护存储的顺序,并且只有在完成所有先前的指令之后才允许存储
他们的商店缓冲模型与Mike Johnson的模型相同.在约翰逊的书(超标量微处理器设计)中,商店首先以获取顺序进入商店预订站.从那里,它们被发送到地址单元,并从地址单元被发送到"存储缓冲区"及其相应的数据.通过此存储缓冲区处理加载转发.我再一次认为这个结构被称为商店队列.在参考文献#2中,作者也提到了这一点
Alpha 21264微处理器有一个32项的推测商店缓冲区,商店一直存在,直到它退役."
我看了一篇关于Alpha 21264的论文,其中指出了这一点
商店首先将数据通过数据总线传输到推测性存储缓冲区.商店数据保留在推测商店缓冲区中,直到商店退休.退出后,数据将在空闲缓存周期内写入数据缓存.
也,
内部存储器系统维护一个32项加载队列(LDQ)和一个32项存储队列(STQ),用于管理它们在飞行中的引用.[...] Stores在退出并转储到数据缓存后以获取顺序退出STQ.[...] STQ CAM逻辑控制推测数据缓冲区.当较旧的商店之后发生较年轻的负载时,它可以绕过推测商店数据加载.
因此,听起来像在Alpha 21264中有一个存储队列,它以获取顺序保存有关存储指令的一些信息,但它不保留存储指令的数据.存储指令的数据保存在存储缓冲区中.
所以,在所有这些之后,我不确定存储缓冲区是什么.它只是存储队列的辅助结构,还是存储等待写入L1的数据的完全不同的结构.或者是别的什么?当我们说"存储缓冲区"时,我觉得有些作者的意思是"存储队列".有任何想法吗?
Write-Combine缓冲区是如何物理连接的?我已经看到了说明许多变体的方框图:
它是依赖于微架构的吗?
我已经阅读了很多关于内存排序的文章,并且所有这些文章都只说CPU重新加载和存储.
CPU(我对x86 CPU特别感兴趣)是否仅重新排序加载和存储,并且不重新排序它具有的其余指令?
考虑以下循环:
loop:
movl $0x1,(%rax)
add $0x40,%rax
cmp %rdx,%rax
jne loop
Run Code Online (Sandbox Code Playgroud)
whererax被初始化为大于 L3 缓存大小的缓冲区的地址。每次迭代都会对下一个缓存行执行存储操作。我希望从 L1D 发送到 L2 的 RFO 请求数量或多或少等于访问的缓存线数量。问题是,即使程序在用户模式下运行,这似乎也只是当我计算内核模式事件时的情况,除非我在下面讨论的一种情况。缓冲区的分配方式似乎无关紧要(.bss、.data 或来自堆)。
我的实验结果如下表所示。所有实验都是在禁用超线程和启用所有硬件预取器的处理器上进行的。
我测试了以下三种情况:
NoInit. 在这种情况下只有一个循环。LoadInit. 在这种情况下有两个循环。StoreInit. 在这种情况下有两个循环。下表显示了英特尔 CFL 处理器上的结果。这些实验是在 Linux 内核版本 4.4.0 上进行的。
下表显示了英特尔 HSW 处理器上的结果。请注意,HSW 未记录事件L2_RQSTS.PF_HIT、L2_RQSTS.PF_MISS和OFFCORE_REQUESTS.ALL_REQUESTS。这些实验是在 Linux 内核版本 4.15 上进行的。
每个表的第一列包含性能监控事件的名称,其计数显示在其他列中。在列标签中,字母U和 分别K代表用户模式和内核模式事件。对于有两个循环的情况,数字1和2分别用于指代初始化循环和主循环。例如,LoadInit-1K代表LoadInit案例初始化循环的内核模式计数。
表中显示的值按高速缓存行的数量标准化。它们也按以下颜色编码。绿色越深,该值相对于同一表中的所有其他单元格就越大。但是,CFL 表的最后三行和 HSW 表的最后两行未进行颜色编码,因为这些行中的某些值太大。这些行被涂成深灰色,以表明它们不像其他行那样进行颜色编码。
我期望用户模式L2_RQSTS.ALL_RFO事件的数量等于访问的缓存行的数量(即标准化值为 1)。该事件在手册中描述如下:
计算对 L2 缓存的 …
我正在使用glibc 2.24版本。它具有通过事务同步扩展(例如_xbegin()和_xend())用于pthread_mutex_lock实现的锁省略路径。我认为硬件应该支持锁定删除,因为hle CPU标志用于硬件锁定删除。我正在使用的处理器是采用Skylake架构的Intel®Xeon®Gold 6130。
首先,我想禁用Lock Elision,但是当我运行使用pthread_mutex_lock且具有perf stat -T来监视事务周期的程序时,我得到0。我认为这意味着pthread_mutex_lock根本不使用事务路径。谷歌搜索之后,我发现可能有必要先使用export GLIBC_TUNABLES = glibc.elision.enable = 1来启用锁定清除,但是在此步骤之后,我仍然看不到使用perf进行任何事务。
另一方面,当我包含_xbegin()时;和_xend(); 直接在此过程中,我获得了一些具有perf stat -T的交易周期,这应该意味着我希望能找到与perf匹配的计数器。
因此,有关如何启用锁定清除的任何建议都将有所帮助。还是我检查不正确?
TSX的更新我在主要功能中使用了这两条指令,就像这样:
_xbegin();
_xend();
Run Code Online (Sandbox Code Playgroud)
我不确定它需要哪个库,我已经包含了数十个。对于编译,我使用以下标志:-O3 -march = native -lpthread与该示例相关。
对于锁,我有互斥锁:
pthread_mutex_t * mutex;
mutex = (pthread_mutex_t *) malloc(10 * sizeof(pthread_mutex_t));
for(int k=0; k<10; k++){
pthread_mutex_init(&mutex[k], NULL);
}
Run Code Online (Sandbox Code Playgroud)
也许为了省略,我应该以不同的方式初始化它?
我试图分析英特尔Haswell的CPU(英特尔®酷睿™i7-4900MQ)与自上而下的微架构分析方法(TMAM),在各章B.1和B.4所述,在执行英特尔®64和IA-32架构优化参考手册.(如果需要,我将B.4中描述的Sandy Bridge公式调整为Haswell Microarchitecture.)
因此,我使用Perf执行性能计数器事件测量.有些结果我不明白:
CPU_CLK_UNHALTED.THREAD_P < CYCLE_ACTIVITY.CYCLES_LDM_PENDING
这仅适用于少数测量,但仍然很奇怪.PMU计数是否会停止CYCLE_ACTIVITY.CYCLES_LDM_PENDING?
CYCLE_ACTIVITY.CYCLES_L2_PENDING> CYCLE_ACTIVITY.CYCLES_L1D_PENDING
和CYCLE_ACTIVITY.STALLS_L2_PENDING>CYCLE_ACTIVITY.STALLS_L1D_PENDING这适用于所有测量.当L1D高速缓存未命中时,负载会转移到L2高速缓存,对吧?因此早先错过L2的负载也错过了L1.这里没有计算L1指令高速缓存,但是它的大小*_L2_PENDING是100倍甚至1000倍*_L1D_PENDING,可能不是这样.是否分别以某种方式测量了档位/周期?但是有这个公式:
%L2_Bound =
(CYCLE_ACTIVITY.STALLS_L1D_PENDING - CYCLE_ACTIVITY.STALLS_L2_PENDING) / CLOCKS
因此CYCLE_ACTIVITY.STALLS_L2_PENDING< CYCLE_ACTIVITY.STALLS_L1D_PENDING假定(公式的结果必须为正).(这个公式的另一个原因是它可能应该CYCLES代替STALLS.但是这不能解决上面描述的问题.)那么如何解释呢?
编辑:我的操作系统:Ubuntu 14.04.3 LTS,内核:3.13.0-65-通用x86_64,性能版本:3.13.11-ckt26
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)