Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Consider avoiding reader for SetM #1102

Open
meooow25 opened this issue Jan 31, 2025 · 0 comments · May be fixed by #1103
Open

Consider avoiding reader for SetM #1102

meooow25 opened this issue Jan 31, 2025 · 0 comments · May be fixed by #1103
Labels

Comments

@meooow25
Copy link
Contributor

meooow25 commented Jan 31, 2025

newtype SetM s a = SetM { runSetM :: STUArray s Vertex Bool -> ST s a }

We currently use a reader for the GHC version of SetM, which means the recursive go function passes around the visited array all the time. This generates Core like

letrec {
  $sgo_sbDD [Occ=LoopBreaker,
             Dmd=SC(S,C(1,C(1,C(1,C(1,C(1,!P(L,L)))))))]
    :: Int#
       -> Int#
       -> Int#
       -> MutableByteArray# RealWorld
       -> [Int]
       -> State# RealWorld
       -> (# State# RealWorld, [Tree Vertex] #)
  [LclId[StrictWorker([~, ~, ~, ~, !])],
   Arity=6,
   Str=<L><L><L><L><1L><L>,
   Unf=OtherCon []]
  $sgo_sbDD
    = \ ...

GHC unboxes the array, which means four static arguments.

If we wrote dfs manually, we would not likely not write it in this manner. We would create the array once, declare the recursive go with it in scope, and simply refer to it.

dfs g vs = runST $ do
  m <- newArray ...
  let go = ...
           ... use m ...
  go vs

So why don't we abstract that?

-run          :: Bounds -> (forall s. SetM s a) -> a
-contains     :: Vertex -> SetM s Bool
-include      :: Vertex -> SetM s ()
...
+run :: Bounds -> (forall s. (Int -> ST s Bool) -> (Int -> ST s ()) -> ST s a) -> a
+run bnds f = runST $ do
+  m <- newArray bnds False
+  f (readArray m) (\v -> writeArray m v True)

 dfs :: Graph -> [Vertex] -> [Tree Vertex]
-dfs g vs0 = run (bounds g) $ go vs0
-  where
+dfs g vs0 = run (bounds g) $ \contains include ->
+  let
     go [] = pure []
...

Now our go is simply in ST and does not pass around the array.

The IntSet based implementation for non-GHC remains largely unchanged.

Effect on performance: Nothing significant, at least on my machine. A few benchmarks seem to be faster by 5-10%. So this is mostly motivated by me wanting to improve what it compiles to, but I will share proper numbers once the changes are ready for a PR.

@meooow25 meooow25 added the Graph label Jan 31, 2025
@meooow25 meooow25 linked a pull request Feb 1, 2025 that will close this issue
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
Projects
None yet
Development

Successfully merging a pull request may close this issue.

1 participant