提高文件操作性能

Increasing performance in file manipulation

我有一个包含数字矩阵的文件,如下所示:

0 10 24 10 13 4 101 ...
6 0 52 10 4 5 0 4 ...
3 4 0 86 29 20 77 294 ...
4 1 1 0 78 100 83 199 ...
5 4 9 10 0 58 8 19 ...
6 58 60 13 68 0 148 41 ...
. .
.   .
.     .

我想做的是对每一行求和并将每一行的总和输出到一个新文件(每行的总和在一个新行上)。

我曾尝试在 Haskell 中使用 ByteStrings 执行此操作,但性能比 python 实施慢 3 倍。这是 Haskell 实现:

import qualified Data.ByteString.Char8 as B

-- This function is for summing a row
sumrows r = foldr (\x y -> (maybe 0 (*1) $ fst <$> (B.readInt x)) + y) 0 (B.split ' ' r)

-- This function is for mapping the sumrows function to each line
sumfile f = map (\x -> (show x) ++ "\n") (map sumrows (B.split '\n' f)) 

main = do
  contents <- B.readFile "telematrix"
  -- I get the sum of each line, and then pack up all the results so that it can be written
  B.writeFile "teleDensity" $ (B.pack . unwords) (sumfile contents)
  print "complete"

对于 25 MB 的文件,这大约需要 14 秒。

这是 python 实现

fd = open("telematrix", "r")
nfd = open("teleDensity", "w")

for line in fd: 
  nfd.write(str(sum(map(int, line.split(" ")))) + "\n")

fd.close()
nfd.close()

对于同一个 25 MB 的文件,这大约需要 5 秒。

关于如何增加 Haskell 实施的任何建议?

乍一看,我敢打赌你的第一个瓶颈是在 sumfile 中字符串的 ++ 中,它每次都在解构左操作数并重建它。您可以将 unwords 函数调用替换为 unlines,而不是将 "\n" 附加到末尾,这完全符合您的要求。那应该会让你的速度有一点提升。

一个更小的挑剔是 maybe 函数中的 (*1) 是不需要的。使用 id 会更有效率,因为 (*1) 浪费了一个乘法运算,但这不过是几个处理器周期而已。

最后,我不得不问你为什么在这里使用 ByteStringByteString 将字符串数据有效地存储为数组,就像命令式语言中的传统字符串一样。但是,您在这里所做的涉及拆分字符串和遍历元素,这是链表适合的操作。老实说,在这种情况下,我建议使用传统的 [Char] 类型。 B.split 调用可能会毁了你,因为它必须将整行复制到拆分形式的单独数组中,而用于字符链接列表的 words 函数只是拆分链接结构在几个点关闭。

他的问题似乎是我正在编译和 运行 使用 runhaskell 的程序,而不是使用 ghc 然后 运行 程序。通过先编译然后 运行,我在 Haskell

中将性能提高到 1 秒

性能不佳的主要原因是因为我使用的是runhaskell,而不是先编译然后运行程序。所以我从:

runhaskell program.hs

ghc program.hs

./program