You need to enable JavaScript to run this app.
最新活动
大模型
产品
解决方案
定价
生态与合作
支持与服务
开发者
了解我们

超线程对缓存的影响及L2命中耗时异常缩短技术问询

超线程下L2缓存命中耗时意外缩短的异常现象分析

现象概述

我碰到了一个超线程相关的反直觉现象:用C语言编写测试L2缓存命中耗时的程序A,单独运行时每次L2命中耗时约26周期,平均输出结果为200;但当我把A和一个仅含空循环的程序B绑定到同一核心以超线程模式运行后,A的L2命中耗时居然降到了约10周期,平均输出结果变为90。通常超线程带来的是资源竞争导致的性能下降,这种耗时反而缩短的情况非常奇怪。

测试环境与伪代码

服务器配置

Linux version 4.15.0-122-generic (buildd@lcy01-amd64-010) 
(gcc version 5.4.0 20160609 (Ubuntu 5.4.0-6ubuntu1~16.04.12)) 
#124~16.04.1-Ubuntu SMP

测试伪代码

程序A(L2缓存命中耗时测试)

// L1缓存为VIPT结构
在某组中选取16条缓存行(line 0-16)
// 组织链表防止硬件预取
将line0至line8的无序序列组织为链表
将line9至line15的无序序列组织为链表

// 预先加载line0-8到缓存中
load line 0-8 // 链表结构,串行读取
fence() // 确保缓存加载完成

t1 = rdtscp() // 读取时间戳计数器
load line 9-15 // 链表结构,串行读取
t2 = rdtscp()

print t2 - t1 // 输出耗时周期数

程序B(空循环)

for(;;){ }

可能的原因分析

结合x86超线程的核心架构特点,我梳理了几个可能的方向:

  • 缓存低功耗状态的影响:当程序A单独运行时,核心可能处于部分闲置状态,L2缓存的部分bank可能进入预充电/低功耗待机模式,此时第一次访问需要额外的周期激活;而程序B的空循环持续占用核心执行单元,迫使缓存控制器保持在活跃状态,A的缓存访问无需额外激活时间,从而延迟降低。
  • 乱序执行窗口的充分利用:超线程模式下,核心的乱序执行单元会同时处理两个线程的指令。程序B的空循环仅占用整数执行端口,不会与A的内存访问指令竞争资源;反而因为乱序窗口被填满,CPU的指令调度逻辑能更高效地安排A的load指令,减少等待周期。
  • 缓存调度策略的动态调整:部分现代CPU的缓存控制器会根据核心的负载情况动态调整调度策略。当只有A运行时,负载较低,调度策略偏保守;当B加入后,核心负载升高,缓存控制器切换到更激进的调度模式,提升了A的缓存访问效率。
  • TSCP计数的潜在干扰?:不过rdtscp的计数是基于CPU的固定时间戳计数器(TSC),不受睿频或负载影响,这个可能性较低,但可以通过多次重复测试排除误差。

内容的提问来源于stack exchange,提问作者Gerrie

火山引擎 最新活动