当前位置: 首页 > 知识库问答 >
问题:

我的CPU是否可以在每个CPU周期执行多个NOP?

衡子琪
2023-03-14

我写了一个简单的程序,在一个循环中执行一堆NOP指令,令我惊讶的是,它每秒执行大约106000000000条指令,或大约10Ghz,而我的CPU只有2.2GHz。

这怎么可能?CPU是将它们视为单个兆NOP,还是我刚刚发现“指令级并行性”是什么意思?

每秒指令的更好衡量标准是什么?添加指令仅达到414900000/s,是我CPU报告的bogomips的十分之一:4390.03

C代码:

#include <stdio.h>
#include <stdint.h>
#include <time.h>

#define ten(a) a a a a a a a a a a
#define hundred(a) ten(a) ten(a) ten(a) ten(a) ten(a) ten(a) ten(a) \
        ten(a) ten(a) ten(a)

#define ITER 10000000
int main(void) {
  uint64_t i=0;
  uint64_t t=time(NULL);
  while(1) {
    for(int j=0; j<ITER;j++) {
    hundred(asm volatile ("nop");)
    }
    i+=ITER*100;
    printf("%lu/%lu\n", i, time(NULL)-t);
  }
  return 0;
}

编译的程序集:

    .file   "gbloopinc.c"
    .section    .rodata
.LC0:
    .string "%lu/%lu\n"
    .text
    .globl  main
    .type   main, @function
main:
.LFB0:
    .cfi_startproc
    pushq   %rbp
    .cfi_def_cfa_offset 16
    .cfi_offset 6, -16
    movq    %rsp, %rbp
    .cfi_def_cfa_register 6
    subq    $32, %rsp
    movq    $0, -16(%rbp)
    movl    $0, %edi
    call    time
    movq    %rax, -8(%rbp)
.L4:
    movl    $0, -20(%rbp)
    jmp .L2
.L3:
#APP
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
# 15 "gbloopinc.c" 1
    nop
# 0 "" 2
#NO_APP
    addl    $1, -20(%rbp)
.L2:
    cmpl    $9999999, -20(%rbp)
    jle .L3
    addq    $1000000000, -16(%rbp)
    movl    $0, %edi
    call    time
    subq    -8(%rbp), %rax
    movq    %rax, %rdx
    movq    -16(%rbp), %rax
    movq    %rax, %rsi
    movl    $.LC0, %edi
    movl    $0, %eax
    call    printf
    jmp .L4
    .cfi_endproc
.LFE0:
    .size   main, .-main
    .ident  "GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.2) 5.4.0 20160609"
    .section    .note.GNU-stack,"",@progbits

共有2个答案

陈瀚玥
2023-03-14

我将进一步阐述汉帕桑的评论。

现代处理器既是超标量的,也是多核的。很容易理解什么是多核处理器 - 它具有多个内核。另一方面,Superscalar需要更多关于硬件的知识。这是一个堆栈交换问题,解释了处理器超标量意味着什么。超标量处理器在同一内核中具有许多功能单元,并且大量流水线化。这就是为什么可以在单个内核中同时调度和运行多个指令的原因。以下是处理器中的一些功能单元:整数加法/减法,浮点乘法,浮点除法,整数乘法,整数除法。

我鼓励您Google更多关于超标量处理器的信息,并特别查找有关您的处理器的更多信息。

魏彦
2023-03-14

这和多核无关。核心不是“端口”。

每个时钟 4 个 NOP 是超标量/无序 CPU 的问题/停用管道宽度。NOP甚至不需要执行单元/执行端口(ALU或加载或存储),因此您甚至不受整数执行单元数量的限制。即使是Core2(英特尔的第一个4宽x86 CPU)也可以运行每个时钟4 NOP。

正如您所猜测的,这是指令级并行性的一个示例。NOP当然没有输入依赖性。

在您的Sandybridge CPU(每个内核有3个ALU执行单元)上,每个时钟可以运行3个ADD和一个load或store指令,因为它的流水线宽度是4 uOS。请参阅Agner Fog的Microch pdf和x86标签wiki中的其他链接。在独立的ADD指令流上,如

add  eax, eax
add  ebx, ebx
add  ecx, ecx
add  edx, edx
...

在SnB上,每个时钟的吞吐量大约为3个,在整数ALU执行端口上,这是瓶颈。Haswell可以在每个时钟上运行4次加法,因为它有第四个ALU执行端口,可以处理非矢量整数运算(和分支)。

无序CPU通常比执行单元的数量具有更宽的前端和发出/收回宽度。一有空闲的执行单元,就有更多的指令被解码并准备好执行,这提高了它们的利用率。否则,如果执行由于串行依赖性而停止或变慢,无序机器只能提前看到当前正在执行的内容。(例如< code>add eax,eax / add eax,eax需要第一个add的输出作为第二个add的输入,所以每个时钟只能运行一个insn。)

 类似资料:
  • 考虑在ARM Cortex-A72处理器上运行的以下代码(此处为优化指南)。我已经包括了每个执行端口的资源压力: 虽然 可以在 F0 或 F1 端口上运行,但我选择将其完全归因于 F1,因为除了此指令之外,F0 上的高压很高,F1 上的压力为零。 除了循环计数器和数组指针之外,循环迭代之间没有依赖关系;与循环主体的其余部分所花费的时间相比,这些应该很快得到解决。 因此,我的直觉是,这段代码应该是吞

  • 我正在将分支目标与NOP对齐,有时CPU会执行这些NOP,最多15个NOP。Skylake在一个周期内可以执行多少个1字节的NOP?其他兼容英特尔的处理器呢,比如AMD?我不仅对Skylake感兴趣,而且对其他微架构也感兴趣。执行一个15个NOP的序列需要多少个周期?我想知道增加这些NOP的额外代码大小和额外执行时间是否值得它的代价。添加这些NOP的不是我,而是每当我编写指令时自动生成的汇编程序。

  • Per-cpu 变量是一项内核特性。从它的名字你就可以理解这项特性的意义了。我们可以创建一个变量,然后每个 CPU 上都会有一个此变量的拷贝。本节我们来看下这个特性,并试着去理解它是如何实现以及工作的。 内核提供了一个创建 per-cpu 变量的 API - DEFINE_PER_CPU 宏: #define DEFINE_PER_CPU(type, name) \ DEFINE_

  • 问题内容: 我正在尝试在运行mako内核的Nexus 4的所有cpus上设置性能监视器用户模式启用寄存器。 现在,我在可加载模块中设置寄存器: 问题是on_each_cpu仅在Online cpus上运行该功能,如printk语句所示: 当我调用on_each_cpu时,只有四个在线。所以我的问题是,如何强制cpu联机,或者如何强制某个cpu执行代码?谢谢 问题答案: 你并不需要运行在每个CPU的

  • 问题内容: 我刚刚开始使用Joblib模块,并且试图了解Parallel函数的工作方式。下面是并行化导致更长运行时间的示例,但我不明白为什么。我在1 cpu上的运行时间为51秒,而2 cpu上的运行时间为217秒。 我的假设是并行运行循环会将列表a和b复制到每个处理器。然后将item_n分配给一个cpu,将item_n + 1分配给另一个cpu,执行该函数,然后将结果写回到一个列表中(按顺序)。然

  • 基于每个JVM的CPU核数创建线程与在多个JVM上运行的线程在CPU核数上创建线程数,条件是所有JVM运行在共享同一CPU的一个物理系统上有何不同?换句话说,一个并行运行8个线程的多线程Java程序vs在共享同一CPU的8个不同JVM上运行的同一多线程程序? 下面我给出了一些我发现的用线程实现并行处理的方法,但是我不能理解它们之间的本质区别? 方法一:线程周期性地查询数据库更改,并行地启动(长时间