Efficiency of using field labels vs pattern matching

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

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:Bulat.Ziganshin@gmail.com

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

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.

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
participants (3)
-
Brian Hulley
-
Bulat Ziganshin
-
Sigbjorn Finne