我写了一个简单的程序,在一个循环中执行一堆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
我将进一步阐述汉帕桑的评论。
现代处理器既是超标量的,也是多核的。很容易理解什么是多核处理器 - 它具有多个内核。另一方面,Superscalar需要更多关于硬件的知识。这是一个堆栈交换问题,解释了处理器超标量意味着什么。超标量处理器在同一内核中具有许多功能单元,并且大量流水线化。这就是为什么可以在单个内核中同时调度和运行多个指令的原因。以下是处理器中的一些功能单元:整数加法/减法,浮点乘法,浮点除法,整数乘法,整数除法。
我鼓励您Google更多关于超标量处理器的信息,并特别查找有关您的处理器的更多信息。
这和多核无关。核心不是“端口”。
每个时钟 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上运行的同一多线程程序? 下面我给出了一些我发现的用线程实现并行处理的方法,但是我不能理解它们之间的本质区别? 方法一:线程周期性地查询数据库更改,并行地启动(长时间