The following code example takes approx 2 seconds to execute. When the bang pattern in line 14 is removed however, it takes 60s. Can anyone explain what is going on?
I am using strict MVar so whatever is put into the MVar, should be fully evaluated to normal form. I would not expect a Bang pattern before inserting into the MVar to have any noticable effect.
{-# 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
CodePudding user response:
Your code is roughly equivalent to:
let !decoded = decodeUtf8 lbs
oldText <- takeMVar mvar
let !newText = oldText <> decoded
putMVar mvar newText
Without bang pattern it is like this:
oldText <- takeMVar mvar
let !newText = oldText <> decodeUtf8 lbs
putMVar mvar newText
Without bang pattern, the computation happens at the latest point possible. That is right before the new value is inserted. However, at that point the MVar
is empty: the oldText
has already been taken out. During this time no other threads can compute anything. So this means that only a single thread can do actual computation at any given time.
The bang pattern forces decodeUtf8 lbs
to be evaluated before the MVar
is taken. So that part of the computation can happen in parallel with other threads. Only the relatively cheap oldText <> decoded
computation needs to happen in the critical section.