summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorBartosz Nitka <niteria@gmail.com>2017-05-15 04:14:01 -0700
committerBartosz Nitka <niteria@gmail.com>2017-05-15 04:14:53 -0700
commite527fc2e90958280a36645b6bd0223861cc50a55 (patch)
treeb7b17398228732c53f6833d11448b91302f87d51
parentd5414dd61b540be3b3945c321065a1c70c7962ac (diff)
downloadhaskell-e527fc2e90958280a36645b6bd0223861cc50a55.tar.gz
Stress test for nested module hierarchies
I'm optimizing a case that is well approximated by multiple layers of modules where every module in a layer imports all the modules in the layer below. It turns out I regressed performance on such cases in 7fea7121. I'm adding a test case to track improvements and prevent future regressions. Test Plan: ./validate Reviewers: simonmar, austin, bgamari Reviewed By: simonmar Subscribers: rwbarton, thomie Differential Revision: https://phabricator.haskell.org/D3575
-rw-r--r--compiler/deSugar/DsUsage.hs4
-rw-r--r--compiler/iface/MkIface.hs5
-rw-r--r--compiler/typecheck/TcRnDriver.hs14
-rw-r--r--compiler/typecheck/TcRnTypes.hs24
-rw-r--r--testsuite/tests/perf/compiler/all.T12
-rwxr-xr-xtestsuite/tests/perf/compiler/genMultiLayerModules21
6 files changed, 60 insertions, 20 deletions
diff --git a/compiler/deSugar/DsUsage.hs b/compiler/deSugar/DsUsage.hs
index 25d8254f55..8158a8e122 100644
--- a/compiler/deSugar/DsUsage.hs
+++ b/compiler/deSugar/DsUsage.hs
@@ -16,7 +16,7 @@ import Module
import Outputable
import Util
import UniqSet
-import UniqDFM
+import UniqFM
import Fingerprint
import Maybes
@@ -37,7 +37,7 @@ mkDependencies
= do
-- Template Haskell used?
th_used <- readIORef th_var
- let dep_mods = eltsUDFM (delFromUDFM (imp_dep_mods imports)
+ let dep_mods = modDepsElts (delFromUFM (imp_dep_mods imports)
(moduleName mod))
-- M.hi-boot can be in the imp_dep_mods, but we must remove
-- it before recording the modules on which this one depends!
diff --git a/compiler/iface/MkIface.hs b/compiler/iface/MkIface.hs
index 14749c7617..4968c2921c 100644
--- a/compiler/iface/MkIface.hs
+++ b/compiler/iface/MkIface.hs
@@ -105,7 +105,6 @@ import Binary
import Fingerprint
import Exception
import UniqSet
-import UniqDFM
import Packages
import Control.Monad
@@ -1220,14 +1219,14 @@ checkVersions hsc_env mod_summary iface
-- We do this regardless of compilation mode, although in --make mode
-- all the dependent modules should be in the HPT already, so it's
-- quite redundant
- ; updateEps_ $ \eps -> eps { eps_is_boot = udfmToUfm mod_deps }
+ ; updateEps_ $ \eps -> eps { eps_is_boot = mod_deps }
; recomp <- checkList [checkModUsage this_pkg u | u <- mi_usages iface]
; return (recomp, Just iface)
}}}}}}
where
this_pkg = thisPackage (hsc_dflags hsc_env)
-- This is a bit of a hack really
- mod_deps :: DModuleNameEnv (ModuleName, IsBootInterface)
+ mod_deps :: ModuleNameEnv (ModuleName, IsBootInterface)
mod_deps = mkModDeps (dep_mods (mi_deps iface))
-- | Check if an hsig file needs recompilation because its
diff --git a/compiler/typecheck/TcRnDriver.hs b/compiler/typecheck/TcRnDriver.hs
index 4011e38856..6755985a93 100644
--- a/compiler/typecheck/TcRnDriver.hs
+++ b/compiler/typecheck/TcRnDriver.hs
@@ -101,7 +101,7 @@ import ErrUtils
import Id
import VarEnv
import Module
-import UniqDFM
+import UniqFM
import Name
import NameEnv
import NameSet
@@ -118,7 +118,7 @@ import Class
import BasicTypes hiding( SuccessFlag(..) )
import CoAxiom
import Annotations
-import Data.List ( sortBy )
+import Data.List ( sortBy, sort )
import Data.Ord
import FastString
import Maybes
@@ -306,7 +306,7 @@ tcRnImports hsc_env import_decls
= do { (rn_imports, rdr_env, imports, hpc_info) <- rnImports import_decls ;
; this_mod <- getModule
- ; let { dep_mods :: DModuleNameEnv (ModuleName, IsBootInterface)
+ ; let { dep_mods :: ModuleNameEnv (ModuleName, IsBootInterface)
; dep_mods = imp_dep_mods imports
-- We want instance declarations from all home-package
@@ -317,7 +317,7 @@ tcRnImports hsc_env import_decls
-- modules batch (@--make@) compiled before this one, but
-- which are not below this one.
; want_instances :: ModuleName -> Bool
- ; want_instances mod = mod `elemUDFM` dep_mods
+ ; want_instances mod = mod `elemUFM` dep_mods
&& mod /= moduleName this_mod
; (home_insts, home_fam_insts) = hptInstances hsc_env
want_instances
@@ -326,7 +326,7 @@ tcRnImports hsc_env import_decls
-- Record boot-file info in the EPS, so that it's
-- visible to loadHiBootInterface in tcRnSrcDecls,
-- and any other incrementally-performed imports
- ; updateEps_ (\eps -> eps { eps_is_boot = udfmToUfm dep_mods }) ;
+ ; updateEps_ (\eps -> eps { eps_is_boot = dep_mods }) ;
-- Update the gbl env
; updGblEnv ( \ gbl ->
@@ -2532,10 +2532,10 @@ pprTcGblEnv (TcGblEnv { tcg_type_env = type_env,
, vcat (map ppr rules)
, vcat (map ppr vects)
, text "Dependent modules:" <+>
- pprUDFM (imp_dep_mods imports) ppr
+ pprUFM (imp_dep_mods imports) (ppr . sort)
, text "Dependent packages:" <+>
ppr (S.toList $ imp_dep_pkgs imports)]
- where -- The use of sortBy is just to reduce unnecessary
+ where -- The use of sort is just to reduce unnecessary
-- wobbling in testsuite output
ppr_types :: TypeEnv -> SDoc
diff --git a/compiler/typecheck/TcRnTypes.hs b/compiler/typecheck/TcRnTypes.hs
index 7aef4bb8a4..40d4f78bc0 100644
--- a/compiler/typecheck/TcRnTypes.hs
+++ b/compiler/typecheck/TcRnTypes.hs
@@ -35,7 +35,7 @@ module TcRnTypes(
-- Renamer types
ErrCtxt, RecFieldEnv,
ImportAvails(..), emptyImportAvails, plusImportAvails,
- WhereFrom(..), mkModDeps,
+ WhereFrom(..), mkModDeps, modDepsElts,
-- Typechecker types
TcTypeEnv, TcIdBinderStack, TcIdBinder(..),
@@ -169,7 +169,7 @@ import Module
import SrcLoc
import VarSet
import ErrUtils
-import UniqDFM
+import UniqFM
import UniqSupply
import BasicTypes
import Bag
@@ -189,6 +189,7 @@ import qualified Control.Monad.Fail as MonadFail
import Data.Set ( Set )
import qualified Data.Set as S
+import Data.List ( sort )
import Data.Map ( Map )
import Data.Dynamic ( Dynamic )
import Data.Typeable ( TypeRep )
@@ -1240,7 +1241,7 @@ data ImportAvails
-- different packages. (currently not the case, but might be in the
-- future).
- imp_dep_mods :: DModuleNameEnv (ModuleName, IsBootInterface),
+ imp_dep_mods :: ModuleNameEnv (ModuleName, IsBootInterface),
-- ^ Home-package modules needed by the module being compiled
--
-- It doesn't matter whether any of these dependencies
@@ -1282,14 +1283,21 @@ data ImportAvails
}
mkModDeps :: [(ModuleName, IsBootInterface)]
- -> DModuleNameEnv (ModuleName, IsBootInterface)
-mkModDeps deps = foldl add emptyUDFM deps
+ -> ModuleNameEnv (ModuleName, IsBootInterface)
+mkModDeps deps = foldl add emptyUFM deps
where
- add env elt@(m,_) = addToUDFM env m elt
+ add env elt@(m,_) = addToUFM env m elt
+
+modDepsElts
+ :: ModuleNameEnv (ModuleName, IsBootInterface)
+ -> [(ModuleName, IsBootInterface)]
+modDepsElts = sort . nonDetEltsUFM
+ -- It's OK to use nonDetEltsUFM here because sorting by module names
+ -- restores determinism
emptyImportAvails :: ImportAvails
emptyImportAvails = ImportAvails { imp_mods = emptyModuleEnv,
- imp_dep_mods = emptyUDFM,
+ imp_dep_mods = emptyUFM,
imp_dep_pkgs = S.empty,
imp_trust_pkgs = S.empty,
imp_trust_own_pkg = False,
@@ -1312,7 +1320,7 @@ plusImportAvails
imp_trust_pkgs = tpkgs2, imp_trust_own_pkg = tself2,
imp_orphs = orphs2, imp_finsts = finsts2 })
= ImportAvails { imp_mods = plusModuleEnv_C (++) mods1 mods2,
- imp_dep_mods = plusUDFM_C plus_mod_dep dmods1 dmods2,
+ imp_dep_mods = plusUFM_C plus_mod_dep dmods1 dmods2,
imp_dep_pkgs = dpkgs1 `S.union` dpkgs2,
imp_trust_pkgs = tpkgs1 `S.union` tpkgs2,
imp_trust_own_pkg = tself1 || tself2,
diff --git a/testsuite/tests/perf/compiler/all.T b/testsuite/tests/perf/compiler/all.T
index 360bef4702..c90378bbb3 100644
--- a/testsuite/tests/perf/compiler/all.T
+++ b/testsuite/tests/perf/compiler/all.T
@@ -1096,3 +1096,15 @@ test('T13379',
],
compile,
[''])
+
+test('MultiLayerModules',
+ [ compiler_stats_num_field('bytes allocated',
+ [(wordsize(64), 6956533312, 10),
+ # initial: 12139116496
+ # 2017-05-12: 6956533312 Revert "Use a deterministic map for imp_dep_mods"
+ ]),
+ pre_cmd('./genMultiLayerModules'),
+ extra_files(['genMultiLayerModules']),
+ ],
+ multimod_compile,
+ ['MultiLayerModules', '-v0'])
diff --git a/testsuite/tests/perf/compiler/genMultiLayerModules b/testsuite/tests/perf/compiler/genMultiLayerModules
new file mode 100755
index 0000000000..b98c481166
--- /dev/null
+++ b/testsuite/tests/perf/compiler/genMultiLayerModules
@@ -0,0 +1,21 @@
+#!/bin/bash
+# Generate $DEPTH layers of modules with $WIDTH modules on each layer
+# Every module on layer N imports all the modules on layer N-1
+# MultiLayerModules.hs imports all the modules from the last layer
+DEPTH=15
+WIDTH=40
+for i in $(seq -w 1 $WIDTH); do
+ echo "module DummyLevel0M$i where" > DummyLevel0M$i.hs;
+done
+for l in $(seq 1 $DEPTH); do
+ for i in $(seq -w 1 $WIDTH); do
+ echo "module DummyLevel${l}M$i where" > DummyLevel${l}M$i.hs;
+ for j in $(seq -w 1 $WIDTH); do
+ echo "import DummyLevel$((l-1))M$j" >> DummyLevel${l}M$i.hs;
+ done
+ done
+done
+echo "module MultiLayerModules where" > MultiLayerModules.hs
+for j in $(seq -w 1 $WIDTH); do
+ echo "import DummyLevel${DEPTH}M$j" >> MultiLayerModules.hs;
+done