Memory 共享内存与消息传递如何处理大型数据结构?

Memory 共享内存与消息传递如何处理大型数据结构?,memory,concurrency,erlang,parallel-processing,go,Memory,Concurrency,Erlang,Parallel Processing,Go,在研究Go和Erlang的并发方法时,我注意到它们都依赖于消息传递 这种方法明显减少了对复杂锁的需要,因为没有共享状态 但是,考虑许多客户端希望并行只读访问内存中的单个大数据结构——比如后缀数组。 我的问题是: 使用共享状态是否会比消息传递更快,占用更少的内存,因为数据是只读的,并且只需要存在于单个位置,所以锁基本上是不必要的 在消息传递上下文中如何处理此问题?是否只有一个进程可以访问数据结构,而客户端只需要从中顺序请求数据?或者,如果可能的话,数据是否会被分块以创建几个包含分块的进程 考虑到

在研究Go和Erlang的并发方法时,我注意到它们都依赖于消息传递

这种方法明显减少了对复杂锁的需要,因为没有共享状态

但是,考虑许多客户端希望并行只读访问内存中的单个大数据结构——比如后缀数组。

我的问题是:

  • 使用共享状态是否会比消息传递更快,占用更少的内存,因为数据是只读的,并且只需要存在于单个位置,所以锁基本上是不必要的

  • 在消息传递上下文中如何处理此问题?是否只有一个进程可以访问数据结构,而客户端只需要从中顺序请求数据?或者,如果可能的话,数据是否会被分块以创建几个包含分块的进程

  • 考虑到现代CPU和内存的体系结构,这两种解决方案之间是否存在很大差异?即,共享内存是否可以由多个内核并行读取?这意味着不存在硬件瓶颈,否则这两种实现的性能大致相同


      • 是的,在这种情况下,共享状态可能更快。但前提是您可以放弃锁,而这只有在绝对只读的情况下才可行。如果它“大部分是只读的”,那么您需要一个锁(除非您成功地编写了无锁结构,请注意它们甚至比锁更复杂),然后您将很难让它像一个好的消息传递架构一样快速地执行

      • 是的,您可以编写一个“服务器进程”来共享它。对于真正轻量级的流程,它并不比编写一个小API来访问数据更繁重。像“拥有”数据的对象一样思考(在面向对象的意义上)。将数据分块以增强并行性(在DB循环中称为“分片”)在大型情况下(或者如果数据存储速度较慢)会有所帮助

      • 即使NUMA正在成为主流,每个NUMA单元仍有越来越多的内核。一个很大的区别是消息只能在两个核心之间传递,而锁必须从所有核心上的缓存中刷新,从而将其限制在单元间总线延迟(甚至比RAM访问还要慢)。如果有的话,共享状态/锁越来越不可行

      简言之。。。。习惯于消息传递和服务器进程,这是非常流行的

      编辑:重温这个答案,我想补充一下Go文档中的一个短语:

      通过交流来分享记忆,不要通过分享记忆来交流

      其思想是:当线程之间共享一块内存时,避免并发访问的典型方法是使用锁进行仲裁。Go样式是传递带有引用的消息,线程仅在接收消息时访问内存。它依赖于某种程度的程序员纪律;但结果是非常干净的代码,可以很容易地校对,所以调试相对容易


      其优点是,您不必在每条消息上复制大块数据,也不必像某些锁实现那样有效地刷新缓存。现在说这种风格是否能带来更高性能的设计还为时过早。(特别是因为当前的Go运行时在线程调度方面有点幼稚)

      大多数现代处理器使用的是。由于共享状态,在不同线程之间传递只读数据非常便宜。但修改后的共享数据非常昂贵,因为存储此缓存线的所有其他缓存都必须使其无效

      因此,如果您有只读数据,那么在线程之间共享数据而不是复制消息是非常便宜的。如果您主要读取数据,那么在线程之间共享可能会很昂贵,部分原因是需要同步访问,部分原因是写入会破坏共享数据的缓存友好行为


      在这里可能是有益的。您不需要更改实际的数据结构,只需创建一个新的数据结构,该数据结构共享大部分旧数据,但需要更改的内容已更改。共享单一版本的it很便宜,因为所有数据都是不可变的,但您仍然可以高效地更新到新版本。

      需要意识到的一点是,Erlang并发模型并没有真正规定消息中的数据必须在进程之间复制,它声明发送消息是唯一的通信方式,并且没有共享状态。由于所有数据都是不可变的,的基础,因此实现很可能不会复制数据,而只是发送对数据的引用。或者可以使用这两种方法的组合。和往常一样,没有最好的解决方案,在选择如何做时需要权衡


      BEAM使用复制,除了发送引用的大型二进制文件。

      这里没有介绍的一种解决方案是主从复制。如果您有一个大的数据结构,您可以将对它的更改复制到对其副本执行更新的所有从属服务器

      如果想要扩展到几台甚至不可能在没有非常人工设置的情况下共享内存的机器(从远程计算机内存读/写的块设备的mmap?),这一点尤其有趣


      它的一个变体是有一个事务管理器,可以很好地请求它更新复制的数据结构,并且它将确保它同时服务于一个且仅服务于一个更新请求。这更多的是mnesia模型,用于mnesia表数据的主-主复制,符合“大数据结构”的要求。

      在Erlang中,所有值都是不可变的-因此在进程之间发送消息时不需要复制消息,因为消息无论如何都无法修改

      在围棋中,消息传递是按惯例进行的-没有什么