应用程序性能瓶颈中的CPU缓存优化

本文探讨了在多线程环境中,不当操作共享变量可能导致的性能问题。文章详细阐述了CPU的硬件架构,包括L1、L2、L3缓存和内存之间的关系,以及缓存行的概念。重点讨论了如何提高缓存命中率,包括数据缓存和指令缓存的命中,以及在多核CPU下通过核绑定避免缓存竞争。此外,还提到了伪缓存行问题及其解决方案——缓存行填充,以优化程序性能。
摘要由CSDN通过智能技术生成

1.前言

        在应用程序中会有大量的对变量的操作,在一般情况下不会导致问题,但在多线程操作共享变量时,不当的操作会产生大量的冗余操作,造成性能的浪费。这篇文章主要从编码方式与逻辑策略对变量从CPU寄存器,CPU缓存,直至内存,描述变量的生命周期,性能瓶颈与解决方式。

2.基本原理

2.1 硬件架构

        在CPU需要对数据进行操作时,会按照以下顺序对数据进行获取,若未获取到则继续向下一级获取,直至内存中命中。

         在上面是普通的双路(安装了两颗CPU)双核CPU,也有不带L3的CPU。一般情况下CPU数据读取是按照上述过程执行。可以看出L1,L2是核内共享,L3为CPU内多核共享,而内存为多CPU共享,寄存器会优先去L1查找,再去L2,L3,内存中查找。不同CPU架构可能会导致L1,L2,L3架构层次不同。

        注意:L1分为数据L1和指令L1两部分

2.2 存储层次

        这里引用一张《深入理解计算机系统》中的图

存储器层次结构

        里面完整描述了各部件与速度的关系,各部件获取数据的速度大约在:

部件时钟周期
L12-4
L210-20
L320-60
内存200-300

        时钟周期:时钟周期是CPU主频的倒数,比如2GHz主频的CPU,一个时钟周期是0.5ns,也就是说,主频越高,时钟周期越小。

2.3 实际参数

        以下是一个6核12线程i7-10750H CPU的参数。

缓存大小:        

        在上面的结构中,可以看到,6个核心中,每个核心有32K的数据L1,32K的指令L1,还有256K的L2,12M的L3。在上面可以看到,L1和L2前面分别跟了‘6 X’,而L3没有,表明L3为CPU共享三级缓存。

缓存路数:

        可以看到缓存后有个8-way,4-way,16-way这种,缓存路数用来将缓存行打包标记,用于快速查找数据。比如L1的数据缓存有32K,每个缓存行有64B,那么这个L1的数据缓存中就有32*1024/64=512个缓存行,而这的8-way代表将每8个缓存行打包为512/8=64标记。

CPU差异:

        不同CPU架构不同,比如AMD 5700U采用Zen2架构,他的L3为两个,多核共享,并不是常见的CPU核内共享。

2.4 缓存行

        缓存行是CPU中很重要的一个结构,在CPU读取数据A时,后面可能再次访问到,并且可能会访问到相邻的数据B,所以为了读取效率,应用到了缓存行的概念,CPU会在读取数据时,将命中对象相邻的区域也读取到缓存行中,这个区域也就是缓存行的大小一般是64Byte。

序号数据数据数据数据数据数据数据数据
1      1(byte)    1(byte)    1(byte)    1(byte)    1(byte)    1(byte)    1(byte)    1(byte)
2  1(byte)    1(byte)    1(byte)    1(byte)    1(byte)    1(byte)    1(byte)    1(byte)
3  1(byte)    1(byte)    1(byte)    1(byte)    1(byte)    1(byte)    1(byte)    1(byte)
...........................

3.性能瓶颈

3.1 提升缓存命中率

        在CPU的L1缓存中,分为数据缓存与指令缓存,数据缓存用来存储被缓存到的数据,指令缓存用户存放CPU的逻辑判断信息。

3.1.1 数据缓存命中

        当对一个数组进行遍历的时候,如果在使用到缓存的时候多次击穿缓存,会造成大量的性能损失,导致性能不达预期,为提高缓存命中率,我们应该顺序的读取缓存中的内容,尽可能提高数据缓存命中率,提高程序运行效率。

CPU缓存与性能优化 | 码农家园 (codenong.com)

3.1.2 指令缓存命中

        在CPU中具备分支预测功能,当代码中出现if,switch等分支语句时,意味着CPU可以选择跳转到两段不同的指令去运行。如果分支预测器可以预测接下来要在哪段代码执行(比如if还是else中的指令),就可以提前把这些指令放在缓存中,CPU 执行时就会更快。

容易命中的判断放前面:

        在需要做逻辑判断时,使用尽可能的将最容易命中的概率放在前面,将不容易命中的放后面,这样能减少判断次数,提高效率。

数据先排序再循环:

        在数据需要循环数据并排序的时候,尽量先排序再进行循环处理,提高分支预测命中率。

3.1.3 多核CPU下缓存命中(核绑定)

        在现代多核CPU中,多个CPU核对同一资源竞争的情况下会导致缓存的丢失,也导致程序性能的下降,所以在程序中,配置线程个数不一定是越多越好,而是因地制宜,根据程序对性能的需求与硬件的条件,设定不同参数,达到最高性能。而有时程序需要对数据进行计算的时候,频繁的CPU切换会导致大量的缓存穿透,造成性能下降,我们可以在这时绑定CPU,强制CPU在某个核心中计算。

3.2 伪缓存行

解决方式:缓存行填充

  • 2
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 打赏
    打赏
  • 1
    评论
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

扶朕去网吧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值