相关疑难解决方法(0)

函数指针强制指令管道清除吗?

现代CPU具有广泛的流水线操作,也就是说,它们在实际执行指令之前很久就会加载必要的指令和数据.

有时,加载到管道中的数据会失效,必须清除管道并重新加载新数据.重新填充管道所需的时间可能相当长,并导致性能下降.

如果我在C中调用一个函数指针,那么管道是否足够智能以实现管道中的指针是一个函数指针,并且它应该跟随该指针用于下一个指令?或者是否有一个函数指针导致管道清除并降低性能?

我在C中工作,但我想这在C++中更为重要,因为许多函数调用都是通过v-tables进行的.


编辑

@JensGustedt写道:

要成为函数调用的真正性能,您调用的函数必须非常简短.如果您通过测量代码来观察这一点,那么您最终应该重新审视您的设计以允许内联调用

不幸的是,这可能是我陷入的陷阱.

出于性能原因,我编写了小而快的目标函数.

但是它被函数指针引用,因此它可以很容易地被其他函数替换(只需使指针引用一个不同的函数!).因为我通过函数指针引用它,所以我认为它不能内联.

所以,我有一个非常简短,没有内联的功能.

c performance x86 pipeline function-pointers

23
推荐指数
2
解决办法
3249
查看次数

生产者 - 消费者在超兄弟姐妹与非兄弟姐妹之间共享内存位置的延迟和吞吐量成本是多少?

单个进程中的两个不同线程可以通过读取和/或写入来共享公共存储器位置.

通常,这种(有意)共享是使用lockx86上的前缀使用原子操作实现的,该前缀对于lock前缀本身(即,无竞争成本)具有相当广为人知的成本,并且当实际共享高速缓存行时还具有额外的一致性成本(真或共享).

在这里,我对生产 - 消费者成本感兴趣,其中单个线程P写入内存位置,另一个线程`C从内存位置读取,都使用普通读取和写入.

在同一个套接字上的不同内核上执行此类操作的延迟和吞吐量是多少,并且在最近的x86内核上在同一物理内核上执行兄弟超线程时进行比较.

在标题中,我使用术语"超级兄弟"来指代在同一核心的两个逻辑线程上运行的两个线程,以及核心间兄弟,以指代在不同物理核心上运行的两个线程的更常见情况.

concurrency performance x86 hyperthreading

17
推荐指数
2
解决办法
846
查看次数

超标量和OoO执行的一般区别是什么?

我一直在阅读关于superscalr和OoO的一些材料,我很困惑.
我认为他们的架构图看起来非常相似.

cpu cpu-architecture

12
推荐指数
1
解决办法
5769
查看次数

Meltdown缓解与`calloc()的CoW"延迟分配"相结合,是否意味着calloc()的性能损失 - 分配内存?

所以calloc()通过向操作系统询问一些虚拟内存来工作.操作系统正在使用MMU进行操作,并巧妙地响应虚拟内存地址,该地址实际上映射到一个写满了零的只读页面.当程序尝试写入该页面中的任何位置时,会发生页面错误(因为您无法写入只读页面),会创建该页面的副本,并且您的程序的虚拟内存将映射到这些全新的副本零.

既然Meltdown是一个东西,操作系统已被修补,因此不再可能跨内核用户边界推测性地执行.这意味着每当用户代码调用内核代码时,它就会有效地导致管道停顿.通常,当管道在循环中停顿时,它对性能造成破坏性,因为CPU最终会浪费时间等待数据,无论是来自缓存还是主存储器.

鉴于此,我想知道的是:

  • 当程序写入分配的从未访问过的页面calloc(),并且重新映射到新的CoW页面时,是否正在执行内核代码?
  • 是否在OS级别或MMU级别实现了页面错误写时复制功能?
  • 如果我调用calloc()分配4GiB的内存,然后在紧密的循环中用一些任意值(比如说,0xFF而不是0x00)初始化它,我的(英特尔)CPU每次写入新页面时都会触及推测边界吗?
  • 最后,如果它是真实的,那么这种影响对现实世界的表现有何影响?

performance memory-management cpu-architecture calloc page-fault

7
推荐指数
1
解决办法
286
查看次数

嵌套分支和推测执行会发生什么?

好吧,所以我知道,如果特定的条件分支有一个需要时间来计算的条件(例如内存访问),CPU 会假定一个条件结果并沿着该路径推测执行。但是,如果沿着该路径弹出另一个缓慢的条件分支(当然,假设第一个条件尚未解决并且 CPU 无法提交更改),会发生什么情况?难道CPU只是在猜测里面猜测吗?如果最后一个条件预测错误但第一个条件没有预测会发生什么?难道只是一路回滚吗?

我正在谈论这样的事情:

if (value_in_memory == y){
   // computations
   if (another_val_memory == x){
      //computations
   }
}
Run Code Online (Sandbox Code Playgroud)

cpu-architecture nested-if speculative-execution branch-prediction

6
推荐指数
1
解决办法
1137
查看次数

当Skylake CPU错误预测分支时会发生什么?

我试图详细了解当分支预测错误时,skylake CPU管道的各个阶段中的指令会发生什么,以及从正确的分支目标开始执行指令的速度如何。

因此,让我们在这里将两个代码路径标记为红色(一个预测但未实际采用)和绿色(一个已预测但未预期)。所以问题是:1.在红色指令开始被丢弃之前,分支必须经过管道多远(以及在管道的哪个阶段被丢弃)?2.绿色指令(在分支到达的流水线阶段方面)多久可以开始执行?

我看过Agner Fogg的文档和许多讲义,但这些观点并不清楚。

x86 intel cpu-architecture speculative-execution branch-prediction

5
推荐指数
1
解决办法
369
查看次数

为什么不预测两个分支?

CPU使用分支预测来加速代码,但仅限于实际采用第一个分支.

为什么不简单地采取两个分支?也就是说,假设两个分支都将被命中,缓存两侧,并在必要时采取适当的分支.缓存不需要无效.虽然这需要编译器预先加载两个分支(更多的内存,适当的布局等),但我认为适当的优化可以简化两者,以便可以从单个预测器获得接近最优的结果.也就是说,需要更多的内存来加载两个分支(对于N个分支是指数的),大多数时候应该能够在完成执行分支之前足够快地用新代码"重新缓存"失败的分支. .

if(x)Bl else Br;

不假设采用Bl,而是假设采用Bl和Br(某种类型的并行处理或特殊交织),并且在实际确定分支之后,一个分支随后无效,然后可以释放缓存以供使用(可能是一些需要特殊技术的类型才能正确填写和使用它.

实际上,不需要预测电路,并且所有用于此的设计可以用于处理两个分支.

任何想法,如果这是可行的?

cpu cpu-architecture prefetch speculative-execution branch-prediction

4
推荐指数
1
解决办法
584
查看次数

推测执行的 CPU 分支是否可以包含访问 RAM 的操作码?

据我了解,当 CPU 推测性地执行一段代码时,它会在切换到推测性分支之前“备份”寄存器状态,以便如果预测结果错误(使分支无用)——寄存器状态将是安全恢复,而不会破坏“状态”。

所以,我的问题是:推测执行的 CPU 分支是否可以包含访问 RAM 的操作码?

我的意思是,访问 RAM 不是“原子”操作——如果数据当前不在 CPU 缓存中,那么从内存中读取一个简单的操作码可能会导致实际的 RAM 访问,这可能会变成一个非常耗时的操作,从 CPU 的角度来看。

如果在推测分支中确实允许这种访问,它是否仅用于读取操作?因为,我只能假设,如果一个分支被丢弃并执行“回滚”,根据它的大小恢复写操作可能会变得非常缓慢和棘手。而且,可以肯定的是,至少在某种程度上支持读/写操作,因为寄存器本身,在某些 CPU 上,据我所知,物理上位于 CPU 缓存上。

所以,也许更精确的表述是:推测执行的一段代码有什么限制?

cpu cpu-architecture speculative-execution

4
推荐指数
1
解决办法
518
查看次数

x86 TSO 内存模型上的“存储缓冲区”试金石测试名称的原因

我一直在研究内存模型并看到了这个(引用自https://research.swtch.com/hwmm):

Litmus Test: Write Queue (also called Store Buffer)
Can this program see r1 = 0, r2 = 0?
// Thread 1           // Thread 2
x = 1                 y = 1
r1 = y                r2 = x
On sequentially consistent hardware: no.
On x86 (or other TSO): yes!

Run Code Online (Sandbox Code Playgroud)
  • 事实1:这是很多文章中提到的存储缓冲区试金石。他们都说,由于存储缓冲区的存在,TSO 上可能会发生 r1 和 r2 为零的情况。他们似乎假设所有存储和加载都按顺序执行,但结果是 r1 和 r2 都为零。后来得出的结论是“存储/加载重新排序可能会发生”,作为“存储缓冲区存在的后果”。

  • 事实 2:但是我们知道 OoO 执行也可以重新排序两个线程中的存储和加载。从这个意义上说,无论存储缓冲区如何,只要所有四个指令都在没有看到彼此对 x 或 y 无效的情况下退出,这种重新排序就可能导致 r1 和 r2 都为零。在我看来,“存储/加载重新排序可能会发生”,只是因为“它们是无序执行的”。(我对此可能是非常错误的,因为这是我所知道的最好的猜测和 OoO 执行。)

我想知道这两个事实是如何融合的(假设我碰巧对这两个事实都是正确的):存储缓冲区或 OoO 执行是“存储/加载重新排序”的原因,还是两者都是?

换句话说:假设我以某种方式在 x86 机器上观察到了这个试金石,是因为存储缓冲区还是 …

x86 cpu-architecture memory-model memory-barriers

4
推荐指数
1
解决办法
524
查看次数

存储缓冲区和重新排序缓冲区是否都用于推测指令?

我知道重新排序缓冲区可以用作推测执行需求的历史缓冲区,并且它还允许将寄存器重命名为 ROB 条目(以中断命名依赖项,作为寄存器重命名)。

在线阅读资源/手册,似乎存储缓冲区也用于推测执行。如果路径预测错误,ROB 是否足以丢弃值?

x86 cpu-architecture

1
推荐指数
1
解决办法
192
查看次数

分支预测和 UB(未定义行为)

我对分支预测了解一些。这发生在 CPU 上,与编译无关。尽管您可能能够告诉编译器一个分支是否比另一个分支更有可能,例如在 C++20 中,通过[[likely]]and [[unlikely]](请参阅cppreference)这与 CPU 执行的分支预测是分开的(请参阅我可以使用我的代码改进分支预测吗? ?)。

据我所知,当我有一个循环(带有退出条件)时,CPU 会预测退出条件不会得到满足,并尝试在循环内执行一些操作,即使条件尚未检查。如果 CPU 预测正确,它会节省一些时间,一切都会好起来。然而,如果它无法正确预测会发生什么?我知道这会对性能造成影响,但我不知道一些已经完成的操作是否被丢弃或逆转,或者只是如何处理。

现在我想出了两个简单的例子。第一个(如果我们忽略编译器可能只是在编译时计算总和并且我假设没有发生优化)对于 CPU 来说应该很容易预测。循环条件始终相同,并且循环中的条件仅切换一次。这意味着预测将为我们带来很好的性能提升,即使它失败了几次,添加一个数字也可以很容易地逆转。

在第二个示例中,退出条件也很容易预测。在循环体中,我int通过分配一个新数组malloc。请注意,我不是故意释放它的,因为我希望分配能够长期成功,以便 CPU 预测到这一成功。有时,当我用完内存(我没有计算总内存消耗并假设内存不会移动到磁盘)或发生其他错误时,分配会失败。这意味着ptrwill beNULL并且取消引用它是UB。没有定义会发生什么,它可能只是一个空操作,使我的程序崩溃或导致我的电脑飞走。因此我得出结论,CPU 不能简单地撤消这一点,我想知道会发生什么。

#include <stdlib.h>

#define VERSION 1

#if VERSION == 1
int main() {
    size_t sum = 0ull;

    for (size_t i = 0ull, max = 1'000ull; i < max;  ++i) {
        if (i < (max / 2)) {
            sum += 2 * i;
        } …
Run Code Online (Sandbox Code Playgroud)

c c++ cpu-architecture undefined-behavior branch-prediction

1
推荐指数
1
解决办法
317
查看次数