并发性:共享内存与消息传递如何处理大型数据结构? [英] Concurrency: how does shared memory vs message passing handle large data structures?

查看:778
本文介绍了并发性:共享内存与消息传递如何处理大型数据结构?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

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



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



然而,考虑到许多客户端想要并行只读访问内存中的单个大型数据结构的情况,如后缀数组。 p>

我的问题:




  • 使用共享状态会更快,内存比消息传递,因为锁大多数是不必要的,因为数据是只读的,并且只需要存在于单个位置?


  • 问题在消息传递上下文中被接近?是否有一个访问数据结构的进程,客户端只需要顺序请求数据呢?


  • 鉴于现代CPU和架构的架构,如果可能,将数据分块以创建保存块的多个进程。内存,这两个解决方案之间有很大的区别 - 即,共享内存可以并行读取多个核心 - 这意味着没有硬件瓶颈,否则将使两个实现粗略地执行相同?



解决方案


  • 是的,共享状态在这种情况下可能更快。但只有当你可以放弃锁,这只是可行的,如果它是绝对只读。如果它'大多是只读的',那么你需要一个锁(除非你设法编写无锁结构,警告他们甚至比锁更棘手),然后你会很难让它执行


  • 是的,您可以编写一个服务器进程来共享它。对于真正轻量级的进程,它没有比编写一个小的API来访问数据更重要。想象一个拥有数据的对象(在OOP意义上)。


  • 分割数据以增强并行性(在数据库圈中称为分片)有助于大数据(如果数据存储缓慢)即使NUMA正在成为主流,但是每个NUMA单元仍然有越来越多的核心。一个很大的区别是消息可以在两个内核之间传递,而锁必须从所有内核的缓存刷新,限制为单元间总线延迟(甚至比RAM访问速度更慢)。如果有什么,共享状态/锁变得越来越不可行。




编辑:重新访问这个答案,我想添加一个短语在Go的文档:


通过通信共享内存,不通过共享内存进行通信。


这个想法是:当你有一个内存块在线程之间共享,避免并发访问的典型方法是使用锁来仲裁。 Go风格是通过引用传递消息,线程只有在接收到消息时才访问内存。它依赖于程序员纪律的一些措施;但结果是看起来很干净的代码,可以很容易地校对,所以它比较容易调试。



其优点是你不必复制大块每个消息上的数据,并且不必像在某些锁实现上那样有效地刷新缓存。如果风格导致更高的性能设计还是有点早。 (特别是因为当前的Go运行时对线程调度有些天真)


In looking at Go and Erlang's approach to concurrency, I noticed that they both rely on message passing.

This approach obviously alleviates the need for complex locks because there is no shared state.

However, consider the case of many clients wanting parallel read-only access to a single large data structure in memory -- like a suffix array.

My questions:

  • Will using shared state be faster and use less memory than message passing, as locks will mostly be unnecessary because the data is read-only, and only needs to exist in a single location?

  • How would this problem be approached in a message passing context? Would there be a single process with access to the data structure and clients would simply need to sequentially request data from it? Or, if possible, would the data be chunked to create several processes that hold chunks?

  • Given the architecture of modern CPUs & memory, is there much difference between the two solutions -- i.e., can shared memory be read in parallel by multiple cores -- meaning there is no hardware bottleneck that would otherwise make both implementations roughly perform the same?

解决方案

  • Yes, shared state could be faster in this case. But only if you can forgo the locks, and this is only doable if it's absolutely read-only. if it's 'mostly read-only' then you need a lock (unless you manage to write lock-free structures, be warned that they're even trickier than locks), and then you'd be hard-pressed to make it perform as fast as a good message-passing architecture.

  • Yes, you could write a 'server process' to share it. With really lightweight processes, it's no more heavy than writing a small API to access the data. Think like an object (in OOP sense) that 'owns' the data. Splitting the data in chunks to enhance parallelism (called 'sharding' in DB circles) helps in big cases (or if the data is on slow storage).

  • Even if NUMA is getting mainstream, you still have more and more cores per NUMA cell. And a big difference is that a message can be passed between just two cores, while a lock has to be flushed from cache on ALL cores, limiting it to the inter-cell bus latency (even slower than RAM access). If anything, shared-state/locks is getting more and more unfeasible.

in short.... get used to message passing and server processes, it's all the rage.

Edit: revisiting this answer, I want to add about a phrase found on Go's documentation:

share memory by communicating, don't communicate by sharing memory.

the idea is: when you have a block of memory shared between threads, the typical way to avoid concurrent access is to use a lock to arbitrate. The Go style is to pass a message with the reference, a thread only accesses the memory when receiving the message. It relies on some measure of programmer discipline; but results in very clean-looking code that can be easily proofread, so it's relatively easy to debug.

the advantage is that you don't have to copy big blocks of data on every message, and don't have to effectively flush down caches as on some lock implementations. It's still somewhat early to say if the style leads to higher performance designs or not. (specially since current Go runtime is somewhat naive on thread scheduling)

这篇关于并发性:共享内存与消息传递如何处理大型数据结构?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