summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartosz Nitka <niteria@gmail.com>2018-02-02 06:55:19 -0800
committerBartosz Nitka <niteria@gmail.com>2018-02-06 08:43:54 -0800
commitd2511e3b61563ed3fc2c9aec2c90a4156373a24c (patch)
tree598cead261f90d53a9f1efa9a326c467ebe4c30f
parent326df5d137ec1a556d77248e16f55e41b7374b1e (diff)
downloadhaskell-d2511e3b61563ed3fc2c9aec2c90a4156373a24c.tar.gz
Compute the union of imp_finsts on the side
I've explained most of the rationale in a new Note. I'd happily add a test for this, but the difference is only visible in run time, allocations remain more or less the same. FWIW running `generateModules` from #14693 with DEPTH=16, WIDTH=30 finishes in `23s` before, and `11s` after. Test Plan: ./validate Reviewers: simonpj, simonmar, bgamari Reviewed By: simonpj Subscribers: rwbarton, thomie, carter GHC Trac Issues: #14693 Differential Revision: https://phabricator.haskell.org/D4369
-rw-r--r--compiler/rename/RnNames.hs67
1 files changed, 61 insertions, 6 deletions
diff --git a/compiler/rename/RnNames.hs b/compiler/rename/RnNames.hs
index ae3f75b1d0..769b34e45b 100644
--- a/compiler/rename/RnNames.hs
+++ b/compiler/rename/RnNames.hs
@@ -177,16 +177,71 @@ rnImports imports = do
return (decls, rdr_env, imp_avails, hpc_usage)
where
+ -- See Note [Combining ImportAvails]
combine :: [(LImportDecl GhcRn, GlobalRdrEnv, ImportAvails, AnyHpcUsage)]
-> ([LImportDecl GhcRn], GlobalRdrEnv, ImportAvails, AnyHpcUsage)
- combine = foldr plus ([], emptyGlobalRdrEnv, emptyImportAvails, False)
-
- plus (decl, gbl_env1, imp_avails1,hpc_usage1)
- (decls, gbl_env2, imp_avails2,hpc_usage2)
+ combine ss =
+ let (decls, rdr_env, imp_avails, hpc_usage, finsts) = foldr
+ plus
+ ([], emptyGlobalRdrEnv, emptyImportAvails, False, emptyModuleSet)
+ ss
+ in (decls, rdr_env, imp_avails { imp_finsts = moduleSetElts finsts },
+ hpc_usage)
+
+ plus (decl, gbl_env1, imp_avails1, hpc_usage1)
+ (decls, gbl_env2, imp_avails2, hpc_usage2, finsts_set)
= ( decl:decls,
gbl_env1 `plusGlobalRdrEnv` gbl_env2,
- imp_avails1 `plusImportAvails` imp_avails2,
- hpc_usage1 || hpc_usage2 )
+ imp_avails1' `plusImportAvails` imp_avails2,
+ hpc_usage1 || hpc_usage2,
+ extendModuleSetList finsts_set new_finsts )
+ where
+ imp_avails1' = imp_avails1 { imp_finsts = [] }
+ new_finsts = imp_finsts imp_avails1
+
+{-
+Note [Combine ImportAvails]
+~~~~~~~~~~~~~~~~~~~~~~~~~~~
+imp_finsts in ImportAvails is a list of family instance modules
+transitively depended on by an import. imp_finsts for a currently
+compiled module is a union of all the imp_finsts of imports.
+Computing the union of two lists of size N is O(N^2) and if we
+do it to M imports we end up with O(M*N^2). That can get very
+expensive for bigger module hierarchies.
+
+Union can be optimized to O(N log N) if we use a Set.
+imp_finsts is converted back and forth between dep_finsts, so
+changing a type of imp_finsts means either paying for the conversions
+or changing the type of dep_finsts as well.
+
+I've measured that the conversions would cost 20% of allocations on my
+test case, so that can be ruled out.
+
+Changing the type of dep_finsts forces checkFamInsts to
+get the module lists in non-deterministic order. If we wanted to restore
+the deterministic order, we'd have to sort there, which is an additional
+cost. As far as I can tell, using a non-deterministic order is fine there,
+but that's a brittle nonlocal property which I'd like to avoid.
+
+Additionally, dep_finsts is read from an interface file, so its "natural"
+type is a list. Which makes it a natural type for imp_finsts.
+
+Since rnImports.combine is really the only place that would benefit from
+it being a Set, it makes sense to optimize the hot loop in rnImports.combine
+without changing the representation.
+
+So here's what we do: instead of naively merging ImportAvails with
+plusImportAvails in a loop, we make plusImportAvails merge empty imp_finsts
+and compute the union on the side using Sets. When we're done, we can
+convert it back to a list. One nice side effect of this approach is that
+if there's a lot of overlap in the imp_finsts of imports, the
+Set doesn't really need to grow and we don't need to allocate.
+
+Running generateModules from Trac #14693 with DEPTH=16, WIDTH=30 finishes in
+23s before, and 11s after.
+-}
+
+
-- | Given a located import declaration @decl@ from @this_mod@,
-- calculate the following pieces of information: