Efficiency of using field labels vs pattern matching

Previous Topic Next Topic
 
classic Classic list List threaded Threaded
5 messages Options
Reply | Threaded
Open this post in threaded view
|

Efficiency of using field labels vs pattern matching

Brian Hulley
Hi,
I'm wondering if it is more efficient to pattern match against the
components of a record instead of using the field functions, though I'd
prefer to just use the field functions.

For example, if I write:

    data R = R {_xRef :: !(IORef Int)}

    foo :: Int -> R -> IO ()
    foo i R{_xRef = xRef} = writeIORef xRef i

is the above more efficient than:

    foo i r = writeIORef (_xRef r) i

I know this is probably a bit silly to worry about the overhead of an extra
function call etc but I've at the moment got a lot of code using the first
version and I think it would look much neater just using field labels but I
don't want to modify it if it will be less efficient since speed is quite
critical here.

Thanks, Brian.
--
Logic empowers us and Love gives us purpose.
Yet still phantoms restless for eras long past,
congealed in the present in unthought forms,
strive mightily unseen to destroy us.

http://www.metamilk.com 

_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Efficiency of using field labels vs pattern matching

Bulat Ziganshin-2
Hello Brian,

Sunday, August 20, 2006, 5:15:41 PM, you wrote:

>     data R = R {_xRef :: !(IORef Int)}

>     foo :: Int -> R -> IO ()
>     foo i R{_xRef = xRef} = writeIORef xRef i

> is the above more efficient than:

>     foo i r = writeIORef (_xRef r) i

