[Haskell-cafe] Re[2]: Optimizing locking with MVars

Bulat Ziganshin bulat.ziganshin at gmail.com
Thu May 4 14:40:46 EDT 2006

Hello Simon,

Wednesday, May 3, 2006, 3:07:19 PM, you wrote:

> I propose to add INLINE pragmas to withMVar and friends.

and to any higher-order function? :)  imho, key of this problem is
that GHC don't have a way to optimize just the required function with
all it's enclosed calls. in old GHC papers there is an "inline"
function that does this (afai understood) but it is no more supported

problem is not that withMVar should be inlined in any case of its use.
problem is what i need to inline it's use in one concrete place of my
concrete library and there is no way to force GHC do this beside of
copying it's implementation! i think that GHC should just give more
priority to inlining higher-order and dictionaries-dependent functions
(afaiu, currently they are weighted by the same rules as ordinary

and the second part of problem is what it can't be decided at the
place of "withMVar" definition whether it should be inlined or not -
this depends in the first place on the call site. if this is the code
that performed just one time, we should decide on the space basis. if
this is the innermost loop, we should inline even largest functions
(especially if they are higher-order/polymorphic ones). but the
compiler don't know how much times some code fragment will be
performed (obviously it's place for future integration between
optimizer and profiler...) so, i think, it should generally optimize
program on code space and only for procedures marked as OPTIMIZE it
should enable all the inlining machinery, trying to make code
generated for this procedure (including all the inner calls) as fast
as possible. this should allow programmer to just mark speed-critical
procedures and will make library writers absolutely free from dancing
with INLINE pragmas.

while currently to optimize some procedure in my application, i mark
it INLINE, then find and mark all procedures it call, and further
recursively. i prefer that GHC will do it instead of me :)

> Having an interface for simple locks sounds like a good idea to me. 
> Would you like to send a patch?

i've attached this module together with two examples on that i've
tested it. first example (a.hs) really don't work :)  i think it's
shortcoming of current Haskell standard (at least, Hugs also can't
compile it). moreover, it seems that we should change MArray
declaration to the following:

class (Monad m, MutableBounds a m, Ix i) => MArray a e m | a->e, a->i where
    newArray    :: (i,i) -> e -> m a
    newArray_   :: (i,i) -> m a
    unsafeRead  :: a -> Int -> m e
    unsafeWrite :: a -> Int -> e -> m ()

(i.e. 'a' here may be for example "IOArray Int Double" while it's just
"IOArray" in current definition)

drawback is what this needs FD (while old definition use only MPTC)
and what this new definition will be not compatible with some old
code. on the other side, as you can see here, using of type
constructor in class definition tend to create problem with
partially-applied type functions, i've encountered this problem
several times while redesigning array library, but before this moment
each time i found some way around problem. but the problem still

> This won't affect Handle I/O unfortunately, because we need block to 
> protect against asynchronous exceptions.

i think that it is not accidental that hPutChar/hGetChar is exactly 3
and 2 times slower than their v* analogs. my routines use only one
locked MVar and i think that Handle routines used 3 and 2 mvars,
respectively. i thought that they call withMVar directly, but it is
not the case. but may be great blocks that they are perform inside
"block" operations are still not inlined and therefore suffers from
the same problem as withMVar? i guess that adding INLINE pragma to
block definition may speed up their execution (in 52/38 ~= 1.4 times),
but possibly at the cost of code size. anyway, Handle I/O is already too
complicated thing to worry about it's further optimizations :(

> I'm still not certain you 
> won't need that in the stream library, too: check any stateful code (eg.
> buffering) and imagine what happens if an exception is raised at an 
> arbitrary point.

thank, it's sort of critics what i very need. the total checking seems
to rather complex problem so i will decline it until working on next
release. the good news is what my buffering scheme updates only one
unboxed pointer (current read/write position inside buffer) on all
operations that can be performed using only buffer contents, so it
seems that i don't need to use "block" to protect such operations
until routine realizes that there is need to switch to other buffer.
btw, i've changed default buffer size to 4kb

in next version i will work on this problem. i think that it should be
described as some contract between library authors (including authors
of 3rd-party streams and transformers) and users, in this form:

if stream operation abandoned because of asynchronous interrupt,
internal bug or error in input data:

- it should leave stream in coherent state (f.e., it should not leave
vIsEOF=True and vTell=0 for non-empty-file)

- it should not have "side-effects", f.e. changing position if whole
operation (such as vShow) don't have such effect

- operation can be performed only partially (i.e. vSeek operation can
leave pointer in unpredictable place, vPutStr can output only part of
string, vGetBuf may fill only part of buffer)

- if possible, operation implementation should give to async.
exceptions chances to interrupt execution with rather small
granularity (i.e. don't write 100 mb in one C call, but split it to 4
kb blocks)

what you will say?

Best regards,
 Bulat                            mailto:Bulat.Ziganshin at gmail.com
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Locking.hs
Type: application/octet-stream
Size: 6476 bytes
Desc: not available
Url : http://www.haskell.org//pipermail/haskell-cafe/attachments/20060504/5a8229a1/Locking.obj
-------------- next part --------------
A non-text attachment was scrubbed...
Name: b.hs
Type: application/octet-stream
Size: 418 bytes
Desc: not available
Url : http://www.haskell.org//pipermail/haskell-cafe/attachments/20060504/5a8229a1/b.obj
-------------- next part --------------
A non-text attachment was scrubbed...
Name: a.hs
Type: application/octet-stream
Size: 606 bytes
Desc: not available
Url : http://www.haskell.org//pipermail/haskell-cafe/attachments/20060504/5a8229a1/a.obj

More information about the Haskell-Cafe mailing list