带 Bang 模式的 Haskell Strict MVar

sel*_*rus 4 haskell functional-programming lazy-evaluation

以下代码示例的执行时间大约为 2 秒。然而,当第 14 行中的刘海图案被移除时,需要 60 秒。谁能解释一下发生了什么事吗?

我使用的是严格的 MVar,因此无论放入 MVar 中的是什么,都应该完全评估为正常形式。我不希望 Bang 模式在插入 MVar 之前产生任何明显的效果。

{-# LANGUAGE BangPatterns #-}

import           Control.Concurrent.MVar.Strict
import qualified Data.Text as T
import           Data.Text.Encoding

main :: IO ()
main = do
    mvar <- newMVar T.empty

    let bsArr = map (\i -> encodeUtf8 $ T.pack $ "some strange string " ++ show i) [0 .. 30000 :: Int]
        mvarWriter =
            \lbs ->
                let !decoded = decodeUtf8 lbs
                in  modifyMVar_ mvar (\oldText -> return $ oldText <> decoded)

    mapM_ (\lbs -> mvarWriter lbs) bsArr
    print . T.length =<< readMVar mvar
Run Code Online (Sandbox Code Playgroud)

Nou*_*are 5

您的代码大致相当于:

  let !decoded = decodeUtf8 lbs
  oldText <- takeMVar mvar
  let !newText = oldText <> decoded
  putMVar mvar newText
Run Code Online (Sandbox Code Playgroud)

没有刘海图案,它是这样的:

  oldText <- takeMVar mvar
  let !newText = oldText <> decodeUtf8 lbs
  putMVar mvar newText
Run Code Online (Sandbox Code Playgroud)

如果没有爆炸模式,计算会在可能的最新点发生。那是在插入新值之前。然而,此时它MVar是空的:它oldText已经被取出了。在此期间,其他线程无法计算任何内容。因此,这意味着在任何给定时间只有一个线程可以进行实际计算。

爆炸图案强制在拍摄decodeUtf8 lbs之前进行评估。MVar因此这部分计算可以与其他线程并行进行。oldText <> decoded仅需要在关键部分进行相对便宜的计算。