【问题标题】:Multithreading vs Shared Memory多线程与共享内存
【发布时间】:2020-02-23 18:03:12
【问题描述】:

我有一个问题,它本质上是在一个庞大但在内存数据库(10 Gb)中的多个项目副本(针)的一系列搜索 - 大海捞针。

这分为任务,每个任务是在大海捞针中找到一系列针中的每一个 每个任务在逻辑上独立于其他任务。

(这已经分布在多台机器上,其中每台机器 有自己的大海捞针。)

有很多方法可以在单个机器上并行化。

我们可以让每个 CPU 内核共享内存有一个搜索进程。 或者我们可以有一个具有多个线程的搜索进程(每个内核一个)。甚至是几个多线程进程。

3 种可能的架构:

  1. 一个进程将干草堆加载到 Posix 共享内存中。

    后续进程使用共享内存段代替(如缓存)

  2. 一个进程将干草堆加载到内存中,然后分叉。

    由于写时复制语义,每个进程都使用相同的内存。

  3. 一个进程将干草堆加载到内存中并产生多个搜索线程

问题是一种可能更好的方法,为什么?或者更确切地说是什么权衡。

(为了论证,假设性能胜过实现复杂性)。

实现两个或三个并进行测量当然是可能的,但工作量很大。 有什么理由可以肯定会更好吗?

  • 大海捞针中的数据是不可变的。
  • 进程正在 Linux 上运行。所以进程并不比线程贵很多。
  • 干草堆跨越许多 GB,因此 CPU 缓存不太可能提供帮助。
  • 搜索过程本质上是一个二分搜索(实际上是 equal_range 加上一点插值)。
  • 由于任务在逻辑上是独立的,因此线程间通信没有任何好处 比进程间通信便宜(例如https://stackoverflow.com/a/18114475/1569204)。

我想不出线程和共享内存之间有任何明显的性能权衡。有吗?也许代码维护权衡更相关?


背景研究

我能找到的唯一相关 SO 答案是指同步线程的开销 - Linux: Processes and Threads in a Multi-core CPU - 这是正确的,但在这里不太适用。

相关且有趣但不同的问题是:

一个有趣的演示是https://elinux.org/images/1/1c/Ben-Yossef-GoodBadUgly.pdf

这表明线程与进程上下文切换的速度可能存在细微差别。 我假设除了监视线程/进程之外,其他线程/进程永远不会被关闭。

【问题讨论】:

  • 您建议使用什么语言和应用服务器?
  • 这是一个独立于语言的问题,但我碰巧使用的是 C++。

标签: multithreading parallel-processing shared-memory


【解决方案1】:

现代方法是使用线程和单个进程。

这是否比使用多个进程和共享内存段更好可能在某种程度上取决于您的个人偏好以及在您使用的语言中使用线程的难易程度,但我会说,如果有体面的线程支持可用(例如Java)你几乎总是最好使用它。

据我所知,使用多个进程的主要优点是不可能遇到管理多个线程时可能遇到的问题(例如,忘记同步对共享可写资源的访问 - 除了共享内存池)。然而,完全没有线程的线程安全并不是一个赞成的论点。

添加进程也可能比添加线程更容易一些。您必须编写一些代码来更改在线处理线程的数量(或使用框架或应用程序服务器)。

但总的来说,多进程方法已死。几十年来我没有使用过共享内存。线程赢得了胜利,值得投资学习使用它们。

如果您确实需要对公共可写内存进行多线程访问,那么像 Java 这样的语言会为您提供各种类型的类(以及语言原语)。在某些时候你会发现你想要那个,然后使用多进程方法,你会面临使用信号量进行同步和编写自己的类,或者可能正在寻找第三方库,但到那时 Java 人将遥遥领先.

您还提到了分叉和依赖写时复制。这似乎是一个非常脆弱的解决方案,取决于系统的特定行为,我自己不会使用它。

【讨论】:

  • 我不认为多进程方法已经死了。例如,它可以跨多台线程无法扩展的机器(除非你有一个非常聪明的运行时)。在我的情况下,交叉机器已经到位。我对单个节点的性能变化感兴趣。维护成本也很重要,但次要。
  • 是的,您需要在不同的机器上使用不同的进程。但是你不能使用共享内存,所以我们正在谈论的不是这种情况。
  • 其实可以的。您可以通过网络共享文件并在每台机器上本地映射它。除非您将其保留为只读,否则不建议这样做。
【解决方案2】:

一般建议:能够衡量改进!没有它,您可以根据互联网上的建议调整所有您喜欢的东西,但仍然无法获得最佳性能。实际上,我是在告诉你不要相信我或任何其他人(包括你自己),而是要衡量。还要准备好在生产系统上实时测量这一点。基准测试可能会在一定程度上对您有所帮助,但真正的负载模式仍然是另一回事。

然后,您说这些操作纯粹是在内存中进行的,因此速度不取决于(网络或存储)IO 性能。您面临的两个瓶颈是 CPU 和 RAM 带宽。因此,为了在正确的部分上工作,找出哪个是限制因素。确保相应的部分高效可确保您的搜索获得最佳性能。

此外,您说您进行二进制搜索。这基本上意味着您进行log(n) 比较,其中每次比较都需要从大海捞针中加载某个元素。此负载可能会通过所有缓存,因为数据的大小使缓存命中的可能性很小。但是,您可以同时持有多根针在缓存中搜索。如果您随后设法先触发针的缓存加载,然后执行比较,则可以减少 CPU 或 RAM 空闲的时间,因为它们等待新操作执行。这显然(像其他人一样)是您需要针对其运行的系统调整的参数。

更进一步,重新考虑二进制搜索。二进制搜索在随机数据上以良好的上限可靠地执行。如果您的数据中有任何模式(即任何非随机的),请尝试利用这些知识。如果您可以粗略估计您正在搜索的针的位置,则可以减少查找次数。这基本上是将工作从 RAM 总线转移到 CPU,所以它再次取决于哪个是实际的瓶颈。请注意,您还可以切换算法,例如当您需要考虑的元素少于一定数量时,从有根据的猜测变为二分搜索。

最后,您说每个节​​点都有您数据库的完整副本。如果 N 个节点中的每一个都被分配了数据库的 N 分之一,那么它可以改进缓存。然后,您将第一步定位元素以确定节点,然后将搜索分派给负责的节点。如果有疑问,每个节点仍然可以将搜索作为后备处理。

【讨论】:

  • 所有合理且相关的建议,但它没有回答“是否有任何理由在线程架构与共享内存架构之间进行选择”的问题。也许这是一个红鲱鱼,因为问题是我们是否受到 CPU 或内存带宽的限制。 stackoverflow.com/questions/2952277/… 提示如何确定。
猜你喜欢
  • 2010-12-09
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2023-04-03
  • 2014-04-19
  • 2014-10-08
相关资源
最近更新 更多