laziness, memoization and inlining

Scott Dillard sedillard at
Tue May 13 19:12:30 EDT 2008

Hi Everybody,

I'm experiencing some undesirable performance behavior, I suspect from
inlining things that shouldn't be, defeating my memoization attempts.
I've been experimenting with purely functional 3D modeling code, so a
mesh is (initially) something like

> type Mesh = Map (Int,Int) (Int,Int)

that is, a function from from an edge to the next edge around the
face, where an edge is a pair of Ints (the vertices.)

This nice and pure and everything, but its slow to read from. So I
have another immutable pointer-based representation

> data Edge = Edge { edgeOrg :: Int , edgeSym :: Edge , edgeNext :: Edge }

which is something like a half-edge, for those familiar with such
things. Its basically a big net made of circular lists that are tied
together. I do the knot tying stuff to create this thing,

> memoMesh :: Map (Int,Int) (Int,Int) -> Edge Int
> memoMesh nexts = head $ Map.elems ties
>   where
>     ties = Map.mapWithKey (\ij _ -> make ij) nexts
>     lookup ij = trace "hello" $ fromJust $ Map.lookup ij ties
>     make ij@(i,j) = Edge i (lookup (j,i)) (lookup . fromJust $ Map.lookup ij nexts)

The program first loads the model file and creates the Edge-based mesh
using the memoMesh function. The result is then captured in the
closure for the rendering callback in GLUT. When I compile with -O0 I
see the "hello" traces only during the first drawing. Subsequent
redraws are fast and output no traces. When I compile with -O1 or -O2,
the traces get output on every redraw, and its very slow. I suspect
all of the calls which create the mesh are inlined into the rendering
callback, effectively rebuilding the mesh on every draw.

I've tried littering NOINLINE pragmas all around, to no avail.

The main function is something like

> main = do
>   initGlut ...
>   rawMesh <- loadMeshFile ...
>   let
>     mesh = memoMesh rawMesh
>     otherstuff = ...
>     display =
>       draw mesh >> amongOtherThings
>   displayCallback $= display
>   glutMainLoop

Can someone help me understand what's going on here? Is there a nice
solution to this, hopefully a single strategic pragma or something?


More information about the Glasgow-haskell-users mailing list