性能:如何提高LLC(最后一级缓存)的命中率

性能问题:L3命中率较低

在这里插入图片描述

CPU的最后一级缓存(一般也就是L3),如果命中率不高,对系统性能会有极坏的影响。所以对这一问题,我们呀及时准确的检测、暴露出来。

缓存的命中率,是CPU性能的一个关键性能指标。我们知道,CPU里面有好几级缓存(cache),每一级缓存都比后面以及缓存速度访问快,最后以及缓存叫做LLC(Last Level Cache),LLC的后面就是内存。

当 CPU 需要访问一块数据或者指令时,它会首先查看最靠近的一级缓存(L1);如果数据存在,那么就是缓存命中(Cache Hit),否则就是不命中(Cache Miss),需要继续查询下一级缓存。

缓存不命中的比例对CPU的性能影响很大,尤其是最后一级缓存的不命中时,对性能的损害尤其严重。这个损害主要有方面的性能影响。

  • 第一个方面,会影响CPU速度。内存的访问延迟,是LLC的延迟的的很多倍(比如五倍);所以 LLC 不命中对计算速度的影响可想而知。
  • 第二个方面,会影响内存带宽
    • 如果LLC没有命中,那么就只能从内存里面去取了。LLC不命中的计数,其实就是对内存访问的计数,因为CPU对内存的访问总是要经过LLC,不会跳过LLC的。所以每一次 LLC 不命中,就会导致一次内存访问;反之也是成立的:每一次内存访问都是因为 LLC 没有命中。
    • 更重要的是,一个系统的内存带宽是有限制的,很有可能会成为性能瓶颈。从内存中取数据,就会占用内存带宽。因此,如果LLC命中率低的话,那么对内存带宽的使用就会很大。内存带宽使用率很高的话,内存的存取延迟就会急剧上升。
    • 另外,最近几年计算机和互联网发展的趋势是,后台系统需要对越来越多的数据进行处理,因此内存带宽越来越成为性能瓶颈

使用perf策略LLC不命中率

perf的工作原理:

  • 它是在内部使用性能监视单元,也就是PMU(Performance Monitoring Units)硬件来收集各种相关CPU硬件事件的数据,并不会给系统带来太大开销
  • 注意,PMU硬件是针对每种处理器特别实现的,所以支持的事件集合以及具体事件原理,在处理器之间可能不同。

PMU 尤其可以监测 LLC 相关的指标数据,比如 LLC 读写计数、LLC 不命中计数、LLC 预先提取计数等指标。具体用 Perf 来测量 LLC 各种计数的命令格式是:

perf stat -e LLC-loads,LLC-load-misses,LLC-stores,LLC-store-misses

下图显示的是一次 Perf 执行结果。
在这里插入图片描述
我们可以看到,在这段取样时间内,有 1951M(19.51 亿)次 LLC 的读取,大约 16% 是不命中。有 313M(3.13 亿)次 LLC 的写入,差不多 24% 是不命中。

如何降低 LLC 的不命中率?

根据具体的问题,至少有三个解决方案。而且,这三个方案也不是互相排斥的,完全可以同时使用。
在这里插入图片描述

缩小数据结构,让数据变得紧凑

  • 对一个系统而言,所有的缓存大小,包括最后一级缓存LLC,都是固定的。
  • 如果每个数据变小,各级缓存自然可以缓存更多的数据,也就可以提高缓存的命中率。

举个例子,开源的 C++ Folly 库里面有很多类,比如 F14ValueMap,就比一般的标准库实现小很多,从而占用比较少的内存;采用它的话,自然缓存的命中率就比较高。

用软件方式来预取数据

  • 通过合理预测,把以后可能要读取的数据提前取出,放到缓存里面,这样可以提高缓存命中率
  • 这是一种“空间换时间”的策略,因为付出的代价是占用了缓存空间。当然,这个预测的结果可能不正确

现代CPU其实一般都有硬件指令和数据预取功能

  • 也就是根据程序的运行状态进程预测,并提取把指令和数据预取到缓存中
  • 这种硬件预测针对连续性的内存访问非常有效。
  • 但是很多情况下,程序对内存的访问模式是随机的、不规则的,也就是不连续的。
  • 硬件预取器对于这种随机的访问模式,根本无法做出正确的预测,这就需要软件预取

软件预取就是一种预取到缓存中的技术,以便及时提供给CPU、减少CPU停顿,从而降低缓存的不命中率,也就提高了CPU的使用效率。

现代CPU都提供相应的预取指令

  • 具体来讲,Windows 下可以使用 VC++ 提供的_mm_prefetch 函数
  • Linux 下可以使用 GCC 提供的 __builtin_prefetch 函数。
  • GCC提供了这样的接口,允许开发人员向编译器提供提示,从而帮助GCC为底层的编译处理器产生预取指令。这种策略在硬件预取不能正确、及时的预取数据时,极为有用。

但是软件预取也是有代价的:

  • 预取的操作本身也是一种CPU指令,执行它就会占用CPU的周期。
  • 预取的内存数据总是会占用缓存空间。
    • 因为缓存空间很有限,这样可能会踢出其他的缓存的内容,从而造成被踢出内容的缓存不命中
    • 如果预取的数据没有及时被用到,或者带来的好处不大,甚至小于带来的踢出其他缓存相对应的代价,那么软件预取就不会提升性能

实践建议:

  • 软件预取最好只针对绝对必要的情况,就是会实际导致CPU停顿的数据进行预取
  • 对于很长的循环(就是循环次数比较多),尽量提前预取后面的两到三个循环所需要的数据。
  • 而对于短些的循环(循环次数比较少),可以试试在进入循环之前,就把数据提前预取到。

去除伪共享缓存

  • 通过让每个数据结构变大,牺牲一点存储空间,来解决伪共享缓存的问题

那什么是伪共享缓存?

  • 内存缓存系统中,一般是以缓存行(cache line)为单位存储的。最常见的缓存行大小是64个字节。
  • 现代CPU为了保证缓存相对于内存的一致性,必须实时监测每个核对缓存相对应的内存位置的修改。如果不同核所对应的缓存,其实是对应内存的同一个位置,那么对于这些缓存位置的修改,就必须轮流有序的执行,以保证内存一致性。
  • 但是,这将导致核与核之间产生竞争关系,因为一个核对内存的修改,将导致另外的核在该处内存上的缓存失效。在多线程的场景下就会导致这样的问题。当多线程修改看似互相独立的变量时,如果这些变量共享同一个缓存行,就会无意中影响彼此的性能,这就是伪共享

你可以参考下面这张 Intel 公司提供的图,两个线程运行在不同的核上,每个核都有自己单独的缓存,并且两个线程访问同一个缓存行。

  • 如果线程 0 修改了缓存行的一部分,比如一个字节,那么为了保证缓存一致性,这个核上的整个缓存行的 64 字节,都必须写回到内存;
  • 这就导致其他核的对应缓存行失效。其他核的缓存就必须从内存读取最新的缓存行数据。这就造成了其他线程(比如线程 1)相对较大的停顿。

在这里插入图片描述

这个问题就是伪共享缓存。之所以称为“伪共享”,是因为,单单从程序代码上看,好像线程间没有冲突,可以完美共享内存,所以看不出什么问题。由于这种冲突性共享导致的问题不是程序本意,而是由于底层缓存按块存取和缓存一致性的机制导致的,所以才称为“伪共享”。

所以,我们开发程序时,不同线程的数据要尽量放到不同的缓存行,避免多线程同时频繁地修改同一个缓存行。

  • 举个具体例子,假如我们要写一个多线程的程序来做分布式的统计工作,为了避免线程对于同一个变量的竞争,我们一般会定义一个数组,让每个线程修改其中一个元素。当需要总体统计信息时,再将所有元素相加得到结果。
  • 但是,如果这个数组的元素是整数,因为一个整数只占用几个字节,那么一个 64 字节的缓存行会包含多个整数,就会导致几个线程共享一个缓存行,产生“伪共享”问题。
  • 这个问题的解决方案,是让每个元素单独占用一个缓存行,比如 64 字节,也就是按缓存行的大小来对齐(Cache Line Alignment)。
  • 具体方法怎么实现呢?其实就是插入一些无用的字节(Padding)。这样的好处,是多个线程可以修改各自的元素和对应的缓存行,不会存在缓存行竞争,也就避免了“伪共享”问题。
  • 5
    点赞
  • 24
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 共振LLC变换器是一种广泛应用于电源转换器中的拓扑结构。对于给定的输入电压(33V)和输出电压(400V),我们可以设计一个250W的LLC变换器。 LLC变换器由谐振电感(Lr),线圈(Lm),电容(C)和开关器件(MOSFET)组成。当开关器件导通时,输入电压被储存于电容中,然后当开关器件断开时,通过谐振电感和线圈,能量传输到输出负载。 为了设计一个250W的LLC变换器,我们需要优化谐振电感和电容的数值以满足输出电压和功率要求。同时,我们还需要选择适当的开关器件以满足所需的输入电压和电流水平。 在操作方面,LLC变换器采用高频谐振原理,在某个特定频率范围内正好符合输入和输出电压之间的谐振特性。通过调节谐振电感和线圈之间的参数,我们可以在设计过程中确定所需的共振频率。 此外,在设计LLC变换器时需要考虑到电路的失真和效率问题。通过合理选择开关器件和控制参数,可以减少能量损耗,并提高转换效率。 总而言之,对于250W 33V输入和400V输出的LLC变换器,我们可以通过适当的设计和操作来满足所需的功率和电压要求,并实现高效的能量转换。 ### 回答2: 共振LLC变换器是一种用于电力转换的拓扑结构。这种变换器采用共振设计原理,可以高效地将输入电压转换为所需的输出电压。根据提供的规格要求,这个特定的共振LLC变换器的输入电压为33V,输出电压为400V,输出功率为250W。 共振LLC变换器的工作原理是通过变压器、电容和电感器的共振来实现电力转换。该变换器将输入直流电压通过谐振变压器进行变压,然后利用电容和电感器的谐振来实现能量转移和输出稳定的直流电压。 对于这个特定的设计,输入电压为33V,则需要首先通过变压器将输入电压升高到400V。接下来,通过选择合适的电感和电容来实现谐振频率的精确匹配,以实现电能的高效转换。同时,需要精确控制开关频率和占空比,以获得所需的输出电压和功率。 在设计共振LLC变换器时,需要考虑以下几个方面:首先,选择合适的变压比以及电感和电容的数值,以实现所需的电压转换比和谐振频率。其次,通过合适的控制方法来实现开关频率和占空比的精确控制。另外,还需要考虑电路的稳定性和保护功能,以确保变换器在工作时稳定可靠。 综上所述,共振LLC变换器是一种高效的电力转换器,通过谐振设计原理实现输入电压向输出电压的转换。对于所给的250W 33Vin 400Vout的设计要求,设计可通过精确选择变压比和合适的电感和电容数值来实现,同时兼顾开关频率和占空比的控制,以确保输出电压和功率的稳定。 ### 回答3: 共振LLC变换器是一种高效率的电力转换设备,通常用于电源模块和电子设备中。对于给定的设计参数,如输入电压为33V和输出电压为400V,其操作和设计原理如下: 1. 操作原理: 共振LLC变换器主要由两个电感器(L1和L2)、一个电容器(C1)和开关管组成。它利用电感器和电容器的振荡来实现能量的转换和稳定输出。其操作可以分为三个主要阶段:充放电、开关以及共振。 - 充放电:在开关管导通之前,L1和L2之间的电容器C1被充电,存储一定的能量。 - 开关:当开关管导通时,电容器C1的电能通过电感L1传递给电感L2,进而转换为输出电压。 - 共振:在一定时间间隔内,L1和L2之间的电能在电容器C1和电感L2之间来回振荡,以控制输出电压的稳定性。 2. 设计原理: 在设计共振LLC变换器时,需要考虑以下几个因素以实现预期的工作效果: - 输入电压:根据给定的参数,输入电压为33V。 - 输出电压:根据给定的参数,输出电压为400V。 - 输出功率:根据给定的参数,输出功率为250W。 - 工作频率:共振LLC变换器的工作频率通常选择在数十kHz到数百kHz范围内,以实现高效率和小尺寸。 在设计过程中,需要选择合适的电感器L1和L2以及电容器C1,以满足输入和输出电压要求,并确保合适的功率传输。此外,还需要合理选择开关管的类型和参数,以确保稳定和高效的工作。 总之,共振LLC变换器是一种高效率的电力转换器,其操作原理基于共振振荡,可通过选择合适的元件参数来实现所需的输入和输出电压。在设计中,需要考虑输入电压、输出电压、输出功率以及工作频率等因素,以确保满足电源模块或电子设备的要求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值