i think that first _may_ be more efficient because of strictness
analysis. btw, if you want beter efficiency, you may use unboxed
references (http://haskell.org/haskellwiki/Library/ArrayRef)




--
Best regards,
 Bulat                            mailto:[hidden email]

_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Efficiency of using field labels vs pattern matching

Sigbjorn Finne
In reply to this post by Brian Hulley

If you let the Simplifier have a crack at your code (i.e., compile with -O
or better), the same code should be generated for the two defns of
'foo'. Try compiling with -ddump-simpl to verify.

The first version is stricter, so it'll be preferable in -Onot mode.

--sigbjorn

Brian Hulley wrote:

> Hi,
> I'm wondering if it is more efficient to pattern match against the
> components of a record instead of using the field functions, though
> I'd prefer to just use the field functions.
>
> For example, if I write:
>
>    data R = R {_xRef :: !(IORef Int)}
>
>    foo :: Int -> R -> IO ()
>    foo i R{_xRef = xRef} = writeIORef xRef i
>
> is the above more efficient than:
>
>    foo i r = writeIORef (_xRef r) i
>
> I know this is probably a bit silly to worry about the overhead of an
> extra function call etc but I've at the moment got a lot of code using
> the first version and I think it would look much neater just using
> field labels but I don't want to modify it if it will be less
> efficient since speed is quite critical here.
>
> Thanks, Brian.

_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Efficiency of using field labels vs pattern matching

Brian Hulley
Sigbjorn Finne wrote:
> If you let the Simplifier have a crack at your code (i.e., compile
> with -O or better), the same code should be generated for the two
> defns of 'foo'. Try compiling with -ddump-simpl to verify.

Thanks. I tried that and you're right. It also works well with an even more
complicated example:

    data R = R{_xRef :: !(Ref.T Int), _y :: Int}

    foo :: Bool -> R -> IO ()
    foo b r = do
        if b
            then Ref.write (_xRef r) ((_y r) * (_y r))
            else return ()
        x <- Ref.read (_xRef r)
        print x

Even though there are 4 applications of field label functions and _y is not
even a strict field, GHC -O2 manages to get both fields with a single case
statement:

    Main.foo :: GHC.Base.Bool -> Main.R -> GHC.IOBase.IO ()
    [GlobalId]
    [Arity 3
        Worker Main.$wfoo
        Str: DmdType SU(U(L)L)L]
    Main.foo = __inline_me (\ (w_s2sp :: GHC.Base.Bool)
            (w1_s2sq :: Main.R)
            (w2_s2sy :: GHC.Prim.State# GHC.Prim.RealWorld) ->
            case (# GHC.Prim.State# GHC.Prim.RealWorld, () #) w1_s2sq
            of w3_X2sB { Main.R ww_s2ss ww1_s2sw ->

                -- well done GHC!!!! :-)

            case (# GHC.Prim.State# GHC.Prim.RealWorld, () #) ww_s2ss
            of ww2_X2sI { GHC.STRef.STRef ww3_s2su ->
            Main.$wfoo w_s2sp ww3_s2su ww1_s2sw w2_s2sy
            }
            })

This is great because some of my records have about 20 fields so the pattern
matching was getting really cumbersome in the source.

BTW I made a module to make the IORef functions work with any MonadIO and
also give them better names (to use by qualified import) which I enclose
below if anyone is interested - I don't know where to put stuff like this:

----------------------------------------------------------------
-- |
-- Module: Prime.IORef
-- Author: Brian Hulley
-- License: Public Domain
--
-- Lifts (most of) Data.IORef to any MonadIO
-- This module should be used qualified so we can avoid
-- writing "IORef" everywhere
----------------------------------------------------------------
module Prime.IORef
    ( T
    , new
    , read
    , write
    , modify
    , weak
    ) where

    import Prelude hiding(read)
    import qualified Data.IORef as D
    import Control.Monad.Trans
    import System.Mem.Weak

    type T = D.IORef

    new :: MonadIO m => a -> m (T a)
    new x = liftIO $ D.newIORef x

    read :: MonadIO m => T a -> m a
    read x = liftIO $ D.readIORef x

    write :: MonadIO m => T a -> a -> m ()
    write x v = liftIO $ D.writeIORef x v

    modify :: MonadIO m => T a -> (a -> a) -> m ()
    modify x f = liftIO $ D.modifyIORef x f

    weak :: MonadIO m => T a -> m (Weak (T a))
    weak r = liftIO $ D.mkWeakIORef r (return ())

Regards, Brian.

--
Logic empowers us and Love gives us purpose.
Yet still phantoms restless for eras long past,
congealed in the present in unthought forms,
strive mightily unseen to destroy us.

http://www.metamilk.com 

_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
Reply | Threaded
Open this post in threaded view
|

Re: Efficiency of using field labels vs pattern matching

Brian Hulley
In reply to this post by Bulat Ziganshin-2
Bulat Ziganshin wrote:
> btw, if you want beter efficiency, you may use unboxed
> references (http://haskell.org/haskellwiki/Library/ArrayRef)

Thanks for the pointer to your ArrayRef library. I've downloaded it and it
will be very useful - its extremely neat that the fact that something is
stored as unboxed can be hidden from the rest of the program.

One thing I wondered was if the functional dependency in Data.Ref.Universal
from the result type to the monad is actually necessary, since this FD
prevents me adding an instance for MonadIO ie the following instance is not
valid:

    instance MonadIO m => URef m IOURef where
        -- m -> r is fine
        -- r -> m restricts m too much

Of course this isn't a big problem because I can simply define lifted
versions separately ie:

    import Data.Ref hiding(newURef, readURef, writeURef)
    import GHC.Unboxed
    import Control.Monad.Trans

    -- instance MonadIO m => URef m IOURef where

    newURef :: (Unboxed a, MonadIO m) => a -> m (IOURef a)
    newURef v = liftIO $ newIOURef v

    readURef :: (Unboxed a, MonadIO m) => IOURef a -> m a
    readURef ref = liftIO $ readIOURef ref

    writeURef :: (Unboxed a, MonadIO m) => IOURef a -> a -> m ()
    writeURef ref v = liftIO $ writeIOURef ref v

    -- test monad
    newtype SomeIO a = SomeIO {runSomeIO :: (IO a)} deriving (Monad,
MonadIO)

    foo :: SomeIO Int
    foo = do
        xRef <- newURef (57::Int)
        readURef xRef

    main = do
        x <- runSomeIO foo
        print x
        _ <- getChar
        return ()

Anyway thanks for sharing your library. I'm going to put the URef functions
above into a module so I can use the same names for URef  functions (ie
URef.T, new, read, write) as I'm already using for boxed refs.

Best regards,
Brian.
--
Logic empowers us and Love gives us purpose.
Yet still phantoms restless for eras long past,
congealed in the present in unthought forms,
strive mightily unseen to destroy us.

http://www.metamilk.com 

_______________________________________________
Glasgow-haskell-users mailing list
[hidden email]
http://www.haskell.org/mailman/listinfo/glasgow-haskell-users