24

使用单核处理器,所有线程都从一个 CPU 运行,在内存中的某个互斥体(或信号量等)上使用原子测试和设置操作来实现临界区的想法似乎很简单;因为您的处理器是从程序中的一个位置执行测试和设置,所以它一定不能从您程序中的另一个位置伪装成其他线程来执行测试和设置。

但是,当您确实拥有多个物理处理器时会发生什么?似乎简单的指令级原子性是不够的,b/c 有两个处理器可能同时执行它们的测试和设置操作,你真正需要保持原子性的是访问共享内存位置互斥体。(如果共享内存位置被加载到缓存中,那么整个缓存一致性问题也需要处理......)

这似乎会比单核情况产生更多的开销,所以这里是问题的核心:它有多糟糕?是不是更糟?我们只是忍受它吗?或者通过强制执行一个进程组中的所有线程必须位于同一个物理核心上的策略来回避它?

4

6 回答 6

17

多核/SMP 系统不仅仅是粘在一起的几个 CPU。明确支持并行处理。所有同步原语都是在硬件的帮助下按照原子 CAS实现的。该指令要么锁定 CPU 和内存控制器(以及执行 DMA 的设备)共享的总线并更新内存,要么仅依靠缓存侦听更新内存。这反过来会导致缓存一致性算法开始强制所有相关方刷新他们的缓存。

免责声明 - 这是非常基本的描述,这里有更多有趣的东西,比如虚拟缓存与物理缓存、缓存回写策略、内存模型、围栏等。

如果您想了解更多有关操作系统如何使用这些硬件设施的信息 - 这是一本关于该主题的优秀书籍

于 2009-06-11T18:23:14.913 回答
6

多核 cpu 的供应商在执行保证原子内存访问的指令时必须注意不同的内核相互协调。

例如,在英特尔芯片上,您有“cmpxchg”指令。它将存储在内存位置的值与预期值进行比较,如果两者匹配,则将其交换为新值。如果你在它前面加上'lock'指令,它保证对所有核心都是原子的。

于 2009-06-11T12:31:03.450 回答
3

您将需要一个测试和设置,强制处理器通知所有其他核心操作,以便他们知道。是的,这会带来开销,你必须忍受它。设计多线程应用程序的原因是它们不会过于频繁地等待同步原语。

于 2009-06-11T11:16:35.063 回答
3

或者通过强制执行一个进程组中的所有线程必须位于同一个物理核心上的策略来回避它?

那将取消多线程的全部意义。当您使用锁、信号量或其他同步技术时,无论您使用多少个内核,您都依赖操作系统来确保这些操作是互锁的。

释放锁后切换到不同线程的时间主要取决于上下文切换的成本。SO 线程处理上下文切换开销,因此您可能需要检查一下。

还有一些其他有趣的线程:

您还应该阅读这篇 MSDN 文章:了解 Low-Lock 技术在多线程应用程序中的影响

于 2009-06-11T11:30:41.097 回答
1

内存访问由内存控制器处理,它应该处理多核问题,即它不应该允许同时访问相同的地址(可能由内存页或内存行处理)。因此,您可以使用一个标志来指示另一个处理器是否正在更新某个块的内存内容(这是为了避免一种脏读类型,其中部分记录被更新,但不是全部)。

如果处理器具有此类功能,则更优雅的解决方案是使用硬件信号量块。硬件信号量是一个简单的队列,其大小可能为 no_of_cores -1。这就是 TI 的 6487/8 处理器中的情况。您可以直接查询信号量(并循环直到它被释放)或执行间接查询,一旦您的核心获取资源,这将导致中断。请求按发出的顺序排队和服务。信号量查询是原子操作。

缓存一致性是另一个问题,在某些情况下您可能需要进行缓存写回和刷新。但这是一个非常特定于缓存实现的事情。对于 6487/8,我们需要在一些操作上做到这一点。

于 2009-06-11T15:01:38.827 回答
0

好吧,根据你家中放置的计算机类型,请执行以下操作: 编写一个简单的多线程应用程序。在单核(Pentium 4 或 Core Solo)上运行此应用程序,然后在多核处理器(Core 2 Duo 或类似处理器)上运行它,看看加速有多大。

当然,这些比较是不公平的,因为 Pentium 4 和 Core Solo 无论使用何种内核都比 Core 2 Duo 慢得多。也许可以将 Core 2 Duo 和 Core 2 Quad 与可以使用 4 个或更多线程的应用程序进行比较。

你提出了一些有效的点。多个处理器会带来很多麻烦和开销。但是,我们只能忍受它们,因为如果关键部分足够长,并行性的速度提升可能远远超过它们。

至于您关于将所有线程都放在同一个物理内核上的最终建议,这完全违背了多核计算机的意义!

于 2009-06-11T11:18:23.670 回答