Reputation: 23135
I've wrote the following function that I believe should perform IO atomically (as long as everyone else is using the same MVar
).
atomicIO :: MVar () -> IO a -> IO a
atomicIO mvar io =
do
takeMVar mvar
result <- io
putMVar mvar ()
return result
Also, from what I understand, some parts of Haskell IO are lazy (e.g.. IORef
s), so there is no need to actually perform the action in this section. It can just return a 'thunk' (is that the right word?) which lists the tasks that needs to be done.
The idea is that, the critical section (i.e. the single threaded part) should be quite quick.
However, lets say I'm writing to IORef
s, and I want Haskell to start computing the result immediately, so it's ready to do when required. But like I said before, I don't want to get locked in the critical section when we're holding the MVar
lock.
So I've thought about doing this:
result <- io `par` io
or this
return result `par` result
or this
result `par` return result
But I'm not sure if this does the job. Is one of these the right approach, or is there another way? (my concern about the latter two is IO ()
actions, as I figure evaluating ()
in parallel doesn't actually do any work).
Upvotes: 4
Views: 450
Reputation: 47052
Where you have
writeIORef myRef newValue
Replace that with
newValue `par` writeIORef myRef newValue
That will spark off a thread to evaluate newValue
in the background...
...with the caveat that it will only evaluate it to WHNF (basically, only the first level of the data structure will be evaluated). So an Int
would be entirely evaluated, but a String
wouldn't. A Maybe a
value would be entirely evaluated to Nothing
or partially evaluated to Just _
.
So if you are using a more complex data structure, you will need to use force
from Control.DeepSeq in the deepseq package, which will completely evaluate the value.
force newValue `par` writeIORef myRef newValue
If you want to use modifyIORef
, I don't think you can reuse modifyIORef
directly, but you can certainly define a similar function (modifyIORef
is defined in terms of readIORef
and writeIORef
anyway):
modifyIORefInParallel :: NFData a => IORef a -> (a -> a) -> IO ()
modifyIORefInParallel ref f
= do old <- readIORef ref
let new = f old
force new `par` writeIORef ref new
(Note that if the last line was force (f old) `par` writeIORef ref (f old)
it wouldn't work: the value that was forced in parallel would not be the value that was stored in the ref.)
(The NFData a
restriction is from using force
.)
Upvotes: 4
Reputation: 1693
The only way to get fast critical sections is by limiting yourself to fast IO actions.
I don't see how trying to force strict evaluation inside atomicIO
would give you any speed up. Moreover, note that atomicIO
itself might not be strictly evaluated, in which case the strict evaluation inside atomicIO
doesn't have any effect.
In any case, you can use seq
instead of par
for strict evaluation, as you're not trying to spark parallel computations.
Upvotes: 1