多线程与共享内存

问题描述 投票:0回答:2

我有一个问题,本质上是在大量但在内存数据库(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

这表明线程与进程上下文切换的速度可能有很小的差异。我假设除了监视线程/进程外,其他线程永远不会切换。

multithreading parallel-processing shared-memory
2个回答
2
投票

一般建议:能够衡量改进!否则,您可能会根据互联网上的建议调整自己喜欢的一切,但仍无法获得最佳性能。实际上,我是在告诉您不要信任我或任何其他人(包括您自己),而要去衡量。还准备自己在生产系统上实时进行测量。基准可以在某种程度上帮助您,但是实际的负载模式仍然是另一回事。

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

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

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

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


0
投票

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

是否比使用多个进程和共享内存段更好,这在某种程度上取决于您的个人喜好以及使用您所使用的语言使用线程的难易程度,但是我要说的是,如果有不错的线程支持(例如Java ),使用它几乎总是更好。

据我所知,使用多个进程的主要优点是,在管理多个线程时(例如,忘记同步对共享可写资源的访问-除了共享之外,不可能碰到类似问题)内存池)。但是,通过完全不使用线程来保证线程安全并不太受支持。

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

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

如果您确实需要对公共可写内存进行多线程访问,则Java之类的语言会为您提供各种各样的类(以及语言原语)。在某个时候,您将找到所需的内容,然后通过多进程方法,您将面临使用信号量进行同步以及编写自己的类或寻找第三方库的问题,但是那时Java人将遥遥领先。

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

© www.soinside.com 2019 - 2024. All rights reserved.