stevenfontanella / microlens

A lightweight (but compatible with ‘lens’) lenses library
https://hackage.haskell.org/package/microlens
285 stars 40 forks source link

Add a note on whether microlens-th is recommended for libraries in the README #78

Open seagreen opened 8 years ago

seagreen commented 8 years ago

Right now the README helpfully suggests that library authors start with microlens instead of microlens-platform and add packages as needed.

I'm curious if microlens-th is appropriate for library authors, or whether slowing down the library's compile time isn't worth the tradeoff of conveniently deriving lenses. Has how much makeLenses slows down compilation been researched? If it hasn't would that be a useful thing for me to look into and contribute to the README?

neongreen commented 8 years ago

Has how much makeLenses slows down compilation been researched?

I don't think it has been.

would that be a useful thing for me to look into

Yep! I can think of several questions off the top of my head:

neongreen commented 8 years ago

Note that if you simply compile a module with lenses, the optimiser might remove unused code instead of optimising it further (I don't know whether it's actually possible but it seems plausible), so don't forget to export the derived lenses. Benchmarking can probably be done with bench.

seagreen commented 8 years ago

Thanks for the thoughts, this is going on my todo list.

seagreen commented 8 years ago

Make an initial experimental repo:

Here are manual lenses: https://github.com/seagreen/microlens-th-experiments/blob/master/plain-lenses/src/Experiment.hs#L121

Here are th lenses: https://github.com/seagreen/microlens-th-experiments/blob/master/th-lenses/src/Experiment.hs#L122

And here are the results: https://github.com/seagreen/microlens-th-experiments/blob/master/results.txt

The test sources are the most boring thing I could think of. 5 data types, each with 20 fields, nothing else (the only exception is the small-data project which has 5 data types with 1 field each).

I've got some thoughts on the results, but take a look at the code first and let me know what you think.

seagreen commented 8 years ago

PS bench was a great recommendation.

neongreen commented 8 years ago

Thanks!

I modified the test to call ghc on one file instead of stack (and also to run longer with -L 300). With 7.10.3 I'm getting that lens generation takes 400ms (and compilation – 1s):

small-data
benchmarking rm -f src/*.hi; rm -f src/*.o; ghc -O src/Experiment.hs
time                 160.5 ms   (155.7 ms .. 165.3 ms)

no-lenses
benchmarking rm -f src/*.hi; rm -f src/*.o; ghc -O src/Experiment.hs
time                 335.8 ms   (331.1 ms .. 339.7 ms)

plain-lenses
benchmarking rm -f src/*.hi; rm -f src/*.o; ghc -O src/Experiment.hs
time                 1.063 s    (1.054 s .. 1.073 s)

th-lenses
benchmarking rm -f src/*.hi; rm -f src/*.o; ghc -O src/Experiment.hs
time                 1.457 s    (1.437 s .. 1.484 s)

With stack ghc and GHC 8 lens generation takes the same 400ms, but compilation jumps to 1.3s (might be because of compiling dyn files too, I don't know how to turn it off):

small-data
benchmarking rm -f src/*.hi; rm -f src/*.o; rm -f src/*.dyn_hi; rm -f src/*.dyn_o; stack ghc --resolver nightly-2016-07-04 -- -O src/Experiment.hs
time                 327.5 ms   (318.4 ms .. 336.3 ms)

no-lenses
benchmarking rm -f src/*.hi; rm -f src/*.o; rm -f src/*.dyn_hi; rm -f src/*.dyn_o; stack ghc --resolver nightly-2016-07-04 -- -O src/Experiment.hs
time                 550.2 ms   (537.5 ms .. 564.4 ms)

plain-lenses
benchmarking rm -f src/*.hi; rm -f src/*.o; rm -f src/*.dyn_hi; rm -f src/*.dyn_o; stack ghc --resolver nightly-2016-07-04 -- -O src/Experiment.hs
time                 1.324 s    (1.317 s .. 1.335 s)

th-lenses
benchmarking rm -f src/*.hi; rm -f src/*.o; rm -f src/*.dyn_hi; rm -f src/*.dyn_o; stack ghc --resolver nightly-2016-07-04 -- -O src/Experiment.hs
time                 1.727 s    (1.701 s .. 1.763 s)
neongreen commented 8 years ago

Interestingly, in your tests the difference between manual and TH lenses is much smaller. Can you rerun them with a bigger -L? (It controls how many seconds the benchmark will run)

neongreen commented 8 years ago

Another thing to do, by the way, is to figure out how the added time depends on the number of fields vs the number of records. Does it matter if the fields are in different records, or not?

seagreen commented 8 years ago

Here are my results with -L 300. The difference between manual and TH lenses stayed small.

benchmarking cd ~/code/mine/microlens-th-experiments/small-data; rm -rf ./.stack-work; stack build
time                 2.688 s    (2.677 s .. 2.696 s)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 2.695 s    (2.689 s .. 2.705 s)
std dev              15.70 ms   (8.709 ms .. 25.10 ms)

benchmarking cd ~/code/mine/microlens-th-experiments/no-lenses; rm -rf ./.stack-work; stack build
time                 2.913 s    (2.893 s .. 2.926 s)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 2.934 s    (2.921 s .. 2.959 s)
std dev              30.07 ms   (5.817 ms .. 43.02 ms)

benchmarking cd ~/code/mine/microlens-th-experiments/plain-lenses; rm -rf ./.stack-work; stack build
time                 3.778 s    (3.732 s .. 3.830 s)
                     1.000 R²   (0.999 R² .. 1.000 R²)
mean                 3.756 s    (3.745 s .. 3.780 s)
std dev              27.24 ms   (14.54 ms .. 42.07 ms)

benchmarking cd ~/code/mine/microlens-th-experiments/th-lenses; rm -rf ./.stack-work; stack build
time                 3.985 s    (3.967 s .. 4.005 s)
                     1.000 R²   (1.000 R² .. 1.000 R²)
mean                 3.979 s    (3.973 s .. 3.987 s)
std dev              11.94 ms   (7.659 ms .. 15.89 ms)

Want to push up your changes (you've got push access to the repo) and then I can rerun them with an identical setup to you?