同时执行多个线程周期的并发编程?



我想知道是否有人知道一种并发(即多线程、并行等)编程语言,它的设置使各个线程不会因为操作系统无法为它们提供CPU时间而相互落后。我甚至不确定组装是否可以避免这种情况。:P但我显然不确定,因此提出了这个问题。

我并不是说程序需要实时访问CPU周期,我是说线程不应该失去同步。此外,如果该语言被编译成二进制可执行文件而不是字节码,或者只是由解释器运行,那就太好了。

我确实相信没有这样的事情。

原因是多个线程只有在不同的内核上执行,才能真正在并行环境中执行。事实上,在多核处理器出现之前,从技术上讲,不可能同时运行(计算)不同的线程。

现代操作系统使用大量的进程,因此也使用大量的线程(至少在逐个进程上是这样,线程是进程的"工作"部分)。尽管有多核处理器,但在所有常见的使用中,您的系统上仍然有比可用核更多的活动线程。

在我写这些行的时候,我有357个线程活动,"只有"8个可用的内核。

这就是调度器的用途。它们在不同的线程之间共享可用的计算时间,以避免饥饿并产生同时执行的错觉。

为了确保不同的线程同时运行,并且不会不时地被放置在表单上,你应该修改操作系统的Scheduler,如果可能的话,这至少是个坏主意。

解释器的使用没有帮助,因为它运行多线程应用程序的唯一方法是创建具有相同问题的解释线程

为了确保不同线程同步,您应该使用barriere或信号量,因为您永远无法修改用户计算机的操作系统调度程序


注意:在HPC应用程序中,研究人员试图避免在上下文切换(保存线程运行环境以稍后恢复线程的操作)中浪费时间。因此,他们根据可用的内核分配线程(通常他们只为操作系统和I/O留下一个内核),并将其他线程固定到特定的内核。这有助于他们确保计算尽可能高效。

然而,这并不能保证同步,而且可能仍然需要使用特定的mecanisme类屏障。

使用现代处理器,很难确保任何特定的计算以绝对已知的速率进行。

例如:一个缓存未命中的线程可能需要比缓存命中的线程多几百个周期。所以现在速度取决于缓存中的内容。缓存中的内容取决于线程过去执行的复杂控制和数据流。有很多不受控制的延迟来源(管道中断、取决于操作数的可变长度指令执行、OOO CPU中的内部资源争用、通过各种内存层次结构和跨总线到其他CPU的内存延迟、消息发送/接收时间…)

因此,要使线程以完全相同的速度进行,需要大量的控制或先见之明,而这是你几乎无法获得的。(超级计算的家伙们几乎关闭了操作系统,以最大限度地减少背景噪音,例如,随机发生的中断。即使这样也无济于事)。

一个更好的方法是让线程发出信号,表明他们所做的工作可用于另一个需要它的线程。如果信令/等待很少发生,那么它就会被线程的计算工作淹没,处理器也会得到有效使用。

实现上述目标仍然很困难。如果你在所有CPU上进行相同的计算[例如,大数据并行计算],并且非常有规律,那么总体速率可能非常相似;最后,您仍然需要一些(屏障)同步,以确保它们都回到锁定状态。

很难将每个CPU上的所有计算组织为"相同"。更有可能的是,你有很多不规则(大小不等)的计算"颗粒"。如果你能跟踪这些,那么你就可以将它们分布在多个线程/CPU中,依靠单个晶粒之间的显式同步来使它们整体正常工作,并将新晶粒交给突然空闲的CPU,使它们保持忙碌。这可以通过"工作窃取"的概念很好地实现:每个CPU都有一个可以运行的未执行粒度池,并以最快的速度处理它们。谷物可能会制造更多的谷物(如果你用完了,你就完成了计算!)。如果一个CPU的内存池变空,它就会从内存池中窃取其他CPU的工作。窃取工作的调度器很难构建;它们必须是正确的,并避免CPU不断相互干扰,因为这只是浪费周期。

我们的PARLANSE并行编程语言正是为了处理这样的问题而设计的。我们在代表程序的非常大的图形上运行它;它倾向于为图中半径适中的每个补丁生成一些工作。在这样一个图中有一百万个节点,我们有很多工作要做

最新更新