24

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

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

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

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

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

一些问题:

  • 您是否希望 ByteStrings 在将一堆整数写入标准输出时表现更好?
  • 我是否错过了一种生成和写出会导致更好性能的答案的方式模式?
  • 如果我只是将数字写成文本,那么使用 ByteString 是否有好处?

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

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

会比上面写的版本快很多。这是真的?

谢谢阅读!

4

2 回答 2

21

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

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

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

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

例如,Haskell 中的网络服务器或高效的 HTML 系统blaze采用了这种方法。

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

于 2011-05-12T00:04:52.723 回答
7

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

于 2011-05-12T08:15:23.013 回答