如何提高在Haskell中为输出生成String的性能

时间:2014-05-13 10:58:03

标签: performance haskell

我有一个程序,我试图加快速度,主要是为了让它更快地了解Haskell。为了比较,我在C中编写了相同的程序,速度提高了4倍。我对C的预期更快,但这种差异让我觉得我有些不对劲。

所以我已经分析了Haskell代码,超过50%的时间用于生成格式化的String输出。所以这一部分需要的不仅仅是我的整个C程序。该功能类似于:

display :: POSIXTime -> [(Int, Centi)] -> IO()
display t l = putStrLn $ t_str ++ " " ++ l_str
    where
        t_str = show . timeToTimeOfDay . unsafeCoerce $ (t `mod` posixDayLength)
        l_str = intercalate " " $ map displayPair l
        displayPair (a,b) = show a ++ " " ++ show b

关于代码的说明: unsafeCoerce是将NominalDiffTime转换为具有相同类型的DiffTime,但这比我一直使用的toRational . fromRational更快。

CentiData.Fixed中定义,是一个带有2位小数的数字

TimeOfDay正如您所期望的那样,只需几小时,几分钟和几秒钟(以皮秒精度存储)。

`mod` posixDayLength所以我们只是在一天中的时间忽略它是哪一天(因为这就是我所关心的......它来自时间戳,我知道它必须是今天 - 我只是关心今天几点!)。

我尝试使用ShowS (String -> String)来连接结果,但速度并不快。

我尝试使用Data.Text,但这会使代码变慢(可能会花费太多时间打包字符串)。

我曾经在一个单独的函数中使用putStrLn,但这里的速度更快(积累的thunk少了?但是为什么?)。

是否有一种简单的方法可以提高我在Haskell中的输出性能?

1 个答案:

答案 0 :(得分:0)

为了产生输出,可以通过避免String支持ByteString或Text来找到最高性能。为了构建输出,有一种称为Builder的特殊数据类型。 [ByteString] hackage description中有一些很好的描述。

结果代码如下所示:

import Data.Monoid
display :: POSIXTime -> [(Int, Centi)] -> IO()
display t l = hPutBuilder stdout $ t_str <> space <> l_str
    where
        space = (byteString . pack) " "
        t_str = (byteString . pack . show . timeToTimeOfDay . unsafeCoerce) $ (t `mod` posixDayLength)
        l_str = foldr <> space $ map displayPair l
        displayPair (a,b) = intDec a <> space <> (byteString . pack . show) b

构建器数据类型构建块,然后它将在O(1)中连接到输出的缓冲区。不幸的是,并非所有类型都有一个构建器,只有基本类型。因此,输出其他解决方案的唯一方法是打包字符串...或者编写一个函数来创建构建器(并将其添加到库中?)。