Linux下做性能分析6:理解一些基础的CPU执行模型

news2024/11/19 13:26:12

[介绍]

前面介绍了两个典型的调度模型,如果调度没有问题,剩下的问题就是正面刚算法了。那个不是我这里要介绍的主题的。

但,Not Really。其实除了算法在消耗CPU,CPU还是有很多余力可以挖掘的,这一篇我们专门讨论一下CPU的执行模型,看看我们在算法本身以外,还可以怎么优化我们程序的执行模型。

[流水线]

在不少软件人员的想象中,似乎只要保证CPU的占用率是100%,CPU应该是很忙的,应该在执行完一条指令,然后执行下一条指令,没有空干别的事情。

但如果我们深入进去看,实际上CPU里面也不是只有一个执行部件。假设有一个CPU上有4个执行部件,这些执行部件在CPU时钟的驱动下,一跳一跳地完成每一个动作,并完成一个指令一个指令的执行,这个执行流程就会是这样的:

看见了把,如果CPU真的这样执行,“取指”这个部件在一条指令的执行中,有三跳(CPU称为时钟周期)其实是“闲”着的。

所以,合理的模型应该是这样的:

也就是说,i1(被取指这个部件)执行后,取指部件反正闲着也是闲着,不如就直接执行下一条指令的取指就好了。这样算起来,其实不是4个时钟周期执行一条指令的,实际上是一个时钟周期执行一条指令的。这个执行模型,我们就称为“流水线”。它和工厂中的生产流水线的调度原理几乎是一样的。每个执行部件在一条指令中执行占用的那个时间,称为一个Stage,一条指令包含多个Stage,但第二条指令并不需要等待上一条指令的所有Stage都完成了才开始自己的stage。每条指令包含的Stage数目,我们称为流水线的长度。流水线的长度决定了一条指令要多长时间才能完成,但如果流水线一切正常,平均起来,我们只需要一个stage的时间,就可以执行一条指令。

现代CPU的流水线是很长的,比如ARM的A57,流水线长度超过15。所以,看起来一条指令需要15个时钟周期,实际上你只需要1个时钟周期就可以执行一条指令。

[流水线的破坏1:指令依赖]

前面这个模型看起来很美,但实际上不是这样的,有很多问题会破坏流水线。最常见的破坏是指令依赖。比如你写如下汇编:

add r1, r2, r3 #r1=r2+r3
add r1, 1 #r1=r1+1
add r4, 2

这里第一条指令计算r1,第二条指令使用r1,第一条指令没有执行完,第二条指令译码完了,一看,我靠,要用r1,前面的还没有搞完,等等吧,就成这样了:

你看,CPU其实又闲下来了。(注:有人说,这个地方处理器可以通过寄存器改名实现不用等待,这句话没有错,但其实芯片不止这一种优化方法,我们要理解核心逻辑,在主线逻辑上拉这种逻辑没有意义,我们还是先聚焦原理,如果你有兴趣讨论这种细节,我们可以单独拉线索来讨论)

高级的CPU,编译器,都会进行指令调度。比如我们看到第三条指令跟谁没有没有依赖,我们可以把它调整到第二条的前面,这样可以填补一定的时间空间,这个执行会变成这样:

这个效率就又高了一点了。

流水线是个很麻烦的事情,而且你在玩这种小聪明,芯片设计师也在玩这种小聪明,所以,不到严重破坏的程度,我们不会在设计的时候就考虑它,尽量把它交给编译器和CPU自己。很多半桶水的程序员,会以为用汇编写的程序比用C写的程序效率高,其实这个基本上都是错的。因为你写汇编代码很难考虑流水线(特别是这里不光有指令的调度,还有寄存器的调度,用不同的寄存器,可能可以造成不同的依赖,从而优化流水线的执行),如果你强行考虑流水线了,你的代码也没法看了,因为它不是以人脑为对象的了,完全是机器的思维),而编译器考虑这样的东西,毫无压力。所以,我们只在流水线特别糟糕的地方考虑用汇编优化一下,而不会吃饱没事到处写汇编。这也是为什么多言数穷,不如守中。大家都想耍小聪明,这个系统就不聪明了,各守本分才“合道”。

