Skip to content

Add running time printer and statistics for Laurel passes#938

Open
aqjune-aws wants to merge 11 commits intomainfrom
jlee/profile-laurel
Open

Add running time printer and statistics for Laurel passes#938
aqjune-aws wants to merge 11 commits intomainfrom
jlee/profile-laurel

Conversation

@aqjune-aws
Copy link
Copy Markdown
Contributor

@aqjune-aws aqjune-aws commented Apr 16, 2026

This pull request

  • Adds running time printer (using the profileStep utility function in Strata) and statistics for Laurel passes
  • Prints them when --profile is given
  • Also does some cosmetic updates like wrapping the Laurel passes in an Array, and adding # in front of derive_prefixed_toString which was promised in Collect Statistics for the Core pipeline #870 (comment) .
[statistics] EliminateHoles.holesEliminated: 14                 
[statistics] InferHoleTypes.holesAnnotated: 15                  

By submitting this pull request, I confirm that you can use, modify, copy, and redistribute this contribution, under the terms of your choice.

@aqjune-aws aqjune-aws requested a review from a team April 16, 2026 03:50
@aqjune-aws
Copy link
Copy Markdown
Contributor Author

This pull request was written by an internal request for collecting measures useful for observing how behaviors of passes change on benchmarks after Strata is updated. I relied on an agent to figure out which numbers to report.

Comment thread Strata/Languages/Laurel/LaurelCompilationPipeline.lean Outdated
Comment on lines +54 to +57
{ name := "EliminateValueReturns"
run := fun p _m =>
let (p', diags) := eliminateValueReturnsTransform p
(p', diags.toList, {}) },
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

EliminateValueReturns is the only pass that doesn't return Statistics from its underlying function — the pipeline wrapper supplies {}. For consistency, consider adding a Statistics return to eliminateValueReturnsTransform (even if it's just counting procedures processed), or at least leave a -- TODO here explaining why it's omitted.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

To avoid conflicts with updates in other Laurel passes, I left stats in eliminateHoles and inferHoleTypes only.

Comment thread Strata/Languages/Laurel/EliminateReturnsInExpression.lean Outdated
Comment thread Strata/Languages/Laurel/LiftImperativeExpressions.lean Outdated
Comment thread Strata/Languages/Laurel/DesugarShortCircuit.lean Outdated
/-- Human-readable name, used for profiling and file emission. -/
name : String
/-- How many times `resolve` should be run after the pass. -/
resolvesAfter : Nat := 0
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this be more than 1 ? Why not use a bool?

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It seems there were two resolve invocations after the previous modifiesClausesTransform call. Or should one of these be removed?

  let (program, modifiesDiags) := modifiesClausesTransform model program
  let result := resolve program (some model)
  let (program, model) := (result.program, result.model)
  let result := resolve program (some model)
  let (program, model) := (result.program, result.model)

Copy link
Copy Markdown
Contributor

@keyboardDrummer keyboardDrummer Apr 17, 2026

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

One should be removed. The bool can be named something like introducesUnresolvedAstNodes: Bool or requiresReresolving: Bool

| .staticProcedure proc => some (proc.inputs.map (·.type))
| _ => none

inductive InferHoleTypesStats where
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this useful? It looks a bit like a showcase for collecting data.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I think this can be (somewhat?) useful because it is good to have an idea about how many annotated/unannotated holes there are in benchmarks.

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

What what you do with that information?

{ program with staticProcedures := finalState.generatedFunctions ++ procs }
let stats := ({} : Statistics)
|>.increment s!"{ElimHoleStats.holesEliminated}" finalState.counter
|>.increment s!"{ElimHoleStats.functionsGenerated}" finalState.generatedFunctions.length
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Nit: by construction each eliminated hole produces exactly one generated function, so functionsGenerated == holesEliminated should always hold. Consider stating that as an assert (or at minimum a comment) so a future change that breaks the 1-to-1 relationship is caught early.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Good catch, thank you.

let (program', diags, stats) ← profileStep options.profile s!" {pass.name}" do
pure (pass.run program model)
program := program'
allDiags := allDiags ++ diags
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Soft note: allDiags := allDiags ++ diags is left-nested List append inside the loop (O(n) per iteration). With 11 passes and typically small diagnostic lists this is fine, but if diagnostic counts grow consider switching to Array accumulation with Array.append.

Copy link
Copy Markdown
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Yes, diags will be pretty small. :) It would be fine.

@aqjune-aws aqjune-aws removed their assignment Apr 16, 2026

public import Strata.Languages.Laurel.MapStmtExpr
public import Strata.Languages.Laurel.Resolution
public import Strata.Util.Statistics
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Unused import?

#966 :)

Comment thread StrataMain.lean
(keepAllFilesPrefix := keepPrefix) (profile := profile)

if profile && !laurelPassStats.data.isEmpty then
let _ ← (IO.println laurelPassStats.format |>.toBaseIO)
Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
let _ ← (IO.println laurelPassStats.format |>.toBaseIO)
IO.println laurelPassStats.format

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants