单线程程序是否可以在CPU中并行执行?

在测量Intel第四代i5的CPI(每条指令的周期)时,我们得到的CPI

一些同学认为这是由于代码的并行执行所致,但这是C语言中的单线程代码,老师说当今的处理器是超标量的。

使用gcc -m32完成编译。

假设编译器没有通过并行化代码来发挥作用。

但是我的疑问仍然存在。由于当今的处理器在代码上做了一些小魔术,说乱序执行和推测性执行,我想知道是否:

  • 处理器是否在多个核心单线程程序中运行?

让我们说以下两条指令:

(1)添加%eax,(%eax) (1)addl%ebx,(%ebx)

Core-0运行(1),Core-1运行(2)

wohenzhuce 回答:单线程程序是否可以在CPU中并行执行?

是的,CPU在单个线程中发现指令级并行性,以便每个周期运行1条以上的指令。有关具体示例,请参见Why does re-initializing a register inside an unrolled ADD loop make it run faster even with more instructions inside the loop?

指令级并行性与线程级并行性无关(在问题的第二部分提出)。在运行单线程工作负载时,只有一个核心处于活动状态。

现代多核系统可以同时利用两者,但是您可以拥有一个而没有另一个。

例如,Sun的Niagara (UltraSPARC T1)是从头开始设计的,旨在利用线程级并行性(和内存级并行性),而不是像某些服务器工作负载那样尝试快速运行任何单个线程。它具有8个物理single-issue in-order cores和4路SMT(每个物理核心4个逻辑核心),而不是OoO执行程序来隐藏延迟(例如,高速缓存未命中)。单线程性能很糟糕,但是运行32个线程的最大吞吐量在2005年凭借其功率预算和晶体管数量是不错的。

与奔腾III一样,早期的x86也是超标量单核。只有多 socket 系统是SMP。但是这样的CPU可以并且确实达到CPI

您的i5第四代CPU是Haswell。 请参阅David Kanter's deep dive on Haswell's microarchitecture ,其中包括每个内核内部各个阶段的宽度框图。

  

处理器是否在多个核心单线程程序中运行?

,单个内核本身就是超标量的,例如在Haswell或Zen中具有4个整数ALU执行单元。 (在Intel CPU上,有3个SIMD ALU执行单元,它们与标量/通用整数ALU的执行端口相同。)前端宽度足够匹配。

通常,超标量CPU的每个时钟每个内核 至少可以运行2条指令。

您的问题中的这个错误猜想与程序员上的How does a single thread run on multiple cores?重复了。{strong}。每个核心都有一个广泛的前端,在后端具有多个执行单元。

直到我们扩大单个内核的收益递减为止,我们一直在构建多核CPU的过程中一直这样做。时间分片/抢占式多任务通常足够好。在几乎所有方面,一个更快的内核要好于1 / N速度的N个内核。但是,如今这不是折衷的选择。它是N个1 / sqrt(N)速度的内核或类似的东西。


  • 添加%eax,(%eax)
  • 添加%ebx,(%ebx)

这些内存目标添加指令每个完成一个周期以上(并在现代Intel上每个解码至少2 uop:加载+添加微融合,并存储(微融合存储地址+存储数据) )。如果它们在相同的物理内核上运行,则它们可以在同一周期内启动。

Ice Lake也可以在同一周期内执行两个存储,但是在此之前,现代x86 CPU每个时钟仅执行1个存储。 (例如,从Haswell到Coffee Lake的Intel可以在每个时钟周期执行2次加载+ 1个存储。SnB/ IvB可以在每个周期进行2次内存操作的地址生成,并且如果其中一个存储多达一个,则可以维持吞吐量。 256位向量的2 + 1特殊情况,可在2个数据周期内重复使用相同的地址生成。)

除非EAX和EBX拥有相同的指针值,否则这些指令访问不同的内存和不同的寄存器,并且它们完全独立,除了执行单元(加载,添加,存储)的资源冲突。 (寄存器重命名处理了FLAGS输出的写后写危险)。

,

是的。 CPU的很大一部分专用于所谓的调度,该调度将工作分配给CPU的内部资源。只要该调度电路能够证明两条指令不会与它们所需的资源(功能单元(例如,不同的ALU,最重要的是寄存器))冲突,则可以并行调度这些指令。这会导致CPI小于1。

彼此独立的典型指令是控制流(分支),整数算术和浮点算术。尤其是后两个实际上几乎总是独立的,因为它们需要非常不同的ALU,并且要处理不同类型的数据。因此,当您的程序运行时,例如

double a = 7.0,factor = 1.1;
for(int i = 42; i--; ) a *= factor;

您可能会发现,浮点电路在整数电路递减的同时执行乘法运算,并检查循环计数器,而控制流电路执行到下一个循环迭代的分支。这样的循环有可能在每次迭代中恰好在一个周期内执行...


我选择了各种指令的示例,因为它使您易于理解每个指令需要不同的资源。但是,现代CPU通常包含多个关键资源副本。例如,它们包含大量能够进行整数加/减的ALU,并且使用复杂的寄存器重命名方案来使用比汇编程序程序员可见的寄存器更多的物理寄存器。这样,即使它们是同一类型(例如,整数加法)并且在相同的寄存器上进行正式操作,它们也可以并行执行两条独立的指令。

基本上,您可以将CPU前端视为即时编译器,它将机器代码转换为内部指令集,其中包括尝试使尽可能多的CPU资源保持繁忙状态的优化器。

,

超标量处理器具有同时获取/解码/执行许多指令的能力。这是通过提供足够的硬件资源来处理几条指令来实现的。例如:Execute阶段将有多个ALU等。

,

了解一些distinction between parallelization and concurrency很重要。虽然这些术语经常互换使用,但实际上是不同的。

在存在多核处理器和多处理器系统之前,通常通过time slicing使用并发性,并发性通过串行执行代码来赋予并行处理的外观。在CPU时钟的相邻时间片中。最终结果是,事情将在大约同一时间完成,并且似乎同时完成。值得注意的是,并发仍被大量使用。

并行化运行多个线程,并允许在各个内核上异步完成工作,这些内核以后可以重新组合(或多或少)以在UI,游戏中的动作等方面提供预期的结果或反馈。

一些现代的编译器和CPU / GPU指令集可以并行化代码中未明确并行的内容。此外,某些基准测试可能会高估或低估给定内核或处理器的线程功能。

本文链接:https://www.f2er.com/3089126.html

大家都在问