[流水线的破坏2:跳转]

跳转指令也会引起流水线的破坏。考虑如下序列:

1:
...
add r1, r2, r3
jmp 1b  #jump back to label 1
add r2, 1
add r3, 2

流水线确实把4条指令都执行了,但没有什么鬼用,因为第二条指令跳转到别的地方去了,后面两条指令执行了也是白执行。这种情况叫“指令预测失效”,也是破坏流水线的行为。

所以你经常看到一些高性能程序里面写这样的代码:

for(i=0; i<800; i+=4) {
  a[i] = x;
  a[i+1] = x;
  a[i+2] = x;
  a[i+3] = x;
}

这个代码看起来完全可以用这样一个简单的代码代替:

for(i=0; i<800; i++) {
  a[i] = x;
}

而作者要写成上面那个鬼样子,很多时候就是为了优化流水线。让跳转不要那么快发生。但还是那句话,不要在开始设计的时候就优化,否则自取其辱。

如果读者习惯Linux的代码,会经常看到likely和unlikely这个宏,它的作用也是这个,考虑一下如下汇编:

xor r1, r1, r1
jz 2f #jump forward to label 2 if zero
add r2, r2, 1
...
2:

我们把分支放在jz后面还是放在2:后面呢?放在jz后面预测就会成功,放到2:后面预测就会失败。那我们就应该把最可能的结果放在jz后面,所以我们才有likely和unlikely,通知编译器,谁才是最有可能的,这样也能有效提高CPU的执行效率。

[流水线的破坏3:内存访问和Cache模型]

指令依赖中, 有一种依赖是要特别注意的,就是访存指令。访问内存是很慢的,你这样想象一下吧:我们执行一条指令可能就是几个时钟周期,但访问一次内存的时间可能就是几百个时钟周期。想象一下下面这个执行过程:

ldr r1, [addr1]
add r1, r1, 3
add r2, r2, 4
add r3, r3, 5

你以为这4条指令在一个流水线周期里就可以执行完了,实际是几百个时钟周期。这个效率一下就慢下来了。

我们当然可以把第三,四条指令提前,勉强填补一下中间的等待,但杯水车薪,也没有什么用。

这种时候,我们就要依赖Cache了,现代CPU系统有多级Cache,类似这样:

L1 Cache中有的,就从L1取,没有的就从L2取,……如此类推。这个问题考虑到他们的速度的时候,你就会发现其实是很严重的。

我们这样考虑这个问题吧:L1 Cache的访问速度是几个时钟周期(常常会是1个),L2是十几个,L3是几十个,到了内存上,就是几百个,如果是多道系统,插几个CPU,跨Socket的时候,就会更慢。

如果我们保证我们的执行尽量都在Cache的范围内,我们的性能就会提高。Cache Line的长度常常比寄存器的长度长,比如64位系统一个寄存器是8个字节,而Cache Line的长度常常可以达到128个字节。如果你的访问是对齐的,很多一次内存操作可以完成了动作,就不需要两次才能完成,这会大大提高执行的效率。另外,如果你在访存之前还有很多准备动作要做(memcpy一类的程序经常如此),你还可以通过Cache预取指令提前把内存的数据拉到Cache中,这也能大大提高效率。

还有一种会严重破坏性能的模型。称为Cache污染,大概的模型是:你的算法做得不好,总是访问一个Cache刚刚干掉的数据,每次访问都导致一次Cache刷新,性能就会严重下降。这个有很多论文了,我就不介绍了。基本上不是专业的研究者,我们也不用专门去记住这些模型,我们只要按功能,按软件构架的要求,把代码写出来,然后通过profiling工具去发现密集出现branch-miss,cache-miss的地方,根据情况作出优化就好了。

