[Haskell-cafe] Profiling nested case

Luke Palmer lrpalmer at gmail.com
Sat Jul 12 15:39:49 EDT 2008


On Sat, Jul 12, 2008 at 8:57 PM, Mitar <mmitar at gmail.com> wrote:
> julia4DFractal :: BasicReal -> World
> julia4DFractal param (x,y,z) = julia4D (Q (x / scale) (y / scale) (z /
> scale) param) iterations
>  where c          = (Q (-0.08) 0.0 (-0.8) (-0.03))
>        alphaBlue  = VoxelColor 0 0 (2 / scale) (2 / scale)
>        scale      = fromIntegral sceneHeight / 1.8
>        threshold  = 16
>        iterations = 100 :: Int
>        julia4D _ 0                                    = (# alphaBlue,
> 1 #) -- point is (probably) not in the set
>        julia4D q it | qMagnitudeSquared q > threshold = (# noColor, 1
> #)   -- point is in the set
>                     | otherwise                       = julia4D
> (qSquared q + c) (it - 1)
>          where distance      = scale * (qMagnitude qN) / (2 *
> (qMagnitude qN')) * log (qMagnitude qN)
>                (# qN, qN' #) = disIter q (Q 1 0 0 0) iterations
>                  where disIter qn qn' 0
>     = (# qn, qn' #)
>                        disIter qn qn' i | qMagnitudeSquared qn >
> threshold = (# qn, qn' #)
>                                         | otherwise
>     = disIter (qSquared qn + c) (2 * qn * qn') (i - 1)
>
> Please observe that distance is never used. And this is also what GHC
> warns. But the trick is that with having this part of a code in there,
> the program virtually never finishes (I killed it after 15 minutes).
> If I remove everything on and after the "where distance" line it
> finishes in 30 seconds. OK, the problem is with (# qN, qN' #), if this
> is changed to normal (qN, qN'), then it works. But to notice this ...
> This is something you have to spend a day for.

My guess is that it was premature optimization that created this bug.
Unboxed tuples are not the best answer for every situation.  They are
evaluated strictly!  Which means:

    unboxedBottom x
        | False = (# 0, 0 #)
        | otherwise = unboxedBottom x

    let (# x, y #) = unboxedBottom 0 in 42

Is an infinite loop, not 42 as you would expect.  So when you write:

    where  (# ... #) = something

You are requiring your program to evaluate 'something' regardless of
whether it is needed.

Unboxed tuples should be taken in the same vain as explicit strictness
annotations:  almost never use them, and let GHC do the work for you.
If you are in that phase where you are doing performance tweaks and
you think GHC's strictness analysis might not be picking up on some
strict behavior in your program, add the annotation.  If it makes it
faster, great; if it doesn't change things, take it out!  Best to
underconstrain your program.

But these days I try to make my programs fast by making the structure
of my program apparent to the compiler, not by forcing it to do things
in a certain way.  Admittedly making the structure of a program
apparent to the compiler is a rather subtle and brittle process.  I'm
sure people have at least brainstormed ways to help the compiler more.

Luke


More information about the Haskell-Cafe mailing list