hus*_*sik 6 x86 assembly intel message-passing cpu-architecture
经过认真的发展,CPU 获得了许多核心,在多个小芯片、numa 系统等上获得了分布式核心块,但数据仍然不仅必须通过 L1 缓存(如果在同一核心 SMT 上),而且还必须通过一些原子/互斥同步未经硬件加速的原始过程。
我想知道为什么英特尔或 IBM 没有想出这样的东西:
movcor 1 MX 5 <---- sends 5 to Messaging register of core 1
pipe 1 1 1 <---- pushes data=1 to pipe=1 of core=1 and core1 needs to pop it
bcast 1 <--- broadcasts 1 to all cores' pipe-0
Run Code Online (Sandbox Code Playgroud)
使其比其他方法快得多?GPU 支持块级快速同步点,例如barrier()或__syncthreads()。GPU 还支持本地阵列的并行原子更新加速。
当 CPU 增加 256 个核心时,此功能是否不会为在核心到核心带宽(和/或延迟)上遇到瓶颈的各种算法提供严重的扩展?
CPU 的编程模型与 GPU截然不同,可以运行多个单独的线程(可能是不同的进程),因此您还需要软件和操作系统基础设施来让线程知道其他线程正在哪个其他核心(如果有)上运行。或者他们必须将每个线程固定到特定的核心。但即便如此,也需要某种方法来虚拟化架构消息传递寄存器,就像上下文切换虚拟化标准寄存器以在每个内核上进行多任务一样。
因此,在普通操作系统下使用这样的东西之前还存在一个额外的障碍,在普通操作系统中,单个进程无法完全拥有物理核心。操作系统仍然可能将其他进程的其他线程调度到核心上,并运行中断处理程序,这与 GPU 不同,GPU 中的核心没有其他事情可做,并且全部构建为在大规模并行问题上协同工作。
英特尔确实在 Sapphire Rapids 中引入了用户空间中断。 包括用户 IPI(处理器间中断),但这不涉及必须在上下文切换时保存/恢复的接收队列。操作系统仍然需要管理一些东西(比如用户中断目标表),但我认为对于上下文切换来说这不是问题。它解决的问题与您建议的问题不同,因为它是中断而不是消息队列。
通知另一个线程何时在共享内存中查找数据是需要解决的问题的困难部分,比在内核之间获取数据更困难。共享内存仍然可以做到这一点(特别是使用新指令,例如cldemote让写入器请求将最近存储的缓存行写回共享 L3,以便其他内核可以更有效地读取它)。请参阅下面有关 UIPI 的部分。
无论如何,想要这样的任务通常最好在 GPU 上完成,而不是几个试图进行推测执行的单独的深度流水线 OoO exec CPU 核心。与 GPU 不同,GPU 是简单的有序管道。
您实际上无法将结果推送到另一个核心,直到它在执行它的核心上退出。因为如果您发现错误推测(例如导致此问题的执行路径中较早的分支错误预测),您不希望也回滚其他核心。可以想象,这仍然可以实现比在共享内存的核心之间弹跳缓存线更低的延迟,但可以使用它的应用程序类别相当狭窄。
然而,高性能计算是现代 CPU 的一个已知用例,因此,如果它真的能改变游戏规则,那么也许值得考虑将其作为一种设计选择。
考虑到现有 CPU 的架构,有些事情并不容易或不可能有效地完成。小工作单元与线程之间的细粒度合作是一个问题。如果您的想法切实可行,可能会有所帮助,但也存在重大挑战。
对于操作系统的使用,有IPI机制。但这会触发中断,因此当接收端的无序执行管道到达它时,它不会排列要读取的数据,因此对于不同的用例,它与您建议的机制非常不同。
除了避免对方轮询之外,它的性能相当低。为了能够将内核从省电睡眠状态唤醒,如果现在有更多线程准备好运行,那么它应该被唤醒,可以调用schedule()来确定要运行哪个线程。
任何内核都可以将 IPI 发送到任何其他内核(如果它在内核模式下运行)。
Sapphire Rapids 中的新功能是对操作系统的硬件支持,让用户空间进程完全在用户空间处理一些中断。
https://lwn.net/Articles/869140/是一篇 LKML 帖子,解释了它以及 Linux 如何支持它。显然,它比“eventfd”快 10 倍左右,可以在两个用户空间线程之间来回发送一条小消息一百万次。或者比使用 POSIX 信号处理程序执行相同操作快 15 倍。
内核管理的架构数据结构
UPID:用户发布的中断描述符 - 保存接收器中断向量信息和通知状态(如正在进行的通知、抑制的通知)。UITT:用户中断目标表 - 存储发送方中断路由的 UPID 指针和向量信息。由senuipi指令引用。每个任务的中断状态通过 MSR 引用,MSR 在上下文切换期间由内核保存和恢复。
指示
senduipi <index>- 根据 UITT 索引将用户 IPI 发送到目标任务。clui- 通过清除UIF(用户中断标志)来屏蔽用户中断。stui- 通过设置 UIF 取消屏蔽用户中断。testui- 测试UIF的当前值。uiret- 从用户中断处理程序返回。
所以它确实有在上下文切换时保存/恢复的新状态。不过,我怀疑它比你想象的队列要小。关键的是,对于未运行的线程,不需要在任何地方有一个接收队列,因为状态涉及内存中的表,并且没有数据,我猜只是一个待处理或未处理的标志。因此,在接收方未运行的情况下,它只需在发送方需要能够看到的表中设置一个位,以便硬件知道将 UIPI 定向到何处。与需要找到内核保存的寄存器状态或其他空间并附加到可变大小(?)缓冲区以实现您的想法不同。
如果接收器正在运行(CPL=3),则直接传递用户中断,无需内核转换。如果接收器没有运行,则当接收器上下文切换回来时会传递中断。如果接收器在内核中被阻塞,则用户中断将传递到内核,然后内核解除对预期接收器的阻塞以传递中断。
因此数据仍然必须通过内存,这只是告诉另一个线程何时查看,这样它就不必轮询/旋转。
我认为 UIPI 对于与您建议的消息队列不同的用例很有用。
因此,当接收线程知道特定数据即将到来时,您通常仍然不会使用它。除了让线程可以处理独立的事情而不是旋转等待或睡眠。
如果线程不特别期望很快就有数据,它也可以使用,这与您的队列想法不同。因此,您可以让它处理低优先级的工作,但一旦更多工作准备就绪,就开始属于关键路径一部分的工作。
它仍然是一个中断,因此仍然需要大量的开销,只是比通过内核处理信号处理程序或类似的开销要少得多。我认为像任何中断一样,它需要耗尽无序的后端。或者也许没有那么糟糕,也许只是将其视为分支错误预测,因为它不必更改特权级别。丢弃 ROB 中的指令会降低中断延迟,但会降低吞吐量,并且只是将前端重新引导到中断处理程序地址。
扩展各种在核心到核心带宽(和/或延迟)上遇到瓶颈的算法?
网状互连(如英特尔自 Skylake Xeon 以来的那样)允许内核之间相当大的聚合带宽。没有一辆共享巴士是他们都必须竞争的。即使是英特尔在 Skylake-Xeon 之前使用的环形总线(并且仍在客户端芯片中使用)也是流水线化的,并且具有相当不错的聚合带宽。
数据可以同时在每对内核之间移动。(我的意思是,128 对内核中的每一个都可以在两个方向上传输数据。通过某些内存级并行性,流水线互连可以拥有每个内核请求的多个传输中的缓存行。)
这涉及共享 L3 缓存,但通常不涉及 DRAM,即使跨套接字也是如此。(或者在 AMD 上,核心集群在 CCX 核心复合体中紧密连接,位于同一芯片内的核心之间)。
另请参阅一些 Anandtech 文章,其中具有良好的内核间延迟基准(缓存线乒乓球)
GPU 还支持本地阵列的并行原子更新加速。
我想我听说过一些 CPU(至少在理论上,也许在实践中)memory_order_relaxed通过将简单的 ALU 放入共享缓存来允许快速原子操作。因此,核心可以向共享 L3 发送原子增量请求,该请求发生在数据上,而不必暂时获得该行的独占所有权。(旧值以只读方式返回,以允许fetch_add或exchange返回值)。
这不容易订购。由发送原子操作请求以由缓存完成的核心完成的其他加载或存储在其他位置。
Anandtech 的Graviton2 评论显示了一张幻灯片,其中提到“动态近与远原子执行”。这可能是这个想法的一种形式!如果内存排序要求足够弱以至于允许它执行该指令,也许允许它远程执行(也许在拥有高速缓存线的核心?)?这只是一个猜测,这是一个单独的问题,我不会在这里进一步深入探讨。
(具有“大型系统扩展”的 ARMv8.1 提供 x86 风格的单指令原子 RMW,以及传统的 LL/SC,在出现虚假故障时需要重试循环,但可以像您一样合成任何原子 RMW 操作CAS 重试循环。)
| 归档时间: |
|
| 查看次数: |
597 次 |
| 最近记录: |