为什么从Data.Text构建Haskell字符串这么慢

时间:2015-11-14 15:31:19

标签: haskell

所以我有一个位置类

data Location = Location {
    title :: String
  , description :: String
  }

instance Show Location where
  show l = title l ++ "\n"
         ++ replicate (length $ title l) '-' ++ "\n"
         ++ description l

然后我将其更改为使用Data.Text

data Location = Location {
    title :: Text
  , description :: Text
  }

instance Show Location where
  show l = T.unpack $
    title l <> "\n"
    <> T.replicate (T.length $ title l) "-" <> "\n"
    <> description l

使用标准,我对showString实施中Data.Text所花费的时间进行了基准测试:

benchmarks = [ bench "show" (whnf show l) ]
  where l = Location {
                title="My Title"
              , description = "This is the description."
              }

String实施需要34ns,Data.Text实施几乎六倍,为170ns

如何让Data.Text的工作速度与String一样快?

编辑:愚蠢的错误

我不确定这是怎么发生的,但是我不能复制原来的速度差异:现在对于String和Text我分别得到28ns和24ns

对于更具侵略性的bench "length.show" (whnf (length . show) l)基准测试,对于字符串和文本,我分别得到467ns和3954ns。

如果我使用非常基本的懒惰构建器,没有复制的破折号

import qualified Data.Text.Lazy.Builder as Bldr

instance Show Location where
  show l = show $
    Bldr.fromText (title l) <> Bldr.singleton '\n'
  --  <> Bldr.fromText (T.replicate (T.length $ title l) "-") <> Bldr.singleton '\n'
    <> Bldr.fromText (description l)

并尝试原始的普通show基准,我得到19ns。现在这是错误的,因为使用show将构建器转换为String将转义换行符。如果我将其替换为LT.unpack $ Bldr.toLazyText,其中LTData.Text.Lazy的合格导入,则我获得192ns。

我在Mac笔记本电脑上对此进行了测试,我怀疑我的时间因机器噪音而受到严重破坏。感谢您的指导。

2 个答案:

答案 0 :(得分:3)

你不能让它快速,但你可以加速它。

附加

Text表示为数组。这使得<>相当慢,因为必须分配新数组并将每个Text复制到其中。您可以先将每个部分转换为String,然后再连接它们来解决此问题。我想Text可能也提供了一种有效的方法来同时连接多个文本(作为commenter提及,你可以使用一个懒惰的构建器),但为了这个目的,这将更慢。另一个好的选择可能是Text的惰性版本,它可能支持有效的连接。

共享

在基于String的实施中,根本不需要复制description字段。它仅在Location和显示Location的结果之间共享。使用Text版本无法实现此目的。

答案 1 :(得分:3)

在String情况下,您没有完全评估所有字符串操作 - (++)和复制。

如果您将基准更改为:

benchmarks = [ bench "show" (whnf (length.show) l) ]

你会发现String情况需要大约520 ns - 大约10倍。