
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