[异步调度模型]

上面我们给了一个基本的流水线模型。但实际上……呵呵,又来了……这种叫同步模型,现代的CPU,基本上不是这样的同步模型。现在CPU是异步调度模型。类似下面这样(网上随便找的图,侵删):

从中间开始,CPU的执行分成了两段。前面一段是取指有关的操作,后面一段是执行有关的操作。CPU有很多的执行通道,可以有多个定点或者浮点加法器,几个取指器等等。这样,实际上整个CPU就像一个多线程的软件程序:有一组线程负责把指令读出来,解码,然后送入队列,另一组线程负责从队列中把指令取出来,投入执行。这个执行并非严格的流水线模型,而更像我们这个系列文章最前面提到的那个队列模型。

在芯片的优化手册中,会给出前端和后端有哪些执行部件(比如一个比较新的RISC CPU上的前端是Fetch, Decode/Rename/Dispatch和Issue,后端是Branch, Int0, Int1, Int Multi-Cycle,FP0, FP1, Load, Store)。然后它还会给出每个指令需要占用哪些执行部件,已经这个指令的执行时延和执行通量。如果你要进行汇编一级的优化(比如为这个CPU配套编译器),你就需要根据这个优化手册对指令进行重排。而对于优化者,则首先看重程序的IPC(每个cycle执行多少条指令),然后查对应的stall参数,看有没有机会特别重排程序特定的部分,从而加快执行效率。

下面这个是Intel的一个Top Down模型(侵删):

前面一段就是取指有关的,是In-Order的操作,这部分是符合原来的流水线模型的。后面一段就是纯粹的调度。这个性能就不能完全按严格的流水线模型来考虑(加上超线程技术就会更加复杂)。所以现在你用perf stat执行一个程序,它会给你这个总结(不是每个CPU都支持这两个统计):

你可以看到了,它首先给你统计了一个stalled-cycles-frontend,和一个stalled-cycles-backend,通过这两个统计,你可以看到,无论你如何执行,你的系统到底有多少花在了前端的等待上,多少花在在后端的执行上。前者说明你供指令的速度不够快,后者说明你CPU处理不过来。我们可以以这个为基础,进一步找到系统的执行瓶颈。

Intel处理器上,这个模型称为Top-Down模型(现在ARMv8也开始用一样的名字了,据说这个名字原来来自IBM),以这个为分界,可以一步步分解下去,最终找到执行瓶颈在什么地方,我们从而可以找到合适的软件执行模型,提高系统的执行效率。这些模型首先和CPU的微架构是相关的,但基于我们原来的流水线中形成的经验,我们在一定程度上,到都有相当的机会了解到我们可以调整软件的什么设计来让CPU执行得更快。

[小结]

本文介绍了一些基础的CPU执行模型,一定程度上了解CPU的执行模型,有助于我们正确找到系统的性能瓶颈。但从这些模型中,我们也看到了,其实整个系统的每个模块都在尝试优化自己的执行效率,而作为最高层的软件,其实是最需要遵守“多言数穷,不如守中”策略的角色。从设计的角度,软件引导了整个需求的响应方法,软件守不稳,所有其他的小九九都是水月镜花,留不住的,我们不能理解这一点,也就不能理解为什么软件架构这么重要。

越是混沌的系统,越需要我们守得住基本面。

很多人也许觉得这里讨论的问题都很简单,但越是简单的东西你越守不住,当你被眼花缭乱的变化吸引了大部分的注意力的时候,你回过头来想一想,你当初的需求到底是什么。这就是我们说的:执古之道,以御今只有,能知古始,是谓道纪。我们能掌握现在的复杂局面,我们必须回到最开始解决的问题上,我们才有可能理解和控制现在的一切变化。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/756106.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

MySQL “error: ‘fd’: 未知重写说明符”

