手指树(Data.Sequence)与绳索(Data.Rope)(Haskell,或一般)

时间:2012-01-17 15:46:28

标签: performance haskell data-structures sequence

Finger Tree (Data.Sequence)和绳索(Data.Rope)(Edward Kmett's versionPierre-Etienne Meunier's version之间的主要区别是什么?

在Haskell库中,Data.Sequence具有更多功能。我认为绳索可以更有效地处理“阻塞”。

作为程序员考虑效率处理,比如一个700万字符的序列,我需要做的事情(a)插入任何地方,(b)剪切和粘贴段(拼接),(c)搜索和替换子字符串哪个更有效率?

对ehird的回应澄清:

  1. 我的算法的大部分都运行了数千个搜索替换操作,如s/(ome)?reg[3]x/blah$1/g,以反复改变数据。所以我需要有效的正则表达式模式匹配(可能是regex-tdfa?)以及拼接(data [a:b] = newData),其中不是必然{ {1}}

  2. Lazy ByteStrings可能没问题,但是拼接呢?拼接ByteString是O(dataSize / chunkSize)线性时间(用于搜索),加上(可能是?)开销用于维护恒定大小的块。 (后一部分可能是错的); vsT(Log(dataSize))用于FingerTree。

  3. 我的“容器”数据类型抽象地是有限字母。它可以具体表示(length(newData) == b-a+1) s或Char s或Byte s甚至是假设的Word8 s(半字节)。 **我有一个关于如何有效使用Word4newtype的相关问题,以便我的代码可以引用抽象字母表,但编译后的程序仍然可以高效。 (我应该单独发布这个问题。)

  4. 性能问题:也许Seq远比ByteString差(通过q显着的常数因子)。在简单的测试中,将7MB读入严格的data,然后将其打印到60MB真实内存使用的控制台峰值(根据Windows Process Manager),但将该内容加载到ByteString然后打印使用400MB ! (我应该单独发布这个问题,包括代码和分析详细信息。)

  5. 平台问题:我正在使用EclipseFP和Haskell平台。我在我的机器上安装了Text,我想尝试一下,但我的Eclipse环境找不到它。每当我使用Seq Char(安装了不兼容的软件包版本,cabal install vs --user混淆)时,我就会遇到严重问题,所以我想坚持使用EclipseFP可以找到的平台软件包。我认为Text将进入下一版本的平台,所以这将很好。

  6. Trifecta :我简短地看到了Trifecta,这加剧了我的困惑。 (为什么它有自己的已经发布的一般数据结构的新实现?它们更好吗?太多几乎相同的选项!)

  7. 已编辑,其中包含更多详细信息和改进的链接。

    这个问题很大。

    @ ehird的总结是主要的回归点。绳索,或ByteStrings或矢量的手指树加上一个小的自定义幺半群。无论哪种方式,我都必须编写一个简单的正则表达式实现来粘合。

      

    鉴于所有这些信息,我建议使用绳索或建筑物   你自己的结构与它所基于的fingertree包(相反   比Seq,所以你可以适当地实现像长度这样的东西   用叶子测量的类型 - 见幺半群和手指树   数据分块为未装箱的Vector。当然,后者更多   工作,但让你专门针对你的用例进行优化。无论哪种方式,   绝对将它包装在一个抽象的界面中。

    我今天晚些时候会回来并分成新的问题。我将整理出低级技术问题,然后再回到整体比较中。 我将更改问题标题以更好地反映我真正关心的问题“哪些Haskell模块提供或支持我需要的序列操作操作?”谢谢你去ehird和其他响应者。

1 个答案:

答案 0 :(得分:16)

对于本答复的其余部分,我假设您实际上是在尝试存储原始字节,而不是字符。如果要存储字符,则应考虑使用text(对于Unicode文本等效ByteString)或基于它编写自己的基于fingertree的结构。您还可以将ByteStringData.ByteString.UTF8包中的utf8-string模块一起使用;我认为最终可能会提高效率,但对于Unicode文本而言,它的功能要比Text更全面。

嗯,你链接的绳子包只存储了ByteString s的等价物,而Seq是通用的,可以处理任何类型的数据;前者可能更有效地存储字符串。

我怀疑它是相同的基本树结构,因为绳索实现"字节串的指尖"和Seq是2-3指树;它取决于(并因此可能使用)fingertree包,它与Data.Sequence基本相同,但更通用。绳索可能会将数据打包成短ByteString秒,这对Seq是不可能的(不会破坏length等操作。)

总而言之,如果您要存储字节串数据,绳索看起来就像是一个更好的结构,并且它似乎具有花哨的功能,用于注释"字符串的一部分;但是,它最后一次更新是在7月份,同一位作者(8月首次发布)的新trifecta解析器组合库包含其own set of rope modules,因此在其上建立新代码可能是不明智的。当然,对三连胜的改变可能与一般用途无关,并且将这些改变作为新版本的绳索分开可能并不困难;也许他们没有成功的唯一原因是因为三连已经有很多依赖:)

但是,如果您在处理过程中的任何一点都需要通用容器类型(例如,将字节解析为更丰富的表示序列),或者想要坚持Haskell平台中的内容,那么您&# 39;我需要使用Seq

您确定ByteStringText(因为您提到的字符)不适合您正在做的事情吗?它们存储偏移量和长度字段,因此获取子字符串不会导致任何复制。如果你的插入操作很少,那么它可能会成功。基于IntMap的某种结构也可能值得考虑。


回答您的最新问题:

  1. 自定义字符串类型的正则表:请记住,将现有的正则表达式实现与"不常见"字符串类型,您必须implement the support yourself将其粘贴到现有的regex-tdfa代码中。我不确定最终的表现会是什么。
  2. 拼接延迟ByteString s:请注意,默认情况下,懒惰ByteString使用64 KiB块,您可以使用{{{{{{ 1}}手动。但你是对的,手指树可能更适合;只是做了更多的工作,已经为你做了懒惰的fromChunks s。
  3. 有限字母:好;我建议你抽象出(用ByteString)表示这个字母表序列的类型。这样,你可以尝试各种实现,同时希望本地化必须完成的工作,这样你就可以根据真实的性能数据而不是猜测来选择:)当然,编写一个新的仍然是一个前期成本实现。就您的其他问题而言,newtype在编译时被删除,因此newtype具有与其包装类型相同的运行时表示。简而言之:不要担心在newtype中包装内容。
  4. Seq表现嗯,这并不奇怪。 newtype完全是懒惰和盒装,并且不会被" chunking" Seq Char就像Char一样{;}}它可能比Rope的内存效率更低。像String之类的东西可能会更好地执行 lot ,但除非你的块大小不变,否则你将无法获得有意义的长度等等而不会遍历整个事物。
  5. EclipseFP包问题:我不会根据这样的简单工具问题选择使用哪种表示形式;我建议问一个新问题。
  6. Trifecta:我不认为三连词与你的问题有关;它只是由绳索的同一作者写的,这就是它与绳索的持续发展有关的原因。它只是像Parsec这样的解析器组合库,它更侧重于诊断等而不是性能,所以我不认为它可以取代你的正则表达式。
  7. 至于#3,而不是Seq ByteString,您可能需要考虑unboxed Vector;这样,您就可以使用抽象字母表类型,而不是将内容隐藏到ByteString基于ByteString的界面中。

    鉴于所有这些信息,我建议使用Word8,或使用fingertree基于({1}}的{​​{3}}软件包构建您自己的结构,以便您可以使用Rope类型类正确实现Seq之类的内容 - 请参阅Monoids and Finger Trees),并将叶数据分块为未装箱的length。当然,后者是更多的工作,但让你专门针对你的用例进行优化。无论哪种方式,绝对将它包装在一个抽象的界面中。

    顺便说一句,正则表达式在Haskell生态系统中并不像它们那样得到良好支持;如果有任何选择,可能值得考虑使用其他东西。但是,它过分依赖于您的计划的具体细节来提供具体的建议。