optimize Natural/fold in the strict case#2585
Conversation
Co-authored-by: Gabriella Gonzalez <[email protected]>
|
@Gabriella439 Thank you for the suggestion. I'm not sure why the build fails for MacOS. I also have trouble building Update - this is fixed now. |
|
@Gabriella439 My changes do not actually seem to work. I built a local executable ( let f : Natural → Natural = λ(x : Natural) → if Natural/isZero x then 1 else x
in Natural/fold 100000000 Natural f 0takes about 5 seconds but should be instantaneous. Am I doing something wrong? I changed the Dhall version to 1.42.2 in $ stack build dhall
...
Installing executable dhall in /Users/sergei.winitzki/Code/winitzki-dhall-haskell/.stack-work/install/x86_64-osx/5cb58a553262eb646270303a92d2143ba964e30fb089950b829eb0787bd00e99/9.2.8/bin
Registering library for dhall-1.42.2..
$ stack exec dhall repl
Welcome to the Dhall v1.42.2 REPL! Type :help for more information.
⊢ :let f : Natural → Natural = λ(x : Natural) → if Natural/isZero x then 1 else x
f : Natural → Natural
⊢ Natural/fold 100000000 Natural f 0
1This still takes about 5 seconds. How could this even work if I compiled dhall with no code for Am I editing the wrong place in the source code? I see that some code involving |
|
I added some new code to Why is For |
|
@Gabriella439 You merged the PR but I still think the work is not finished. See my comment in the code. |
| in go zero (fromIntegral n' :: Integer) | ||
| go zero (fromIntegral n' :: Integer) where | ||
| go !acc 0 = acc | ||
| go (VNaturalLit x) m = |
There was a problem hiding this comment.
Here, we detect the shortcut only when the accumulator is a Natural literal. However, we would like to detect the shortcut in all cases. I could not find any way of comparing Val values. What kind of code would need to be written here?
There was a problem hiding this comment.
Dhall.Eval.conv is what you want
1.42.2 * [Supports standard version 23.1.0](https://github.com/dhall-lang/dhall-lang/releases/tag/v23.1.0) * [Allow `Natural` and `Integer` literals in binary notation](dhall-lang/dhall-haskell#2540) * Fix macOS build [[#2561](dhall-lang/dhall-haskell#2561)] / [[#2586](dhall-lang/dhall-haskell#2586)] * [`dhall to-directory-tree`: Fix support for empty `Map`s](dhall-lang/dhall-haskell#2609) * [`Dhall.TH`: Improve recompilation checking](dhall-lang/dhall-haskell#2620) * `Dhall.TH` utilities now use `addDependentFile` internally so that GHC will recompile if any Dhall dependencies change * Performance improvements * Optimize `Natural/fold`: [[#2585](dhall-lang/dhall-haskell#2585)] / [[#2596](dhall-lang/dhall-haskell#2596)] * [Improve `Dhall.Map.traverseWithKey` performance](dhall-lang/dhall-haskell#2589) * The fold will now short-circuit if it reaches a fixed point * [#2611](dhall-lang/dhall-haskell#2611) * Fixes and improvements to test suite * [#2593](dhall-lang/dhall-haskell#2593) * Fixes and improvements to haddocks * [#2546](dhall-lang/dhall-haskell#2546) * Fixes and improvements to code formatting * [#2608](dhall-lang/dhall-haskell#2608)
The proposed optimization follows the discussion in dhall-lang/dhall-lang#1213 (comment)
The beta-normalization for
Natural/fold n t succ zerocorresponds to evaluatingsucc(succ(succ(...(succ(zero))...)))where we applysuccexactlyntimes.The current code uses a loop where
succis always evaluatedntimes.However, sometimes the loop can be stopped early because the values stop changing.
For example:
With this definition, we have
f x = xwhen x is nonzero. The expression evaluates to 5, but the evaluation takes time linear in the first argument (10000000) because the functionfwill be applied that many times to the value5and beta-normalization will be performed each time.The new code rewrites
strictLoopviastrictLoopShortcut, which stops the loop as soon asf x = x.This optimization is only applied in the "strict" case (
Normalization.hsintroducesstrictandlazycases when evaluatingNatural/fold.)There should be no change of behavior after this optimization.
This is because all Dhall functions are pure; if
f x = xthen the result of evaluatingf (f (f x))is exactly the same as justx.So, the only effect of the new code is that some
Natural/foldexpressions will be beta-normalized faster than before.I'd like to add some tests for the new behavior but I don't know how. The new behavior returns exactly the same normal forms as before, but does it faster when the "shortcut" is detected (when
f x = xis detected). I am not a Haskell programmer, so any suggestions will be welcome here.Another issue is that I am not familiar with the Haskell build systems, and both
cabal new-build dhallas well asstack buildfail on my machine in this project, with dependency errors that I am unable to fix.