jub*_*0bs 45 python io performance haskell
作为编程挑战的一部分,我需要从stdin读取一系列空格分隔的整数(在一行上),并将这些整数的总和打印到stdout.有问题的序列可以包含多达10,000,000个整数.
我有两个解决方案:一个用Haskell(foo.hs
)编写,另一个用等价的,用Python 2(foo.py
)编写.不幸的是,(编译的)Haskell程序一直比Python程序慢,而且我不知道解释两个程序之间的性能差异; 请参阅下面的基准部分.如果有的话,我会期望Haskell占上风......
我究竟做错了什么?我如何解释这种差异?有没有一种简单的方法来加速我的Haskell代码?
(有关信息,我使用的是2010年中期的Macbook Pro,配备8Gb RAM,GHC 7.8.4和Python 2.7.9.)
foo.hs
main = print . sum =<< getIntList
getIntList :: IO [Int]
getIntList = fmap (map read . words) getLine
Run Code Online (Sandbox Code Playgroud)
(编译ghc -O2 foo.hs
)
foo.py
ns = map(int, raw_input().split())
print sum(ns)
Run Code Online (Sandbox Code Playgroud)
在下文中,test.txt
由一行1000万个以空格分隔的整数组成.
# Haskell
$ time ./foo < test.txt
1679257
real 0m36.704s
user 0m35.932s
sys 0m0.632s
# Python
$ time python foo.py < test.txt
1679257
real 0m7.916s
user 0m7.756s
sys 0m0.151s
Run Code Online (Sandbox Code Playgroud)
And*_*ács 66
read
是慢的.对于批量解析,使用bytestring
或text
原语,或attoparsec
.
我做了一些基准测试.您的原始版本在我的计算机上运行了23.9秒.以下版本的运行时间为0.35秒:
import qualified Data.ByteString.Char8 as B
import Control.Applicative
import Data.Maybe
import Data.List
import Data.Char
main = print . sum =<< getIntList
getIntList :: IO [Int]
getIntList =
map (fst . fromJust . B.readInt) . B.words <$> B.readFile "test.txt"
Run Code Online (Sandbox Code Playgroud)
通过将解析器专门化到您的test.txt
文件,我可以将运行时间缩短到0.26秒:
getIntList :: IO [Int]
getIntList =
unfoldr (B.readInt . B.dropWhile (==' ')) <$> B.readFile "test.txt"
Run Code Online (Sandbox Code Playgroud)
Tho*_*son 27
读取很慢
从这个答案中快速阅读将使您降低到5.5秒.
import Numeric
fastRead :: String -> Int
fastRead s = case readDec s of [(n, "")] -> n
Run Code Online (Sandbox Code Playgroud)
字符串是链接列表
在Haskell中,String
类型是链表.使用打包表示(bytestring
如果你真的只想要ascii,但Text
也非常快并且支持unicode).如这个答案所示,表现应该是颈部和颈部.
我敢猜测,您的问题的很大一部分实际上是words
. 当你map read . words
,你实际上在做什么是这样的:
这是一种相当荒谬的前进方式。我相信你甚至可以使用类似的可怕的东西做得更好reads
,但使用类似ReadP 的东西会更有意义。你也可以尝试更高级的东西,比如基于流的解析;我不知道这是否会有很大帮助。