lean4-htt/tests/bench/ilean_roundtrip.lean
Marc Huisinga 9009c1ac91
fix: ilean loading performance (#4900)
This PR roughly halves the time needed to load the .ilean files by
optimizing the JSON parser and the conversion from JSON to Lean data
structures.

The code is optimized roughly as follows:
- String operations are inlined more aggressively
- Parsers are changed to use new `String.Iterator` functions `curr'` and
`next'` that receive a proof and hence do not need to perform an
additional check
- The `RefIdent` of .ilean files now uses a `String` instead of a `Name`
to avoid the expensive parse step from `String` to `Name` (despite the
fact that we only very rarely actually need a `Name` in downstream code)
- Instead of `List`s and `Subarray`s, the JSON to Lean conversion now
directly passes around arrays and array indices to avoid redundant
boxing
- Parsec's `peek?` sometimes generates redundant `Option` wrappers
because the generation of basic blocks interferes with the ctor-match
optimization, so it is changed to use an `isEof` check where possible
- Early returns and inline-do-blocks cause the code generator to
generate new functions, which then interfere with optimizations, so they
are now avoided
- Mutual defs are used instead of unspecialized passing of higher-order
functions to generate faster code
- The object parser is made tail-recursive

This PR also fixes a stack overflow in `Lean.Json.compress` that would
occur with long lists and adds a benchmark for the .ilean roundtrip
(compressed pretty-printing -> parsing).
2024-08-29 11:51:48 +00:00

54 lines
1.7 KiB
Text

import Lean.Data.Lsp
def genModuleRefs (n : Nat) : IO Lean.Lsp.ModuleRefs := do
let someLoc : Lean.Lsp.RefInfo.Location := {
range := ⟨⟨333, 444⟩, ⟨444, 555⟩⟩
parentDecl? := some {
name := "A.Reasonably.Long.ParentDecl.Name.barfoo",
range := ⟨⟨1111, 2222⟩, ⟨3333, 4444⟩⟩
selectionRange := ⟨⟨5555, 6666⟩, ⟨7777, 8888⟩⟩
}
}
let mut someUsages : Array Lean.Lsp.RefInfo.Location := #[]
for _ in [0, 200] do
someUsages := someUsages.push someLoc
let someInfo : Lean.Lsp.RefInfo := {
definition? := someLoc
usages := someUsages
}
let mut refs : Lean.Lsp.ModuleRefs := .empty
for i in [0:n] do
let someIdent := Lean.Lsp.RefIdent.const s!"A.Reasonably.Long.Module.Name{i}" s!"A.Reasonably.Long.Declaration.Name.foobar{i}"
refs := refs.insert someIdent someInfo
return refs
@[noinline]
def compress (refs : Lean.Lsp.ModuleRefs) : IO String := do
return Lean.Json.compress (Lean.ToJson.toJson refs)
@[noinline]
def parse (s : String) : IO (Except String Lean.Json) := do
return Lean.Json.parse s
def main (args : List String) : IO Unit := do
let n := (args.get! 0).toNat!
let refs ← genModuleRefs n
let compressStartTime ← IO.monoMsNow
let s ← compress refs
let compressEndTime ← IO.monoMsNow
let compressTime : Float := (compressEndTime - compressStartTime).toFloat / 1000.0
IO.println s!"compress: {compressTime}"
let parseStartTime ← IO.monoMsNow
let r ← parse s
let parseEndTime ← IO.monoMsNow
let parseTime : Float := (parseEndTime - parseStartTime).toFloat / 1000.0
match r with
| .ok _ =>
IO.println s!"parse: {parseTime}"
| .error _ =>
IO.println s!"parse: {parseTime}"
IO.println "error"