我有一个程序,我试图加快速度,主要是为了让它更快地了解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
更快。
Centi
在Data.Fixed
中定义,是一个带有2位小数的数字
TimeOfDay
正如您所期望的那样,只需几小时,几分钟和几秒钟(以皮秒精度存储)。
`mod` posixDayLength
所以我们只是在一天中的时间忽略它是哪一天(因为这就是我所关心的......它来自时间戳,我知道它必须是今天 - 我只是关心今天几点!)。
我尝试使用ShowS (String -> String)
来连接结果,但速度并不快。
我尝试使用Data.Text
,但这会使代码变慢(可能会花费太多时间打包字符串)。
我曾经在一个单独的函数中使用putStrLn
,但这里的速度更快(积累的thunk少了?但是为什么?)。
是否有一种简单的方法可以提高我在Haskell中的输出性能?
答案 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)中连接到输出的缓冲区。不幸的是,并非所有类型都有一个构建器,只有基本类型。因此,输出其他解决方案的唯一方法是打包字符串...或者编写一个函数来创建构建器(并将其添加到库中?)。