文章目录 1、【问题】MySQL “error: ‘fd’: 未知重写说明符”2、【解决】增加引用头文件 1、【问题】MySQL “error: ‘fd’: 未知重写说明符” 有以下代码&#xff0c;mysql 的 include 和 lib 都已经加入附加目录&#xff0c;libmysql.lib 已经加入依赖库&#xff1a; #p…

IEEE ICME 2023论文|基于交互式注意力的语音情感识别联合网络

论文题目&#xff1a; A Joint Network Based on Interactive Attention for Speech Emotion Recognition 作者列表&#xff1a; 胡英&#xff0c;侯世静&#xff0c;杨华敏&#xff0c;黄浩&#xff0c;何亮 研究背景 语音情感识别&#xff08;Speech Emotion Recognitio…

4. MySQL 的增删查改(重点 9000字详解)

目录 准备工作 一、数据的插入 &#xff08;insert&#xff09; 注意 1. 整行插入 2. 指定列的插入&#xff08;常用&#xff09; 3. 一次插入多行数据 4. 清空数据库的数据&#xff08;truncate&#xff09; 5. 拓展练习&#xff1a;对于数据库中的数据进行统计&#…

多智能体强化学习(MARL)研究汇总:行为分析、通信学习、协作学习、智能体建模

【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应用项目实现 专栏详细介绍:【强化学习原理+项目专栏】必看系列:单智能体、多智能体算法原理+项目实战、相关技巧(调参、画图等、趣味项目实现、学术应…

第一百零七天学习记录:C++核心:类和对象Ⅷ(五星重要)多态

多态 多态的基本概念 多态是C面向对象三大特性之一 多态分为两类 1、静态多态&#xff1a;函数重载 和 运算符重载属于静态多态&#xff0c;复用函数名 2、动态多态&#xff1a;派生类和虚函数实现运行时多态 静态多态和动态多态的区别&#xff1a; 1、静态多态的函数地址早绑…

Arthas的火焰图生成

之前说过用idea自带的工具生成火焰图&#xff0c;但是idea是在本地的&#xff0c;在机器上如何生成呢&#xff1f; 我觉得方法有很多&#xff0c;这里用arthas工具简单搞一搞 Arthas官网地址 下载Arthas 如果在机器上要下载整个包&#xff0c;arthas-boot.jar需要依赖其他的j…

上门按摩系统如何运营才能做大

预约上门按摩系统是一种在线平台或应用程序&#xff0c;用于帮助用户预约并安排专业按摩师上门提供按摩服务。这种系统通常为用户提供一个简便的方式来选择按摩服务类型、时间和地点&#xff0c;并与合适的按摩师进行预约。用户可以通过应用程序或网站浏览按摩师的资料和评论&a…

用googletest写cpp单测

框架概述 Google Test&#xff08;也称为 googletest&#xff09;是由 Google 开发的 C 单元测试框架。它的首个版本是在2004年发布的&#xff0c;作为 Google 内部的测试框架使用。随后&#xff0c;Google Test 在开源社区中得到广泛应用&#xff0c;并在许多项目和组织中成为…

总结927

今晚用了40分钟进行回顾&#xff0c;但这40分钟&#xff0c;能回顾一天所学&#xff1f;一共四门课&#xff0c;每门用10分钟回顾&#xff0c;光是书籍&#xff0c;资料的切换都需要30秒。10分钟回顾对于政治来说是足够的&#xff0c;但对于数学&#xff0c;能重做2~3道题就很不…

centos环境搭建nsq单点

简言 下载 启动nsq(单节点) 1. 启动nsqd 2. 启动nsqlookupd 3. 启动nsqadmin 查看状态 简言 1. nsq是go语言实现的分布式消息处理平台&#xff0c;类似我们常用的kafka&#xff0c;rocket mq等&#xff0c;目的是用来大规模地处理每天数以十亿计级别的消息。它具有分布式和…

前端:UI 交互式特效 —— Css、Js

&#x1f637;&#x1f60a;&#x1f93a;&#x1f93a;&#x1f93a;前期回顾 打造极简风格动效 —— 5 分钟轻松实现惊艳、震撼人心的视觉效果_彩色之外的博客-CSDN博客 &#x1f601; css动画 —— 把你喜欢css动画嵌入到浏览器中_css做的动画效果怎么嵌入网页_彩色之外的…

研0进阶式学习—-数据挖掘概念与技术

目录 【 写在前面】什么是数据挖掘为何进行模式评估如何进行模式评估数据挖掘的发展趋势 【 写在前面】 本科期间&#xff0c;数据挖掘算法学过一些&#xff0c;甚至本人的毕业设计也是围绕此展开的&#xff0c;但是显然学得太皮毛&#xff0c;今天偶然读到《数据挖掘•概念与…

基于tauri+vue3+pinia2客户端管理系统程序|tauri+vite4后台系统

TauriAdmin一款跨端通用后台系统模板解决方案 基于 tauri rust webview2 整合 vite4 搭建桌面端 vue3 管理后台模板TauriVue3Admin。支持多窗口切换管理、vue-i18n多语言、动态路由权限、常用业务功能模块及动态路由缓存等功能。 使用技术 编码工具&#xff1a;Vscode框架技术…

【chap4-链表】用Python3刷《代码随想录》

通过指针串联在一起的线性结构&#xff0c;每一个节点由两部分组成&#xff0c;一个是数据域data&#xff0c;另一个是指针域next&#xff08;存放指向下一个节点的指针&#xff09;&#xff0c;最后一个节点的指针域指向null&#xff08;空指针&#xff09; 链接的入口点称为…

耳夹式骨传导耳机有哪些比较好用?这三个款式不容错过!

骨传导耳机由于不入耳&#xff0c;不用担心耳道健康问题&#xff0c;越来越受到广大网友的喜欢&#xff0c;而传统的入耳式耳机&#xff0c;则因为长时间佩戴会耳朵痛&#xff0c;容易掉落等问题逐渐的被网友抛弃&#xff0c;那么在骨传导耳机市场种类这么多的情况下&#xff0…

Apache Kudu 在**医疗科技的生产实践

目录 说明 医疗场景下数据特点 KUDU 的介绍 kudu 架构 kudu 文件组织形式 kudu的生产实践 技术选型 整体的架构 项目遇到的问题 参考资料 说明 本文主要介绍APACHE KUDU 在**医疗科技数据实时分析场景下的实践&#xff0c;内容包括&#xff1a; 医疗场景下数据特点 …

mysql什么情况下行锁(表锁)(锁的概念)

1&#xff1a;数据表aa的设计结构 2&#xff1a; 使用navicat编写手动控制事务 3&#xff1a;先选择开启事务和执行更新操作&#xff0c;where b1&#xff08;表锁&#xff09;b不是索引&#xff0c;不提交事务&#xff0c;&#xff08;如果where b1&#xff0c;b是索引就行锁&…

本地Nginx部署React前端项目浅尝

目录 nginx [下载](http://nginx.org/en/download.html)nginx命令react打包文件放置nginx 配置 运行效果nginx踩坑根目录配置 nginx 下载 根据上面的版本找到适合自己的 nginx版本&#xff0c;我目前是环境是 windows&#xff0c;所以下载 稳定版本。 nginx命令 在下载的ngin…

数学建模-判断数据是否服从正态分布

大样本用qq图 >1000 皮尔逊相关系数需要正态性检验&#xff0c;利用上面三种方法其中一种 斯皮尔曼相关系数不用正态性检验

Claude2轻松解决代码Bug的实战方案

大家好,我是herosunly。985院校硕士毕业,现担任算法研究员一职,热衷于机器学习算法研究与应用。曾获得阿里云天池比赛第一名,CCF比赛第二名,科大讯飞比赛第三名。拥有多项发明专利。对机器学习和深度学习拥有自己独到的见解。曾经辅导过若干个非计算机专业的学生进入到算法…