laziness, memoization and inlining
Simon Peyton-Jones
simonpj at microsoft.com
Wed May 14 03:48:00 EDT 2008
Scott
| I'm experiencing some undesirable performance behavior, I suspect from
| inlining things that shouldn't be, defeating my memoization attempts.
This is bad, very bad. I think Don is right. I believe the following is happening. In your main program you have
do let mesh = memoMesh rawMesh
display :: IO ()
display = draw mesh >> stuff
setDisplayCallback display
glutMainLoop
So the effect is that 'display' is performed many times, by glutMainLoop.
Now 'display' is seen by GHC thus:
display = \s -> draw mesh s >> stuff
The "\s" says "given the state of the world, s, I'll draw the mesh on it". The "state hack" makes GHC think that a "\s" will only ever be called once (which is utterly false in this case), so it can inline mesh=memoMesh rawMesh. Result disaster.
I bet you'll be fine if you compile your main module with -fno-state-hack.
But I should fix this, somehow. It's coming up too often to justify the hack. Can you make a Trac bug report, and include your message and this one?
Thanks for reporting it.
Simon
| -----Original Message-----
| From: glasgow-haskell-users-bounces at haskell.org [mailto:glasgow-haskell-users-bounces at haskell.org] On
| Behalf Of Scott Dillard
| Sent: 14 May 2008 00:13
| To: glasgow-haskell-users at haskell.org
| Subject: laziness, memoization and inlining
|
| 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?
|
| Scott
| _______________________________________________
| Glasgow-haskell-users mailing list
| Glasgow-haskell-users at haskell.org
| http://www.haskell.org/mailman/listinfo/glasgow-haskell-users
More information about the Glasgow-haskell-users
mailing list