簡體   English   中英

使用ST monad

[英]Using the ST monad

在博客文章http://galvanist.com/post/83741037068/adding-badly-under-python-julia-go中 ,作者使用一種簡單的算法來比較各種語言(包括Haskell)的性能。 在Haskell示例中,作者使用遞歸函數。 作為練習,我想使用ST monad來允許本地可變狀態。 這是有效的,但遞歸函數比我使用ST monad的函數快得多。

遞歸函數 -

peanoAdd :: Int -> Int -> Int
peanoAdd 0 y = y
peanoAdd x y = peanoAdd (x - 1) (y + 1)

main :: IO ()
main = do
    let a = 64000000 :: Int
    let b = 64000000 :: Int
    let n = peanoAdd a b
    print n

128000000

real    0m0.583s
user    0m0.480s
sys     0m0.096s

使用ST monad-

import Control.Monad.ST
import Data.STRef
import Control.Monad.Loops

peanoAdd :: Int -> Int -> Int
peanoAdd x y = runST $ do
    x' <- newSTRef x
    y' <- newSTRef y
    whileM_ (do x'' <- readSTRef x'
                return $ x'' /= 0)
            (do modifySTRef x' (subtract 1)
                modifySTRef y' (+1))
    readSTRef y'

main :: IO ()
main = do
    let a = 64000000 :: Int
    let b = 64000000 :: Int
    let n = peanoAdd a b
    print n

128000000

real    0m17.837s
user    0m16.412s
sys     0m1.424s

在ST monad例子中,有什么東西顯然是錯誤的嗎? (PS。我正在使用Stack和兩個項目的簡單模板。)

你的ST程序運行緩慢的一個原因是你使用的是modifySTRef ,它是非嚴格的

請注意, modifySTRef不嚴格應用該功能。 這意味着如果程序多次調用modifySTRef ,但很少使用該值,thunk會堆積在內存中導致空間泄漏。 這是使用STRef作為計數器時常見的錯誤。 例如,以下內容將泄漏內存並可能產生堆棧溢出:

 print $ runST $ do ref <- newSTRef 0 replicateM_ 1000000 $ modifySTRef ref (+1) readSTRef ref 

你的x'每循環被強制一次,但y'print之前不會被強制,因此會形成一大堆thunk。

在我的筆記本電腦上對使用modifySTRef'的版本進行基准測試顯示了嚴格性如何提高運行時間(盡管兩者仍然輸給遞歸版本)。

benchmarking rec
time                 7.896 ms   (7.602 ms .. 8.269 ms)
                     0.992 R²   (0.988 R² .. 0.997 R²)
mean                 7.842 ms   (7.724 ms .. 8.001 ms)
std dev              404.5 μs   (303.9 μs .. 523.8 μs)
variance introduced by outliers: 25% (moderately inflated)

benchmarking st
time                 18.44 ms   (17.84 ms .. 19.01 ms)
                     0.996 R²   (0.993 R² .. 0.998 R²)
mean                 18.03 ms   (17.79 ms .. 18.41 ms)
std dev              750.4 μs   (528.0 μs .. 1.110 ms)
variance introduced by outliers: 16% (moderately inflated)

benchmarking st'
time                 9.191 ms   (9.028 ms .. 9.437 ms)
                     0.996 R²   (0.992 R² .. 0.999 R²)
mean                 9.317 ms   (9.175 ms .. 9.527 ms)
std dev              475.8 μs   (311.8 μs .. 677.9 μs)
variance introduced by outliers: 25% (moderately inflated)

基准代碼:

import Criterion.Main
import Control.Monad.ST
import Data.STRef
import Control.Monad.Loops

peanoAddST :: Int -> Int -> Int
peanoAddST x y = runST $ do
    x' <- newSTRef x
    y' <- newSTRef y
    whileM_ (do x'' <- readSTRef x'
                return $ x'' /= 0)
            (do modifySTRef x' (subtract 1)
                modifySTRef y' (+1))
    readSTRef y'

peanoAddST' :: Int -> Int -> Int
peanoAddST' x y = runST $ do
    x' <- newSTRef x
    y' <- newSTRef y
    whileM_ (do x'' <- readSTRef x'
                return $ x'' /= 0)
            (do modifySTRef' x' (subtract 1)
                modifySTRef' y' (+1))
    readSTRef y'

peanoAddRec :: Int -> Int -> Int
peanoAddRec 0 y = y
peanoAddRec x y = peanoAddRec (x - 1) (y + 1)

main =
  let n = 64000 in
  defaultMain
  [ bench "rec" $ whnf (peanoAddRec n) n
  , bench "st" $ whnf (peanoAddST n) n
  , bench "st'" $ whnf (peanoAddST' n) n
  ]

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM