@kha I created a variant of the `rbmap` example where we create a tree
but also save "checkpoints". The idea is to simulate the idiom frequently
used in a backtracking search where we "save" the "context" before each
case split in a "trail stack".
The benchmark has two parameters: the number of nodes to be inserted, and a "frequency" (how often we create a "checkpoint").
The command `rbmap_checkpoint.lean.out n n` behaves like the original
`rbmap` benchmark, and `rbmap_checkpoint.lean.out n 1` creates a
checkpoint after each insertion. The frequency provides a simple way to
control the amount of sharing. We can provide performance numbers for
different frequencies, and show the impact on the `reset/reuse` optimization.
BTW, the performance numbers are much better than I expected. For
example,
`./rbmap_checkpoint.lean.out 1000000 10` is only 30% slower than
`./rbmap_checkpoint.lean.out 1000000 1000000`
although 100k checkpoints were created.
Another good news is that we are faster than Haskell even for
`./rbmap_checkpoint.lean.out 1000000 1`
For lists of size 0, 1 and 2, it avoids the overhead of creating
temporary lists of closures. I measure the overhead with `test1.lean`
and there is no overhead in this case.
`test1.lean` has a test for length = 4, and the overhead is 7%.
We only use longestMatch to implement the Pratt Parser.
The lists should be small. So, the overhead is acceptable.
If it is not. We can add back the `longestMatch` specific for `TermParser`.
cc @kha
The artificial `Thunk` was being used just to make sure a `Parser`
object was small enough to be inlined. We don't need this hack anymore.
Commit 0ea944a adds a new transformation that makes sure the size of
field `info` does not impact the decision on whether field `fn` will be
inlined or not.
@kha I renamed the homogeneous `map` to `hmap`, and added the
heterogeneous one as `map`. As soon as we add user-defined rewriting
rules, we will be able to replace `map` with `hmap` whenever the types
are the same.