Azul Systems拥有一个支持数千个缓存一致CPU的设备。我希望能够深入了解操作系统需要进行哪些更改才能安排数千个同时运行的线程。
答案 0 :(得分:15)
调度数千个线程并不是什么大问题,但是在数百个CPU上安排它们是。您首先需要的是非常细粒度的锁定,或者更好的是无锁数据结构和算法。当一个CPU执行关键部分时,你只能让200个CPU等待。
答案 1 :(得分:6)
使Linux规模化是一个漫长而持续的项目。第一个支持多处理器的Linux内核有一个锁保护整个内核(大内核锁,BKL),这很简单,但可扩展性有限。
随后锁定已经变得更细粒度,即有许多锁(数千?),每个锁仅覆盖一小部分数据。但是,由于细粒度锁定往往很复杂,并且锁定开销开始消耗性能优势,因此可以采取多大程度的限制,特别是考虑到大多数多CPU Linux系统具有相对较少的CPU。 / p>
另一件事是,内核尽可能使用per-cpu数据结构。这非常重要,因为它避免了共享数据的缓存一致性性能问题,当然也没有锁定开销。例如。每个CPU都运行自己的进程调度程序,只需要偶尔进行全局同步。
此外,选择一些具有可扩展性的算法。例如。一些读取主要数据受读取 - 更新(RCU)保护,而不是传统的互斥锁;这允许读者在并发更新期间继续。
至于内存,Linux会努力从与运行进程相同的NUMA节点分配内存。这为应用程序提供了更好的内存带宽和延迟。
答案 2 :(得分:6)
您要求对操作系统进行可能的更改,因此我认为这项工作背后有一个重要的工程团队。
还有一些澄清信息可以帮助定义问题参数:
您需要多少IPC(进程间通信)? 他们真的必须是线程,还是他们可以成为流程? 如果它们是进程,是否可以通过套接字相互通信,而不是使用共享内存? 什么是内存架构?你是带有1024个内核的直接SMP,还是还有其他一些NUMA(非统一内存架构)或MMP?你的页面表是什么样的?
只知道关于Azul系统的最小信息,我猜你的IPC很少,而且一个简单的“每个核心运行一个内核”模型实际上可能会很好。如果进程需要相互通信,那么他们可以创建套接字并以这种方式传输数据。您的硬件是否支持此型号? (你可能最终每个核心也需要一个IP地址,而在1024个IP地址中,这可能会很麻烦,虽然它们都可能是NAT,但也许并不是什么大问题)。如果当然,这种模式会导致一些低效率,例如额外的页面表,以及相当多的RAM开销,甚至可能不被您的硬件系统支持。
即使“每个内核1个内核”不起作用,你也可以运行1024/8个内核,并且可以正常运行,让每个内核控制8个物理CPU。
也就是说,如果你想在一个拥有1024个核心(只有几个物理CPU)的传统SMP机器中每个核心运行1个线程,那么我希望老式的O(1)调度程序是你想要的。你的CPU [0]可能会在内核中占据近100%并进行中断处理,但这对于这个用例来说还不错,除非你需要超过1个内核来处理你的工作负载。
答案 3 :(得分:5)
我没有受过教育的猜测是,当处理器处于空闲状态时,每个处理器都有一个运行队列和一个工作窃取算法。我可以看到这在M:N模型中工作,其中每个CPU有一个进程,轻量级进程作为工作项。这会感觉类似于工作窃取线程池,例如Java-7的fork-join库中的线程池。
如果您真的想知道,请选择Solaris Internals或深入了解Solaris内核代码。我还在阅读Design&在FreeBSD的Impl中,Solaris Internals是我列表中的下一个,所以我所能做的就是做出疯狂的猜测。
答案 4 :(得分:1)
我很确定我们工作的SGI Altix(ccNUMA)使用特殊硬件来实现缓存一致性。
连接每个核心相关的4mb缓存有巨大的开销。它不太可能仅在软件中发生。
在256 cpus的数组中,你需要768mb ram来保存缓存失效位。 每个缓存行12mb缓存/ 128字节*256²核心。
答案 5 :(得分:1)
修改操作系统是一回事,但使用未更改的应用程序代码会浪费硬件。当超过某个限制(取决于硬件)时,为了执行通用代码而保持一致性和同步的努力实在太多了。你可以做到,但它会非常昂贵。 从操作系统方面来说,你需要复杂的亲和力模型,即不要因为你的忙而跳过CPU。基于硬件拓扑调度线程 - CPU上的协作线程“接近”以最小化惩罚。简单的工作窃取不是一个好的解决方案,您必须考虑拓扑。一种解决方案是分层工作窃取 - 通过距离窃取工作,将拓扑划分为扇区并尝试从最近的第一个窃取。 触摸一下锁定问题;你仍然会使用自旋锁这样,但使用完全不同的实现。这可能是CS最近获得专利的领域。 但是,再次,你需要专门针对如此大规模的规划。或者你只是使用不足。没有自动“并行化器”会为你做这件事。
答案 6 :(得分:1)
最简单的方法是将每个进程/线程绑定到几个CPUS,然后只有那些CPU必须在该线程上竞争锁定。显然,需要有一些方法来移动线程来均衡负载,但在NUMA架构上,你必须尽可能地减少这种情况。
答案 7 :(得分:0)
即使在双核英特尔系统上,我也非常确定Linux已经能够处理具有原生posix线程的“数千个”线程。
(但是,Glibc和内核都需要配置为支持这一点,但我相信现在大多数系统现在都默认配置了。)