import ViE.Lsp.Lean
/-- Case: Insert/backspace via keymap queue path (single-writer worker). -/
def benchInsertQueue (iterations : Nat) (buildLeafBits : Bool) : IO Unit := do
let cfg := makeBenchConfig (some buildLeafBits)
let mut s := { ViE.initialState with config := cfg.settings }
s ← ViE.update cfg s (Key.char 'i')
for i in [0:iterations] do
if i % 9 == 0 then
s ← ViE.update cfg s Key.backspace
else
s ← ViE.update cfg s (Key.char 'a')
let _ ← ViE.update cfg s Key.esc
/-- Case: line lookup workload on large file (tree-based line traversal). -/
def benchLineLookupLarge10MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := do
let text := buildLargeText (10 * 1024 * 1024)
let pt := PieceTable.fromString text buildLeafBits
let lineCount := max 1 pt.lineCount
let total := pt.length
let mut checksum := 0
for i in [0:iterations] do
let row := i % lineCount
match pt.getLineRange row with
| some (start, len) =>
checksum := checksum + start + len
| none =>
pure ()
let off := if total == 0 then 0 else (i * 97) % total
let (r, c) := pt.getPointFromOffset off
checksum := checksum + r + c
if checksum == 0 then
IO.println "Zero checksum?"
/-- Case: compare sequential vs parallel substring extraction on large file. -/
def benchSubstringSeqLarge10MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := do
let text := buildLargeText (10 * 1024 * 1024)
let pt := PieceTable.fromString text buildLeafBits
let total := pt.length
let readLen := min (512 * 1024) total
let mut checksum := 0
for i in [0:iterations] do
let off := if total <= readLen then 0 else (i * 65537) % (total - readLen + 1)
let s := PieceTree.getSubstring pt.tree off readLen pt
checksum := checksum + s.length
if checksum == 0 then
IO.println "Zero checksum?"
/-- Case: compare sequential vs parallel substring extraction on large file. -/
def benchSubstringParLarge10MB (iterations : Nat) (buildLeafBits : Bool) : IO Unit := do
let text := buildLargeText (10 * 1024 * 1024)
let pt := PieceTable.fromString text buildLeafBits
let total := pt.length
let readLen := min (512 * 1024) total
let mut checksum := 0
for i in [0:iterations] do
let off := if total <= readLen then 0 else (i * 65537) % (total - readLen + 1)
let s ← PieceTree.getSubstringParallelIO pt.tree off readLen pt
checksum := checksum + s.length
if checksum == 0 then
IO.println "Zero checksum?"
/-- Case: syncEditorUpdate change-detection hot path. -/
def benchSyncEditorUpdate (iterations : Nat) (buildLeafBits : Bool) : IO Unit := do
let cfg := makeBenchConfig (some buildLeafBits)
let mut prev := { ViE.initialState with config := cfg.settings }
let mut curr := prev
for i in [0:iterations] do
curr := curr.insertChar (if i % 2 == 0 then 'a' else 'b')
let synced ← ViE.Lsp.Lean.syncEditorUpdate prev curr
prev := synced
curr := synced
"getbytes-1mb", "getbytes-10mb",
"insert-queue",
"line-lookup-10mb",
"substring-seq-10mb", "substring-par-10mb",
"sync-update" ]
| "insert-queue" => timeCase "insert-queue" opts.iterations (benchInsertQueue opts.iterations buildLeafBits)
| "line-lookup-10mb" => timeCase "line-lookup-10mb" opts.iterations (benchLineLookupLarge10MB opts.iterations buildLeafBits)
| "substring-seq-10mb" => timeCase "substring-seq-10mb" opts.iterations (benchSubstringSeqLarge10MB opts.iterations buildLeafBits)
| "substring-par-10mb" => timeCase "substring-par-10mb" opts.iterations (benchSubstringParLarge10MB opts.iterations buildLeafBits)
| "sync-update" => timeCase "sync-update" opts.iterations (benchSyncEditorUpdate opts.iterations buildLeafBits)
| other =>
IO.println s!"[bench] Unknown case: {other}"
| "edit" => timeCase "edit" opts.iterations (benchEditMix opts.iterations)
| "clipboard" => timeCase "clipboard" opts.iterations (benchClipboard opts.iterations)
| "workgroups" => timeCase "workgroups" opts.iterations (benchWorkgroups opts.iterations)
| "windows" => timeCase "windows" opts.iterations (benchWindows opts.iterations)
| "undo" => timeCase "undo/redo" opts.iterations (benchUndoRedo opts.iterations)
| "search-bloom" =>
timeCase "search-bloom" opts.iterations (benchSearch opts.iterations true opts.textLines opts.lineLen buildLeafBits cacheMax)
| "search-linear" =>
timeCase "search-linear" opts.iterations (benchSearch opts.iterations false opts.textLines opts.lineLen buildLeafBits cacheMax)
| "render" =>
if opts.render then
timeCase "render" opts.iterations (benchRender opts.iterations)
else
IO.println "[bench] render skipped (--no-render)"
/-- Run benchmark cases. -/
def runBenchmark (opts : BenchOptions) : IO Unit := do
let buildLeafBits := opts.buildLeafBits.getD ViE.defaultConfig.searchBloomBuildLeafBits
let config := makeBenchConfig (some buildLeafBits)
let cacheMax := config.settings.searchBloomCacheMax
let cases := if opts.cases.isEmpty then availableCases else opts.cases
IO.println s!"Starting benchmark: iter={opts.iterations}, render={opts.render}"
if opts.warmup > 0 then
IO.println s!"Warmup: {opts.warmup} iterations"
for _ in [0:opts.warmup] do
let mut s := ViE.initialState
s := s.insertChar 'a'
let _ ← pure s
for c in cases do
match c with
| "insert" => timeCase "insert" opts.iterations (benchInsert opts.iterations)
def availableCases : List String :=
/-- Case: Mixed small edits/movements. -/
def benchEditMix (iterations : Nat) : IO Unit := do
let mut s := ViE.initialState
for _ in [0:iterations] do
s := s.insertChar 'a'
namespace ViE.Benchmark
open ViE
/-- Mock Config for Benchmarking -/