【问题标题】:When do I use ByteString and when do I not?什么时候使用 ByteString,什么时候不使用?
【发布时间】:2011-05-11 23:49:27
【问题描述】:

我在 SPOJ 上的 PRIME1 问题上的尝试相当糟糕。我发现使用 ByteString really 有助于提高阅读问题文本的性能。但是,使用 ByteString 写出结果实际上比使用 Prelude 函数要慢一些。我试图弄清楚我是否做错了,或者这是预期的。

我使用 (putStrLn.show) 和 ByteString 等效项进行了三种不同的分析和计时:

  1. 我测试每个候选人,看看它是否 是素数。如果是这样,我将其添加到列表中 并用 (putStrLn . 显示)
  2. 我列出了所有素数 并使用 (putStrLn.unlines.show)
  3. 我列出了所有素数 并使用 地图 (putStrLn . 显示)

当您在一个函数中构建列表并在另一个函数中使用它时,我预计数字 2 和 3 的执行速度会变慢。通过在生成数字时打印它们,我避免为列表分配任何内存。另一方面,您在每次调用 putStrLn 时都会进行调用系统调用。正确的?所以我测试了,#1 实际上是最快的。

使用选项 #1 和 Prelude ([Char]) 函数实现了最佳性能。我希望我的最佳表现是使用 ByteString 的选项 #1,但事实并非如此。我只使用了惰性字节字符串,但我认为这无关紧要。会吗?

一些问题:

  • 您是否希望 ByteStrings 写一堆更好的表现 整数到标准输出?
  • 我是否缺少一种方式模式 生成并写出答案 这将导致更好的 性能?
  • 如果我只是将数字写成 文本,如果有的话,是否有 使用 ByteString 有什么好处?

我的工作假设是,如果您不将整数与其他文本结合使用,那么用 ByteString 写出整数会更慢。如果您将整数与 [Char] 结合使用,那么使用 ByteStrings 会获得更好的性能。即,ByteString 重写:

putStrLn $ "the answer is: " ++ (show value)

将比上面写的版本快得多。这是真的吗?

感谢阅读!

【问题讨论】:

    标签: haskell io performance bytestring


    【解决方案1】:

    使用字节串进行批量输入通常更快,因为数据很密集,从磁盘到内存的数据更少。

    然而,将数据写成输出有点不同。通常,您正在序列化一个结构,生成许多小的写入。因此,在这种情况下,字节串的密集、批量写入对您没有多大帮助。即使是普通的Strings 也能合理地增加输出。

    但是,一切都没有丢失。我们可以通过在内存中有效地构建字节串来恢复快速批量写入。各种*-builder 包都采用这种方法:

    我们没有将值转换为大量微小的字节串并一次写入一个,而是将转换流式传输到不断增长的缓冲区中,然后将该缓冲区写入一个大块。与字符串 IO 相比,这会大大减少 IO 开销,并提高性能(通常很显着)。

    这种方法是由 e.g. Haskell 中的网络服务器,或高效的 HTML 系统,blaze

    此外,即使是批量写入,性能也将取决于您在类型和字节串之间拥有的任何转换函数的效率。对于Integer,您可以简单地将内存中的位模式复制到输出,或者通过一些低效的解码器。因此,您有时不得不考虑一下您正在使用的编码函数的质量,而不仅仅是使用 Char/String 还是字节串 IO。

    【讨论】:

    • 你能指出我上面提到的其中一个库的内容吗?即,将转换流式传输到不断增长的缓冲区的部分?我不熟悉任何这些库的内部结构。 Text.Blaze.Renderer.UTF8 中的 renderHtml 会是一个例子吗?
    • 例如在二进制中,builder 是一个异步更新可变缓冲区的延续函数。
    【解决方案2】:

    请注意,性能并不是ByteStringString 之间的主要区别。前者用于二进制数据,后者用于 Unicode 文本。如果你有二进制数据,使用ByteString,如果你有Unicode文本,使用text package中的Text类型。

    【讨论】:

      猜你喜欢
      • 1970-01-01
      • 2022-11-10
      • 1970-01-01
      • 2014-06-04
      • 2017-09-13
      • 1970-01-01
      相关资源
      最近更新 更多