diff options
author | Bartosz Nitka <niteria@gmail.com> | 2017-05-15 04:14:01 -0700 |
---|---|---|
committer | Bartosz Nitka <niteria@gmail.com> | 2017-05-15 04:14:53 -0700 |
commit | e527fc2e90958280a36645b6bd0223861cc50a55 (patch) | |
tree | b7b17398228732c53f6833d11448b91302f87d51 | |
parent | d5414dd61b540be3b3945c321065a1c70c7962ac (diff) | |
download | haskell-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.hs | 4 | ||||
-rw-r--r-- | compiler/iface/MkIface.hs | 5 | ||||
-rw-r--r-- | compiler/typecheck/TcRnDriver.hs | 14 | ||||
-rw-r--r-- | compiler/typecheck/TcRnTypes.hs | 24 | ||||
-rw-r--r-- | testsuite/tests/perf/compiler/all.T | 12 | ||||
-rwxr-xr-x | testsuite/tests/perf/compiler/genMultiLayerModules | 21 |
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 |