Skip to content

Split module interface rule into interface and code outputs - #5057

Merged
fendor merged 1 commit into
haskell:masterfrom
crtschin:crtschin/iface-only-cutoff-layered
Aug 27, 2026
Merged

Split module interface rule into interface and code outputs#5057
fendor merged 1 commit into
haskell:masterfrom
crtschin:crtschin/iface-only-cutoff-layered

Conversation

@crtschin

@crtschin crtschin commented Aug 26, 2026

Copy link
Copy Markdown
Collaborator

I initially wanted to optimize interaction times and ran benchmarks. This led me to the high rulesBuilt number for the simple edit benchmark. Using #5055, shows the built rules being dominated by (re)loading module interfaces. This despite the edit only modifying a string in the benchmark, which doesn't touch the module's interface.

Investigating this further leads to the fingerprint used to determine when to reload a modules interface. The fingerprint is defined as:

hiFileFingerPrint :: HiFileResult -> ByteString
hiFileFingerPrint HiFileResult{..} = hirIfaceFp hirIface <> fromMaybe "" hirCoreFp

What I think's wrong here is that the core file is more susceptible to being invalidated despite no interface changes occurring. A module's interface being invalidated is infectious, and dirties a module's transitive reverse closure.

This PR fixes it by being explicit on which of a module's artefacts a rule depends on. GetLinkable can reference the on-disk core file, while GetModIface can stick to strictly a module's ABI, primarily so GetModIface can ignore non-ABI changes.


Running the edit bench experiment on both MultiLayerModules and MultiLayerModulesNoTH using cabal bench and the rule build histogram counts from #5055.

before with TH
+--------------------------------+------+
|              rule              | edit |
+--------------------------------+------+
| GhcSessionDeps                 | 601  |
| GetModIfaceFromDisk            | 599  |
| GenerateCore                   | 3    |
| GetHieAst                      | 3    |
| TypeCheck                      | 3    |
| GetModIface                    | 2    |
| GetParsedModule                | 2    |
| NonLocalCompletions            | 2    |
| GetFileContents                | 1    |
| GetModSummary                  | 1    |
| GetModSummaryWithoutTimestamps | 1    |
| GetModificationTime            | 1    |
| TOTAL                          | 1219 |
+--------------------------------+------+
+------+---------+---------+---------+-------+-------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
| name | success | samples | startup | setup | userT | delayedT | 1stBuildT | avgPerRespT | totalT | rulesBuilt | rulesChanged | rulesVisited | rulesTotal | ruleEdges | ghcRebuilds |
+------+---------+---------+---------+-------+-------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
| edit | True    | 50      | 0.24s   | 0.00s | 1m05s | 2.07s    | 2.06s     | 0.44s       | 1m07s  | 1219       | 619          | 5141         | 11556      | 118622    | 141         |
+------+---------+---------+---------+-------+-------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
after with TH
+--------------------------------+------+
|              rule              | edit |
+--------------------------------+------+
| GenerateCore                   | 3    |
| GetHieAst                      | 3    |
| GetParsedModule                | 3    |
| TypeCheck                      | 3    |
| GetModArtefacts                | 2    |
| GetModIface                    | 2    |
| GetFileContents                | 1    |
| GetModSummary                  | 1    |
| GetModSummaryWithoutTimestamps | 1    |
| GetModificationTime            | 1    |
| TOTAL                          | 20   |
+--------------------------------+------+
+------+---------+---------+---------+-------+--------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
| name | success | samples | startup | setup | userT  | delayedT | 1stBuildT | avgPerRespT | totalT | rulesBuilt | rulesChanged | rulesVisited | rulesTotal | ruleEdges | ghcRebuilds |
+------+---------+---------+---------+-------+--------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
| edit | True    | 50      | 0.26s   | 0.00s | 14.34s | 2.16s    | 2.17s     | 0.10s       | 16.51s | 20         | 17           | 5782         | 12197      | 143303    | 101         |
+------+---------+---------+---------+-------+--------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
before without TH
+--------------------------------+------+
|              rule              | edit |
+--------------------------------+------+
| GenerateCore                   | 3    |
| GetHieAst                      | 3    |
| GetParsedModule                | 3    |
| TypeCheck                      | 3    |
| GetModIface                    | 2    |
| GetFileContents                | 1    |
| GetModSummary                  | 1    |
| GetModSummaryWithoutTimestamps | 1    |
| GetModificationTime            | 1    |
| TOTAL                          | 18   |
+--------------------------------+------+
+------+---------+---------+---------+-------+--------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
| name | success | samples | startup | setup | userT  | delayedT | 1stBuildT | avgPerRespT | totalT | rulesBuilt | rulesChanged | rulesVisited | rulesTotal | ruleEdges | ghcRebuilds |
+------+---------+---------+---------+-------+--------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
| edit | True    | 50      | 0.30s   | 0.00s | 17.02s | 2.37s    | 2.39s     | 0.12s       | 19.39s | 18         | 15           | 5142         | 11556      | 118620    | 101         |
+------+---------+---------+---------+-------+--------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
after without TH
+--------------------------------+------+
|              rule              | edit |
+--------------------------------+------+
| GenerateCore                   | 3    |
| GetHieAst                      | 3    |
| GetParsedModule                | 3    |
| TypeCheck                      | 3    |
| GetModArtefacts                | 2    |
| GetFileContents                | 1    |
| GetModSummary                  | 1    |
| GetModSummaryWithoutTimestamps | 1    |
| GetModificationTime            | 1    |
| TOTAL                          | 18   |
+--------------------------------+------+
+------+---------+---------+---------+-------+--------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
| name | success | samples | startup | setup | userT  | delayedT | 1stBuildT | avgPerRespT | totalT | rulesBuilt | rulesChanged | rulesVisited | rulesTotal | ruleEdges | ghcRebuilds |
+------+---------+---------+---------+-------+--------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
| edit | True    | 50      | 0.26s   | 0.00s | 14.58s | 2.14s    | 2.13s     | 0.10s       | 16.72s | 18         | 15           | 5142         | 12197      | 143301    | 101         |
+------+---------+---------+---------+-------+--------+----------+-----------+-------------+--------+------------+--------------+--------------+------------+-----------+-------------+
csv comparison results
Example              , Version, Configuration, name, success, samples, startup, setup, userT  , delayedT, 1stBuildT, avgPerRespT, totalT , rulesBuilt, rulesChanged, rulesVisited, rulesTotal, ruleEdges, ghcRebuilds, maxResidency, allocatedBytes
MultiLayerModules    , after  , All          , edit, True   , 0.00%  , 8.33%  , NA   , -78.04%, 4.35%   , 5.34%    , -77.27%    , -75.50%, -98.36%   , -97.25%     , 12.47%      , 5.55%     , 20.81%   , -28.37%    , -47.18%     , -84.20%       
MultiLayerModulesNoTH, after  , All          , edit, True   , 0.00%  , -13.33%, NA   , -14.34%, -9.70%  , -10.88%  , -16.67%    , -13.77%, 0.00%     , 0.00%       , 0.00%       , 5.55%     , 20.81%   , 0.00%      , -1.28%      , 0.74%         
                     

@crtschin crtschin changed the title WIP: Split module interface rule into interface and code outputs Split module interface rule into interface and code outputs Aug 27, 2026
@crtschin
crtschin marked this pull request as ready for review August 27, 2026 00:10
@crtschin
crtschin requested a review from wz1000 as a code owner August 27, 2026 00:10

@wz1000 wz1000 left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Very nice results, this makes a lot of sense.

@fendor fendor left a comment

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

LGTM, thank you!

@fendor
fendor merged commit 6ce83e2 into haskell:master Aug 27, 2026
57 of 59 checks passed
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants