让我们将任务切换的成本分为“直接成本”(任务切换代码本身的成本)和“间接成本”(TLB缺失等的成本)。
直接成本
对于直接成本,这主要是保存前一个任务的(面向用户空间的可见架构)状态的成本,然后加载下一个任务的状态。这取决于具体情况,主要是因为它可能包括FPU / MMX / SSE / AVX状态,这些状态可以增加几KB的数据(特别是如果涉及AVX-例如,AVX2本身就是512字节,而AVX-512本身就超过了2 KB)。
请注意,有一种“延迟状态加载”机制,以避免加载(某些或全部)FPU / MMX / SSE / AVX状态的成本,并避免保存未加载的状态;出于性能原因(如果几乎所有任务都使用该状态,则“正在使用该状态需要加载”陷阱/异常的成本超过了尝试在任务切换期间避免执行该操作所节省的成本)或出于安全原因(例如,因为Linux中的代码执行“保存使用的内容”,而不是“保存然后清除使用的内容”,并将属于一个任务的数据留在可以通过漏洞利用攻击获得的寄存器中)。
还有一些其他的成本(更新统计信息 - 如“前一个任务使用的CPU时间量”),确定新任务是否与旧任务使用相同的虚拟地址空间(例如同一进程中的不同线程)等。
间接成本
间接成本实际上是 CPU 所具有的所有“类似缓存”的东西损失的效率,包括缓存本身、TLB、更高级别的分页结构缓存、所有分支预测相关的东西(分支方向、分支目标、返回缓冲区)等等。
间接成本可以分为三种原因。其中一种是由于任务切换完全刷新而发生的间接成本。过去,这主要限于因任务切换期间TLBs被刷新而导致的TLB未命中。请注意,即使使用PCID,也可能会发生这种情况 - ID的数量有4096个限制(当使用“熔断缓解”时,ID成对使用 - 对于每个虚拟地址空间,一个用于用户空间,另一个用于内核),这意味着当使用超过4096(或2048)个虚拟地址空间时,内核必须回收先前使用的ID并刷新所有TLB以便重新使用ID。然而,现在(由于所有的漏洞问题),内核可能会刷新其他内容(例如分支预测内容),以便信息无法从一个任务泄漏到另一个任务,但我真的不知道Linux是否支持这种“类似缓存的”内容(我怀疑他们主要试图防止数据从内核泄漏到用户空间,并出于偶然而防止数据从一个任务泄漏到另一个任务)。
间接成本的另一个原因是容量限制。例如,如果L2缓存只能缓存最多256 KiB的数据,并且先前的任务使用了超过256 KiB的数据; 那么L2缓存将充满对下一个任务无用的数据,并且所有下一个任务想要缓存的数据(之前已经缓存)都将因“最近最少使用”而被逐出。这适用于所有“类似缓存”的东西(包括TLB和更高级别的分页结构缓存,即使使用了PCID功能也是如此)。
间接成本的另一个原因是将任务迁移到不同的CPU。这取决于哪些CPU - 例如,如果将任务迁移到同一核心内的不同逻辑CPU,则许多“类似缓存”的东西可能会由两个CPU共享,迁移成本可能相对较小; 如果将任务迁移到物理套件中的CPU,则两个CPU之间可能没有任何“类似缓存”的东西可以共享,迁移成本可能相对较大。
请注意,间接成本的数量上限取决于任务所执行的操作。例如,如果任务使用大量数据,则间接成本可能相对昂贵(很多缓存和TLB未命中),而如果任务使用微小的数据,则间接成本可能可以忽略不计(很少的缓存和TLB未命中)。
无关
请注意,PCID特性有自己的成本(不仅限于任务切换本身)。具体而言,当一个CPU上的页面翻译被修改时,可能需要使用称为“多CPU TLB清除”的东西在其他CPU上使之失效,这相对昂贵(涉及到IPI /互处理器中断,会干扰其他CPU,每个CPU的成本低至“数百个周期”)。没有PCID,您可以避免其中一些。例如,对于正在单个CPU上运行的单线程进程,如果没有PCID,则知道没有其他CPU可以使用相同的虚拟地址空间,因此知道不需要进行“多CPU TLB清除”,如果多线程进程限制在单个NUMA域中,则只需涉及该NUMA域内的CPU即可进行“多CPU TLB清除”。当使用PCID时,您无法依赖这些技巧,并且存在更高的开销,因为“多CPU TLB清除”并不经常避免。
当然,还有与ID管理相关的一些成本(例如,找出哪个ID可以分配给新创建的任务,当任务终止时撤销ID,当虚拟地址空间比ID更多时重新分配ID的某种“最近未使用”系统等)。
由于这些成本,必然存在病态情况,其中使用PCID的成本超过了由任务切换引起的“较少的TLB未命中”效益(使用PCID会使性能变差)。