繁体   English   中英

如何加快我的 Haskell 程序(到 Python 级别)

[英]How can I speed up my Haskell program (to the level of Python)

我有以下玩具程序,它循环移位向量并将其添加到自身(在 mod 下)。 它针对不同的班次和大量迭代(与向量的大小相比)执行此操作。 程序有效,但它的狗慢。 我还在学习 Haskell,所以我的问题是:我做错了吗?

import Data.List (foldl')
import qualified Data.Sequence as Seq
import Data.Sequence (index, zipWith, Seq, (><), (<|), (|>))

seqSize = 100
numShifts = 10000 

cycleShift :: Integer -> Seq a -> Seq a
cycleShift s l = Seq.drop (fromInteger s) l >< Seq.take (fromInteger s) l

modAdd :: Seq Integer -> Seq Integer -> Seq Integer 
modAdd s t = Seq.zipWith (\ a b -> (a + b) `mod` 10^16) s t

step :: Seq Integer -> Integer -> Seq Integer
step l shift = modAdd l (cycleShift shift l)

allshifts = [i `mod` seqSize |i <- [1..numShifts]]
start = Seq.fromList (1 : [0 | i <- [1..(seqSize - 1)]])
end = foldl' step start allshifts

main :: IO ()
main = print (Seq.index end 0)

Python中的相同程序

seq_size = 100 
num_shifts = 10000

S = [i % seq_size for i in xrange(1, num_shifts + 1)]
ssums = [1] + [0 for i in range(seq_size - 1)]

for s in S: 
    shift = ssums[s:] + ssums[:s]  
    ssums = [(ssums[i] + shift[i]) % 10**16 for i in range(seq_size)]  

print ssums[0]

以下是时间安排。 Haskell:实际0m5.596s Python:实际0m0.551s

Python 的速度不为人知,但速度却快了 10 倍???

你是如何运行它的?

对于 Haskell 版本,我得到 1.6 秒。 (用ghc.exe -O2 seq.hs编译。)

另外,您使用 Seq 是否有原因? 如果我将其更改为使用列表,我将获得 0.3 秒的执行时间。

这是列表:

import Data.List (foldl')

seqSize = 100
numShifts = 10000 

cycleShift s l = drop (fromInteger s) l ++ take (fromInteger s) l

modAdd s t = zipWith (\ a b -> (a + b) `mod` 10^16) s t

step l shift = modAdd l (cycleShift shift l)

allshifts = [i `mod` seqSize |i <- [1..numShifts]]
start = (1 : [0 | i <- [1..(seqSize - 1)]])
end = foldl' step start allshifts

main :: IO ()
main = print (end !! 0)
  1. 使用普通列表。 它们经过大量优化。 使用Data.Vector甚至更快。
  2. 使用rem而不是mod
  3. 避免不必要的工作。 (请参阅cycleShift 。之前,您将列表拆分了两次)
  4. 如果您的计算可能不超过界限,请使用Int而不是Integer 前者是硬件整数,而后者是任意精度,但通过软件模拟。

结果:3.6 秒到 0.5 秒。 更多可能是可能的。

代码:

import Data.List (foldl')
import Data.Tuple

seqSize, numShifts :: Int
seqSize = 100

numShifts = 10000 

cycleShift :: Int -> [a] -> [a]
cycleShift s = uncurry (++) . swap . splitAt s

modAdd :: [Int] -> [Int] -> [Int]
modAdd = zipWith (\ a b -> (a + b) `rem` 10^16)

step :: [Int] -> Int -> [Int]
step l shift = modAdd l (cycleShift shift l)

allshifts = map (`rem` seqSize) [1..numShifts]
start = 1 : replicate (seqSize - 1) 0
end = foldl' step start allshifts

main :: IO ()
main = print (head end)

编辑

使用Data.Vector会变得更快。 我使用以下代码在我的机器上得到大约 0.4 秒:

import Data.List (foldl')
import Data.Tuple

import Data.Vector (Vector)
import qualified Data.Vector as V

seqSize, numShifts :: Int
seqSize = 100

numShifts = 10000 

cycleShift :: Int -> Vector a -> Vector a
cycleShift s = uncurry (V.++) . swap . V.splitAt s

modAdd :: Vector Int -> Vector Int -> Vector Int
modAdd = V.zipWith (\ a b -> (a + b) `rem` 10^16)

step :: Vector Int -> Int -> Vector Int
step l shift = modAdd l (cycleShift shift l)

allshifts = map (`rem` seqSize) [1..numShifts]
start = 1 `V.cons` V.replicate (seqSize - 1) 0
end = foldl' step start allshifts

main :: IO ()
main = print (V.head end)

编辑 2

使用Data.Vector.Unboxed (只需更改导入并修复签名),运行时间下降到 0.074 秒。 但结果只有在Int有 64 位时才是正确的。 不过,使用Int64也可能会那么快。

确保 Haskell 代码已编译并且生成的可执行文件正在计时,而不是代码的解释版本。

极客之物

暂无
暂无

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM