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

Files are needlessly rechecked #4959

Closed
nad opened this issue Oct 6, 2020 · 7 comments · Fixed by #5402
Closed

Files are needlessly rechecked #4959

nad opened this issue Oct 6, 2020 · 7 comments · Fixed by #5402
Assignees
Labels
performance Slow type checking, interaction, compilation or execution of Agda programs regression on master Unreleased regression in development version (Change to "regression in ..." should it be released!) ux: emacs Issues relating to the Emacs agda2-mode
Milestone

Comments

@nad
Copy link
Contributor

nad commented Oct 6, 2020

Sometimes when I've checked a file in Emacs and load another file, then the first file is checked again. This is a major performance regression. Today I managed to create a small test case:

$ cat B.agda 
open import A

$ cat script.hs 
#!/usr/bin/env runhaskell

{-# LANGUAGE RecordWildCards #-}

import Data.List
import System.Directory
import System.Exit

import RunAgda

topLevel     = "B.agda"
importedName = "A"
imported     = importedName ++ ".agda"

main :: IO ()
main = runAgda [ "--no-libraries"
               , "--ignore-interfaces"
               ] $ \(AgdaCommands { .. }) -> do

  -- Discard the first prompt.
  echoUntilPrompt

  -- Set up and load the imported file.
  writeFile imported "-- 0"
  loadAndEcho imported

  -- Edit and load the imported file.
  writeFile imported "-- 1"
  loadAndEcho imported

  -- Load the top-level module.
  output <- loadAndEcho topLevel

  -- Clean up.
  removeFile imported

  -- Check the output.
  if ("Checking " ++ importedName) `isInfixOf` unlines output
  then exitFailure
  else exitSuccess
$ ./script.hs agda-2.6.1 && echo ok
(agda2-status-action "")
(agda2-info-action "*Type-checking*" "" nil)
(agda2-highlight-clear)
(agda2-info-action "*Type-checking*" "Checking A ([…]/A.agda).\n" t)
(agda2-status-action "Checked")
(agda2-info-action "*All Done*" "" nil)
((last . 1) . (agda2-goals-action '()))
(agda2-status-action "")
(agda2-info-action "*Type-checking*" "" nil)
(agda2-highlight-clear)
(agda2-info-action "*Type-checking*" "Checking A ([…]/A.agda).\n" t)
(agda2-status-action "Checked")
(agda2-info-action "*All Done*" "" nil)
((last . 1) . (agda2-goals-action '()))
(agda2-status-action "")
(agda2-info-action "*Type-checking*" "" nil)
(agda2-highlight-clear)
(agda2-info-action "*Type-checking*" "Checking B ([…]/B.agda).\n" t)
(agda2-status-action "Checked")
(agda2-info-action "*All Done*" "" nil)
((last . 1) . (agda2-goals-action '()))
ok
$ ./script.hs agda || echo failure
(agda2-status-action "")
(agda2-info-action "*Type-checking*" "" nil)
(agda2-highlight-clear)
(agda2-info-action "*Type-checking*" "Checking A ([…]/A.agda).\n" t)
(agda2-status-action "Checked")
(agda2-info-action "*All Done*" "" nil)
((last . 1) . (agda2-goals-action '()))
(agda2-status-action "")
(agda2-info-action "*Type-checking*" "" nil)
(agda2-highlight-clear)
(agda2-info-action "*Type-checking*" "Checking A ([…]/A.agda).\n" t)
(agda2-status-action "Checked")
(agda2-info-action "*All Done*" "" nil)
((last . 1) . (agda2-goals-action '()))
(agda2-status-action "")
(agda2-info-action "*Type-checking*" "" nil)
(agda2-highlight-clear)
(agda2-info-action "*Type-checking*" "Checking B ([…]/B.agda).\n" t)
(agda2-info-action "*Type-checking*" " Checking A ([…]/A.agda).\n" t)
(agda2-status-action "Checked")
(agda2-info-action "*All Done*" "" nil)
((last . 1) . (agda2-goals-action '()))
failure
$ agda --version
Agda version 2.6.2-7d26757

Note that Agda 2.6.2-7d26757 typechecks A.agda, even though this file has already been typechecked.

Bisection points towards 549f939 ("[ fixed #4647 ] top-level interaction: access to private decls."). The problem is still there if --top-level-interaction-no-private is used. @andreasabel, can you take a look at this?

@nad nad added performance Slow type checking, interaction, compilation or execution of Agda programs regression on master Unreleased regression in development version (Change to "regression in ..." should it be released!) labels Oct 6, 2020
@nad nad added this to the 2.6.2 milestone Oct 6, 2020
@nad nad added the ux: emacs Issues relating to the Emacs agda2-mode label Oct 6, 2020
nad added a commit that referenced this issue Oct 7, 2020
@uncle-betty
Copy link
Contributor

Oh, interesting. After poking around a little, I think that the following happens:

  • When loading A.agda for the second time, top-level interaction mode prevents createInterface from actually writing the interface file A.agdai for the modified A.agda.

  • Now the source code A.agda (actually: its hash value) doesn't match A.agdai (actually: the source code hash value embedded in it). That's why A.agdai is (rightfully) considered to not be up-to-date when loading B.agda and thus A.agda gets type-checked again.

I think that I get why Agda won't load interface files in top-level interaction mode. As I understand it, interface files only contain a subset of the information gathered during the type-check. In particular, they lack information required by the top-level interaction mode.

But why doesn't Agda save interface files in top-level interaction mode? To speed up the type-check by skipping serialization and compression?

In any case, it seems that the --top-level-interaction-no-private flag doesn't change this behavior. cmd_load' always receives a TypeCheck TopLevelInteraction from the Cmd_load handler. Unless the current top-level file changes, this seems to get passed all the way down to typeCheckMain unchanged. (That's why it takes two loads of A.agda to trigger the issue. The second load doesn't change the current top-level file.) typeCheckMain wraps it in a MainInterface, which ultimately ends up in createInterface, where it prevents writing A.agdai.

@uncle-betty
Copy link
Contributor

After reading the comments on PR #4652 it seems that the .agdai files aren't written in top-level interaction mode, because top-level interaction mode keeps extra information on a module (its private declarations) that's not supposed to go in the .agdai file. So, this behavior is about more than just serialization and compression.

@uncle-betty
Copy link
Contributor

uncle-betty commented Oct 9, 2020

To address this issue, could createInterface in top-level interaction mode still do writeInterface, but ignore the stripped interface returned by writeInterface? So, when in top-level interaction mode, just strip the interface for the sake of serializing it to the .agdai file, but then keep using the original unstripped interface?

The following test with current master seems to indicate that this would do the trick, at least on the small scale of the given test case. What would be the downsides of doing something along these lines?

tlo@carol:~/agda/mine/test/interaction$ git diff
diff --git a/src/full/Agda/Interaction/Imports.hs b/src/full/Agda/Interaction/Imports.hs
index 9170b8851..324fb0737 100644
--- a/src/full/Agda/Interaction/Imports.hs
+++ b/src/full/Agda/Interaction/Imports.hs
@@ -1056,6 +1056,8 @@ createInterface file mname isMain msi =
         return i
       (_, MainInterface (TypeCheck TopLevelInteraction)) -> do
         reportSLn "import.iface.create" 7 "We are in top-level interaction mode and want to retain private declarations, skipping writing interface file."
+        ifile <- toIFile file
+        _ <- writeInterface ifile i
         return i
       _ -> Bench.billTo [Bench.Serialization] $ do
         reportSLn "import.iface.create" 7 "Actually calling writeInterface."
tlo@carol:~/agda/mine/test/interaction$ stack runghc Issue4959.hs agda
(agda2-status-action "")
(agda2-info-action "*Type-checking*" "" nil)
(agda2-highlight-clear)
(agda2-info-action "*Type-checking*" "Checking Issue4959-2 (/home/tlo/agda/mine/test/interaction/Issue4959-2.agda).\n" t)
(agda2-status-action "Checked")
(agda2-info-action "*All Done*" "" nil)
((last . 1) . (agda2-goals-action '()))
(agda2-status-action "")
(agda2-info-action "*Type-checking*" "" nil)
(agda2-highlight-clear)
(agda2-info-action "*Type-checking*" "Checking Issue4959-2 (/home/tlo/agda/mine/test/interaction/Issue4959-2.agda).\n" t)
(agda2-status-action "Checked")
(agda2-info-action "*All Done*" "" nil)
((last . 1) . (agda2-goals-action '()))
(agda2-status-action "")
(agda2-info-action "*Type-checking*" "" nil)
(agda2-highlight-clear)
(agda2-info-action "*Type-checking*" "Checking Issue4959 (/home/tlo/agda/mine/test/interaction/Issue4959.agda).\n" t)
(agda2-status-action "Checked")
(agda2-info-action "*All Done*" "" nil)
((last . 1) . (agda2-goals-action '()))
tlo@carol:~/agda/mine/test/interaction$ 

@nad
Copy link
Contributor Author

nad commented Mar 18, 2021

If work on this issue has stalled, then I suggest that we undo the fix of #4647.

@jespercockx
Copy link
Member

👍 on undoing the fix

@andreasabel
Copy link
Member

I spent some time this week on this issue, let's see if I can carve out more time.

andreasabel added a commit that referenced this issue May 19, 2021
This commit unfixes #4647 by removing the
`InteractionMode` (`TopLevelInteraction`/`RegularInteraction`) and the
option to switch off the logic (which didn't fully work).

For #4647 there is a simple workaround: add a hole to the file so that
the interface is not serialize and thus the private definitions are
not removed.
andreasabel added a commit that referenced this issue May 19, 2021
This commit unfixes #4647 by removing the
`InteractionMode` (`TopLevelInteraction`/`RegularInteraction`) and the
option to switch off the logic (which didn't fully work).

For #4647 there is a simple workaround: add a hole to the file so that
the interface is not serialize and thus the private definitions are
not removed.
andreasabel added a commit that referenced this issue May 19, 2021
This commit unfixes #4647 by removing the
`InteractionMode` (`TopLevelInteraction`/`RegularInteraction`) and the
option to switch off the logic (which didn't fully work).

For #4647 there is a simple workaround: add a hole to the file so that
the interface is not serialize and thus the private definitions are
not removed.
@andreasabel
Copy link
Member

In PR #5402 I am ripping out the fix for #4647, as I do not have the time atm for a proper investigation. Maybe after the release we can revisit #4647 (maybe...).

andreasabel added a commit that referenced this issue May 19, 2021
This commit unfixes #4647 by removing the
`InteractionMode` (`TopLevelInteraction`/`RegularInteraction`) and the
option to switch off the logic (which didn't fully work).

For #4647 there is a simple workaround: add a hole to the file so that
the interface is not serialize and thus the private definitions are
not removed.
andreasabel added a commit that referenced this issue May 19, 2021
This commit unfixes #4647 by removing the
`InteractionMode` (`TopLevelInteraction`/`RegularInteraction`) and the
option to switch off the logic (which didn't fully work).

For #4647 there is a simple workaround: add a hole to the file so that
the interface is not serialize and thus the private definitions are
not removed.
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
performance Slow type checking, interaction, compilation or execution of Agda programs regression on master Unreleased regression in development version (Change to "regression in ..." should it be released!) ux: emacs Issues relating to the Emacs agda2-mode
Projects
None yet
Development

Successfully merging a pull request may close this issue.

4 participants