Bools are not unboxed

Carsten Schultz carsten at codimi.de
Sun Oct 3 10:03:55 EDT 2004


Hi Tomasz!

On Sun, Oct 03, 2004 at 03:07:01PM +0200, Tomasz Zielonka wrote:
> Hello!
> 
> I was playing with monadic looping a'la replicateM_ and I created this
> function:
> 
>     for :: Int -> IO () -> IO ()
>     for 0 _ = return ()
>     for n x = x >> for (n - 1) x
> 
> Compiled with -O2, it is really fast and makes no unnecessary
> allocations.

Yes, good code:

T.$wfor =
    \r [ww w w1]
        case ww of ds {
          __DEFAULT ->
              case w w1 of wild {
                GHC.Prim.(#,#) new_s a41 ->
                    case -# [ds 1] of sat_s1ZG {
                      __DEFAULT -> T.$wfor sat_s1ZG w new_s;
                    };
              };
          0 -> GHC.Prim.(#,#) [w1 GHC.Base.()];
        };
SRT(T.$wfor): []
T.for =
    \r [w w1 w2] case w of w3 { GHC.Base.I# ww -> T.$wfor ww w1 w2; };
SRT(T.for): []

> So I made another version:
> 
>     for :: Int -> IO () -> IO ()
>     for n x | n > 0     = x >> for (n - 1) x
> 	    | otherwise = return ()
> 
> To my surprise, it was much slower and made many allocations:
[...
> Then I noticed the cause: 
>     GHC.Prim.<# returns a boxed, heap allocated Bool, and so do other
>     primitive comparison operators.

That's not really the cause.  A function returning a boxed value does
not necessarily have to allocate it, it is just a vectored return
afaik.

The code is:

T.$wfor' =
    \r [ww w]
        case ># [ww 0] of wild {
          GHC.Base.True ->
              let {
                k = \u []
                        case -# [ww 1] of sat_s1Z9 {
                          __DEFAULT -> T.$wfor' sat_s1Z9 w;
                        }; } in
              let {
                sat_s20d =
                    \r [eta]
                        case w eta of wild1 
			    { GHC.Prim.(#,#) new_s a41 -> k new_s; };
              } in  sat_s20d;
          GHC.Base.False -> lvl4;
        };
SRT(T.$wfor'): []
T.for' =
    \r [w w1] case w of w2 { GHC.Base.I# ww -> T.$wfor' ww w1; };
SRT(T.for'): []

The culprit is `let { k = \u ... }'.  The cause seems to be that eta
expansion is done at the wrong place, I do not know why.  The code we
would want is

T.$wfor4 =
    \r [ww w w1]
        case ># [ww 0] of wild {
          GHC.Base.True ->
              case w w1 of wild1 {
                GHC.Prim.(#,#) new_s a41 ->
                    case -# [ww 1] of sat_s1Y0 {
                      __DEFAULT -> T.$wfor4 sat_s1Y0 w new_s;
                    };
              };
          GHC.Base.False -> GHC.Prim.(#,#) [w1 GHC.Base.()];
        };
SRT(T.$wfor4): []
T.for4 =
    \r [w w1 w2] case w of w3 { GHC.Base.I# ww -> T.$wfor4 ww w1 w2; };
SRT(T.for4): []

(Notice that $wfor again take three arguments, the last one being the
state.)

Actually, this is produced by the following, although I have no idea
why.  Just the optimizer working unpredictably, I guess.

for4 :: Int -> IO () -> IO ()
for4 n x = if n `gt` 0 == 0 then return () else x >> (for4 (n-1) x)

gt :: Int -> Int -> Int
gt x y = if x > y then 1 else 0

If you test it, it should be fast.

BTW, although counting upwards (and not solving the problem
generally), the following is ok too:

for2 :: Int -> IO () -> IO ()
for2 n x = sequence_ [x | i <- [1..n]]

T.lvl = \r [s] GHC.Prim.(#,#) [s GHC.Base.()];
SRT(T.lvl): []
T.$wfor2 =
    \r [ww w]
        case ># [1 ww] of wild {
          GHC.Base.True -> T.lvl;
          GHC.Base.False ->
              let {
                go10 =
                    \r [x1 eta]
                        case w eta of wild1 {
                          GHC.Prim.(#,#) new_s a41 ->
                              case ==# [x1 ww] of wild11 {
                                GHC.Base.True -> 
				    GHC.Prim.(#,#) [new_s GHC.Base.()];
                                GHC.Base.False ->
                                    case +# [x1 1] of sat_s1XA {
                                      __DEFAULT -> go10 sat_s1XA new_s;
                                    };
                              };
                        };
              } in  go10 1;
        };
SRT(T.$wfor2): []

T.for2 =
    \r [w w1] case w of w2 { GHC.Base.I# ww -> T.$wfor2 ww w1; };
SRT(T.for2): []


Playing with the code generated by ghc is a great way to waste time
for me.  Wait until you have found the RULES-pragma :-)

Have fun,

Carsten

-- 
Carsten Schultz (2:38, 33:47), FB Mathematik, FU Berlin
http://carsten.codimi.de/
PGP/GPG key on the pgp.net key servers, 
fingerprint on my home page.
-------------- next part --------------
A non-text attachment was scrubbed...
Name: not available
Type: application/pgp-signature
Size: 189 bytes
Desc: not available
Url : http://www.haskell.org//pipermail/glasgow-haskell-users/attachments/20041003/3b54caba/attachment.bin


More information about the Glasgow-haskell-users mailing list