为什么英特尔的下一代处理器取消了超线程?
为什么英特尔的下一代处理器取消了超线程?
这事嘛……其实我之前也写过不少相关文章了。
Intel取消超线程,并不是因为超线程不好,而是因为今天P-core已经足够强,SMT带来的收益越来越小;与此同时,它还要付出额外面积、功耗以及安全风险。因此Intel更愿意把这些晶体管拿去增加更多E-core。
简单说,“超线程”并不是简单的三个字;它的本质还是有点复杂的。
你可以这么理解:每个CPU核心内部并不仅仅只有一个ALU以及配套的一组寄存器——那是2000年代之前的老黄历了,甚至2000年之前很多CPU也已经不是486那傻样了。这个就不展开说了——它的内部其实会有一组整数单元、一组浮点单元以及配套的寄存器/译码器等等玩意儿。
注意我说的是“一组”,不是一个。
这意味着什么呢?
意味着,这个CPU核心不会一条一条的执行一个程序中每条指令,那样这一整组玩意儿就得经常处于空闲状态、得不到充分利用。
那怎么才能充分利用呢?
指令多发射、乱序执行、超级流水线以及SIMD,诸如此类。
所有这些技术,说白了都是为了尽可能充分的利用核心内部的大量计算资源——这东西和一条指令的执行时间或者什么指令吞吐量无关……也不能说无关,一个片面的指标而已。
我们真正关注的,是CPU的内部资源是否每时每刻都被充分利用了;充分利用的时间占比越大,这个CPU的效能就发挥的越充分。
那么,超线程是怎么来的呢?
简单说,2000年,AMD抢先搞出了1GHZ的CPU;但那时候的Intel呢,它“不务正业”的和Rambus合作,试图一举绞杀所有x86兼容CPU厂商……结果自然就在市场上遭受重创,被AMD干的非常惨。之后仓促超频奔腾3又搞出大丑闻、再仓促推出的奔腾4又押注超高频+超长流水线,结果同频性能反而严重下滑……
被逼急了,Intel就宣布,说自己有“超线程”,可以一颗核心当两颗用!
如此一来,Intel就可以在“数框框”大战中占据上风;AMD自然不乐意了:“我们也早就有这个技术,只是我们没这么叫而已!”[1]
于是AMD就……推出了真正物理双核的CPU,哈哈。
不过,虽然也是显示很多核,但AMD的CPU是“里面有多个整数核和共享的浮点核”,和Intel的超线程不是一回事。直到2017年的zen才用了和Intel类似的SMT技术。
那么,超线程究竟是什么玩意儿?
在讲这个之前,我们得先搞明白一个概念:数据相关。
比如,我们考虑这样一段程序:
A=A+B;
x=A+B;这条语句先计算了A+B,并把它们的和存入A;之后,新的A值和B相加,和存入x。
显然,x=A+B这条指令一定得在A=A+B执行完成之后才能执行,不然它就得不到最新的A值。那么在指令流水线里,x=A+B的执行就不能紧跟着A=A+B执行,而是必须等上几个时钟周期,等到A=A+B执行完才能继续执行。
那么,指令多发射的意思是,取指、译码、执行、写回这些步骤本身就可以并行,不再是486时代那样,只有一个取指令、译码、执行、访存单元,而是多个单元可以同时处理多条指令;乱序执行的意思就是,CPU有能力不按照顺序同时执行多条指令,识别和区分“数据相关”以及“数据不相关”的指令,在确保结果正确的前提下尽量把不存在数据相关的指令“提前”拿进来执行了,以填补数据相关指令造成的空白……
很容易想到:既然“数据相关”这么麻烦……那如果送给CPU的指令本来就来自两个线程,它们本就不存在数据相关,是不是CPU利用率就能更高?
(严格的说,不同线程之间也可能存在数据相关,比如两个线程来自同一个进程、操作同一块数据时;或者,虽然来自不同进程但操作的是同一块共享内存……)
那,怎么让CPU得到的指令来自两个线程呢?
没错。“误导”操作系统,告诉它我这不是一颗内核,我是两颗!你尽管把两个线程同时丢给我执行,我搞得定!
你看,识别数据相关、内存地址翻译等等,这些都是现成的;你丢指令过来,我在内部先完成地址转换,然后一视同仁的执行……这需要什么新鲜技术吗?不就是改了个名字、“骗”操作系统同时把两个指令序列的内容同时丢过来吗?
(这里只是打个比方,实际上操作系统知道这是超线程。)
当然,实际上也没这么简单。因为超线程毕竟不是真的双核,操作系统还是得知道点别的东西的——比如,单个核心毕竟资源有限,不能真当两个核心用,能绑不同物理核心还是尽量分到不同物理核心更好:这就是超线程的操作系统支持。
其实这里还有一些细节,比如现代处理器的L1通常是每核私有的,但会由同一物理核心上的两个SMT线程共享;L2可能是每核私有,也可能由一个核心簇共享;末级缓存的共享范围同样取决于具体的芯片、Tile和核心簇设计。多路CPU服务器通常采用NUMA共享内存架构:各CPU可以访问整个系统的内存地址空间,但访问本地内存通常比访问另一CPU所连接的远端内存延迟更低、带宽更高。
换句话说,操作系统并不是被愚弄的傻子,别把这个技术想的太有感情色彩——这里其实是需要做非常非常精细的调控的,不然性能立即爆掉。
与之同时,CPU内部也得有相应的准备:
每个线程要有:
- 独立寄存器状态(Architectural Registers)
- 独立RIP
- 独立RFLAGS
- 独立CR3
- 独立TLB上下文(很多情况下)
- 独立中断状态
- 独立异常状态
除此之外,前端还要:
- 双线程取指
- 双线程分支预测状态
- 双线程Rename Map
- 双线程ROB管理
- 双线程资源仲裁
真正共享的是:
- ALU
- FPU
- Cache
- Load/Store Unit
- Execution Port
总之,超线程的确能较大幅度的提高核心内部资源的利用率——对当年那些常见应用来说。但,它对核心内部资源的利用是随机的,不可控的:可能你会说,啊,我知道你这个核心里面有多少东西,所以我这一坨函数里面的、被我巧妙安排的指令恰好把它利用完全了,完美!
然而,真实环境里,你这坨函数丢给CPU的同时,它上面另外跑了个视频解码线程;你那巧妙的、充分利用了核心所有资源的优化就此被撕的支离破碎……
当然,这是个理想中的极端情况;现实中极少有这样的事——如果真的有如此极限的需求,那这台电脑就应该专机专用。
但这个思路是正确的:较佳的优化后的代码,或者,AVX之类指令集本身就能极为充分的利用片内资源;此时不可控的灌进来的另一条线程就会造成极大的干扰(同时也被那种“大块头指令”干扰)。
换句话说,理论上说,超线程下限高上限受限;而去掉超线程后,性能下限变低但上限或许可以提高。
总之,这里存在一个取舍:Intel的P-core本身已经足够完善,很容易就能吃满核内资源;前面提到的超级流水线、指令多发射、乱序执行等等都是为了提高核内资料利用率:如果这些技术已经足够好,何必搞超线程呢?
支持超线程的那块面积完全可以做成E-core。这样相对功耗可以更低。
当然,超线程仍然对整体性能是有提升的;只是单核性能-功耗-芯片面积彼此制约,你不可能全都要。
另一个问题则是安全:OS调度不同进程时,会有明确的执行权切换动作,这个动作包括前一个进/线程的执行现场保护和下一个进/线程的执行线程恢复;但一股脑的灌进物理上是一颗却“骗”OS说自己是两颗的核心时,这两个线程之间显然不可能再有明确区分彼此的“执行权切换”,两者要长时间的、彼此交错的运行,一切都要靠核心内部自动识别和区分两者。这显然会扩大被攻击面。
这些,可能就是Intel抛弃超线程的原因。