软件事务内存,包含大量共享数据

gsp*_*spr 10 haskell stm

原来的问题

我是STM的新手.我想在Haskell中做的一件事涉及大量数据,以及大量轻量级线程读取和写入所述大数据的一小部分.读取和写入的位置可以被认为是基本上随机的和小的.STM看起来很棒,但我对如何解决这个问题有一些疑问.我看到了几种可能的方法,每种方法都有一些缺点,有些方法看起来很愚蠢.关于这些或其他替代方案的一些评论将不胜感激.

让我们简单地假设大块数据是a Data.Vector a,其中元素本身很小.

  1. 整块数据为一体TVar (Vector a).我想这将导致大量数据的复制,因为STM会认为每个单独的写入可能会影响整个共享数据.当然,STM确定读写是非常本地化的,并且不需要跨大数据的一致性,这是不可思议的?

  2. 大量的TVar as,基本上每个元素一个,给出完全本地化的STM,但基本上复制了整个Vector a.这看起来很愚蠢.

  3. 通过对数据进行分段以使得我具有TVar (Vector a)与数据的子向量对应的合理数量的s来在1和2之间进行折衷.我觉得这个解决方案太依赖于启发式,比如细分应该有多大.

  4. 消息.不是每个工作者使用STM读取和写入数据,而是每个都写入带有要读取数据的请求的消息或者通过某些STM机制写入的数据块,例如a TChan.特殊线程接收这些消息,传递通过另一个消息请求的数据TChan,或者接收数据并将其写入共享数据结构.这个解决方案似乎没有困扰解决方案1-3的问题,但在我看来,它基本上放弃了使用STM的细节来保持数据的一致性.相反,它只是消息传递.当然,消息传递部分是用STM实现的,但我的真正问题是通过消息传递解决的.STM似乎很棒,消息传递是如此......嗯.

我正确地考虑了这个吗?有人有任何提示或其他建议吗?

请记住,我没有使用STM的经验,也没有尝试过上述解决方案.我会离开扶手椅,但有时在尝试任何事情之前考虑这些事情会很好.

附录:第五种方法来自Nathan Howell并使用TArray.这听起来像我想要的,但文档说:

它目前被实现为Array ix(TVar e),但它可能在将来被更有效的实现取代(但是接口将保持不变).

我认为这意味着这TArray只是我穿着更好衣服的第2号方法.暗示"更有效"实施的文档很有意思,因为它暗示实际上有一种更好的方法.

跟进Vagif Verdi的回答

Vagif Verdi的答案非常有趣,所以我做了一个小实验来试试.我现在没有时间减少代码,所以对此感兴趣的人将不得不承担代码而不仅仅包含基本要素.我决定使用一个10 ^ 8 Ints 的可变向量作为"大共享数据",让多个读者/写者对应于进入网络套接字的线程.

请注意,代码甚至不会读取或写入共享数据.它只是在那里,每个线程都拥有TVar它.

那会发生什么?我运行程序,并立即占用大约780 MB的RAM,这是预期的(它大致是10 ^ 8 Int需要的).现在,如果我使用netcat连接几个客户端并编写一些文本,程序应该打印出来,甚至不写入共享数据,那么进程的CPU使用率会高达100%,持续时间超过一秒!在显示文本之前有明显的延迟.从好的方面来看,内存使用率保持不变(根据Vagif Verdi的回答).如果我删除了矢量TVar,即取出所有STM和共享数据,一切都非常快速和响应,并且每当客户端写入内容时,没有明显的CPU使用率.

因此,虽然很高兴看到共享数据实际上并不重复(虽然我认为我应该实际写入共享数据以便完全验证),但是维护相干状态会导致非常严重的性能损失.对我来说,我的问题仍然存在:如何在保持STM的细节的同时正确地解决这个问题?

感谢Vagif Verdi提出一些非常有趣的观点.

Phi*_* JF 5

STM不是魔术.如果你有一个巨人,TVar它将不得不在任何给定的时间阻止所有线程.这相当于"粗粒度锁定",并且具有易于使用的优点,但STM的全部要点是不要强制使用粗粒度锁定.实际上,只有一个线程可以使用这种方法一次写入.您的"消息传递"系统也是如此 - 一个线程是限制可扩展性的瓶颈.

我不认为使用TVars 数组有一个大问题,我不知道你为什么把你的方法2描述为"愚蠢".这正是STM发明的目的.

编辑:我鼓励有兴趣的人观看这个视频,或者至少是它的开头,讨论STM的一些动机.它已经有几年的历史了,而事务性提升的内容并不是真正相关的,但是Herlihy非常出色,也是计算机科学家之一,即使不是你的事情,也能让这个领域变得有趣.