
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@haskell.org [mailto:glasgow-haskell-users-bounces@haskell.org] On | Behalf Of Scott Dillard | Sent: 14 May 2008 00:13 | To: glasgow-haskell-users@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@haskell.org | http://www.haskell.org/mailman/listinfo/glasgow-haskell-users