Zulip Chat Archive

Stream: general

Topic: trace debugging


Kevin Buzzard (Aug 08 2023 at 18:19):

Right now I'm looking at some trace.profiler output in quite a large block of code, and it doesn't add up. I'd like to investigate further. Right now it looks like this in the infoview when I unfold the unique output of the trace profiler:

[Elab.command] [3.682466s] /--
    The Hales-Jewett theorem. This version has a restriction on universe levels which is necessary
    for the proof. See `exists_mono_in_high_dimension` for a fully universe-polymorphic version. -/
   **100 lines omitted* 
  [Elab.step] [0.010713s] expected type:  (α : Type u) [inst : Finite α] (κ : Type (max v u))...
  **pretty much same 100 lines omitted 
  [Elab.step] [0.382477s]
   ** etc** 
  [Kernel] [0.024552s] typechecking declaration

It was stupidly annoying having to check that the smaller numbers didn't add up to the bigger numbers (and I did it all by manual text editor in emacs, I could easily have got it wrong). Is there a way I can just view the top few nodes on the skeleton in a small number of lines as I start changing the proof, in order to further debug?

Sebastian Ullrich (Aug 08 2023 at 20:08):

By "top" you mean the slowest ones? If it doesn't add up, we simply may be missing a trace node for a task. Could you check the plain profiler on the declaration as well?

Henrik Böving (Aug 08 2023 at 20:13):

You can still use https://github.com/hargoniX/Flame to get a nice overview as well^^

Anne Baanen (Aug 08 2023 at 21:20):

BTW: I also ran into this question here.

Tomas Skrivan (Aug 08 2023 at 21:53):

Henrik Böving said:

You can still use https://github.com/hargoniX/Flame to get a nice overview as well^^

I wrote a command #profile_this_file that automatically compiles the current file with profiler and launches speedscope. I use it all the time.

https://github.com/lecopivo/SciLean/blob/master/SciLean/Profile.lean

Just on th6e line 5 change the location of flame on your system.

Kevin Buzzard (Aug 08 2023 at 22:44):

I have output from Flame in "collapsed stack format" here

out.txt

but have no idea what to do with it. I looked at it using an online viewer -- is it something to do with the unused variables linter? I don't even know what I'm looking at.

Kevin Buzzard (Aug 08 2023 at 22:54):

Aah, set_option pp.oneline true was what I was looking for earlier I think:

[Elab.command] [3.126436s] /-- The Hales-Jewett theorem. This version has a restriction on universe levels which is necessary [...] ▼
  [step] [0.011979s] expected type: ∀ (α : Type u) [inst : Finite α] (κ : Type (max v u)) [inst : Finite κ], [...], term
      Finite.induction_empty_option [...] ▶
  [step] [0.331203s] intro α _ ihα κ _ [...] ▶
  [Kernel] [0.018651s] typechecking declaration

My issue is that 0.01+0.33+0.01 is not 3.12.

Wojciech Nawrocki (Aug 08 2023 at 22:57):

The subtraces do not cover the parent trace, i.e. there might be more calls beyond step/step/Kernel made during the [Elab.command] step which are not traced. We only get an inequality. It would be worrying if the inequality didn't hold though!

Kevin Buzzard (Aug 08 2023 at 23:02):

My question is why the HalesJewett file in mathlib is slow. How should I proceed?

Matthew Ballard (Aug 08 2023 at 23:04):

Comment it halfway and add a sorry. See if there is a significant change. If not cut that in half. If so add in half of what was left out.

Kevin Buzzard (Aug 08 2023 at 23:05):

I have been trying to do that for literally hours. It's really hard. extract_goal needs some work.

Kevin Buzzard (Aug 08 2023 at 23:06):

I don't understand the mathematics at all, this is one of the issues.

Kevin Buzzard (Aug 08 2023 at 23:12):

The profiler says

tactic execution of Lean.Parser.Tactic.rewriteSeq took 124ms
linting took 2.74s
elaboration took 3.07s

So it is the unused variables linter?

Matthew Ballard (Aug 08 2023 at 23:14):

If I sorry any of the goals from the last refine, it is pretty snappy. There must be somethings being postponed and unified at the completion of the proof.

Kevin Buzzard (Aug 08 2023 at 23:20):

Beyond some point

    refine' Or.inl ⟨⟨(s.lines.map _).cons ⟨(l'.map some).vertical s.focus, C' s.focus, fun x => _⟩,
            Sum.elim s.focus (l'.map some none), _, _⟩, _

stopped working for me

Kevin Buzzard (Aug 08 2023 at 23:21):

there was some unification issue which I couldn't resolve

Anne Baanen (Aug 09 2023 at 08:37):

There are some benchmark results in this file that I would like to understand better: between the merges of #6256 and #5998 VelCom reports an increase from 44M to 58M instructions, then between #6395 and #6441 it goes down to 48M instructions. I can't figure out what would cause either change: in the first range I don't see a commit that would obviously affect the build times, while in the second range we have #6145 which reports a benchmark increase.

Anne Baanen (Aug 09 2023 at 08:41):

@Sebastian Ullrich, would you be willing to prioritize benchmarking 1accb5fb?

Sebastian Ullrich (Aug 09 2023 at 08:43):

Done

Anne Baanen (Aug 09 2023 at 08:43):

Thank you!

Sebastian Ullrich (Aug 09 2023 at 08:44):

The instructions counts have been very reliable so far, if they cease to do that, that would be worrying. I think the next step there would be for someone to reproduce those timings on their own machine (requires Linux perf stat)

Sebastian Ullrich (Aug 09 2023 at 08:45):

I can also rerun the commit with the unexpected rise but of course that's two more hours of queueing

Anne Baanen (Aug 09 2023 at 08:47):

My hypothesis is that the instruction counts are accurate (they seem to be reliable across commits that don't affect the imported files, at least) and we just don't know what subtle thing causes them to go up and down so much.

Anne Baanen (Aug 09 2023 at 09:51):

Here's my latest attempt at splitting up the slow proof. Splitting key off made the whole thing go from 9s to 7s + 0.2s. Splitting off refine as well made it go to 2s + 0.2s + 0.2s. This is similar to, but less dramatic than, what I saw with ClassGroup, where the speedup seemed to be in hiding a big expression behind a def so it wouldn't get unfolded in the rest of the proof. Maybe something similar is going on here?

set_option trace.profiler true

-- ~2s
theorem exists_mono_in_high_dimension'.refine {α : Type u} (κ : Type (max v u))
    [Nonempty α] (r : ) (ι ι' : Type) (C : (ι  ι'  Option α)  κ) (l' : Line α ι') (C' : (ι  Option α)  κ)
    (hl' :  (x : α), (fun v' v  C (Sum.elim v (some  v'))) ((fun x i  Option.getD (idxFun l' i) x) x) = C')
    (s : ColorFocused C') (sr : Multiset.card s.lines = r)
    (h : ¬∃ p, p  s.lines  p.color = C' s.focus) : ( (s : ColorFocused C), Multiset.card s.lines = Nat.succ r)   l, IsMono C l := by
  refine' Or.inl ⟨⟨(s.lines.map _).cons ⟨(l'.map some).vertical s.focus, C' s.focus, fun x => _⟩,
          Sum.elim s.focus (l'.map some none), _, _⟩, _
  -- Porting note: Needed to reorder the following two goals
  -- The product lines are almost monochromatic.
  · refine' fun p => p.line.prod (l'.map some), p.color, fun x => _
    rw [Line.prod_apply, Line.map_apply,  p.has_color,  congr_fun (hl' x)]
  -- The vertical line is almost monochromatic.
  · rw [vertical_apply,  congr_fun (hl' x), Line.map_apply]
  -- Our `r+1` lines have the same endpoint.
  · simp_rw [Multiset.mem_cons, Multiset.mem_map]
    rintro _ (rfl | q, hq, rfl⟩)
    · simp only [vertical_apply]
    · simp only [prod_apply, s.is_focused q hq]
  -- Our `r+1` lines have distinct colors (this is why we needed to split into cases above).
  · rw [Multiset.map_cons, Multiset.map_map, Multiset.nodup_cons, Multiset.mem_map]
    exact fun q, hq, he => h q, hq, he⟩, s.distinct_colors
  -- Finally, we really do have `r+1` lines!
  · rw [Multiset.card_cons, Multiset.card_map, sr]

-- ~ 0.2s
theorem exists_mono_in_high_dimension'.key {α : Type u} [Fintype α]
    (ihα :  (κ : Type (max v u)) [Finite κ],
       (ι : Type) (_x : Fintype ι),  (C : (ι  α)  κ),  l, IsMono C l) (κ : Type (max v u))
    [inst : Fintype κ] (h : Nonempty α) (r : ) :
     (ι : Type) (_x : Fintype ι),
       (C : (ι  Option α)  κ), ( (s : ColorFocused C), Multiset.card s.lines = r)   l, IsMono C l := by
  induction' r with r ihr
  -- The base case `r = 0` is trivial as the empty collection is color-focused.
  · exact PEmpty, inferInstance, fun C => Or.inl default, Multiset.card_zero⟩⟩
  -- Supposing the key claim holds for `r`, we need to show it for `r+1`. First pick a high
  -- enough dimension `ι` for `r`.
  obtain ι, _inst,  := ihr
  -- Then since the theorem holds for `α` with any number of colors, pick a dimension `ι'` such
  -- that `ι' → α` always has a monochromatic line whenever it is `(ι → Option α) → κ`-colored.
  -- have : Finite ((ι → Option α) → κ) := Fintype.finite (@Pi.fintype _ _ _ _ _)
  specialize ihα ((ι  Option α)  κ)
  obtain ι', _inst, hι' := ihα
  -- We claim that `ι ⊕ ι'` works for `Option α` and `κ`-coloring.
  refine' Sum ι ι', inferInstance, _
  intro C
  -- A `κ`-coloring of `ι ⊕ ι' → Option α` induces an `(ι → Option α) → κ`-coloring of `ι' → α`.
  specialize hι' fun v' v => C (Sum.elim v (some  v'))
  -- By choice of `ι'` this coloring has a monochromatic line `l'` with color class `C'`, where
  -- `C'` is a `κ`-coloring of `ι → α`.
  obtain l', C', hl' := hι'
  -- If `C'` has a monochromatic line, then so does `C`. We use this in two places below.
  have mono_of_mono : ( l, IsMono C' l)   l, IsMono C l := by
    rintro l, c, hl
    refine' l.horizontal (some  l' (Classical.arbitrary α)), c, fun x => _
    rw [Line.horizontal_apply,  hl,  hl']
  -- By choice of `ι`, `C'` either has `r` color-focused lines or a monochromatic line.
  specialize  C'
  rcases  with (⟨s, sr | h)
  on_goal 2 => exact Or.inr (mono_of_mono h)
  -- Here we assume `C'` has `r` color focused lines. We split into cases depending on whether
  -- one of these `r` lines has the same color as the focus point.
  by_cases h :  p  s.lines, (p : AlmostMono _).color = C' s.focus
  -- If so then this is a `C'`-monochromatic line and we are done.
  · obtain p, p_mem, hp := h
    refine' Or.inr (mono_of_mono p.line, p.color, _⟩)
    rintro (_ | _)
    rw [hp, s.is_focused p p_mem]
    apply p.has_color
  -- If not, we get `r+1` color focused lines by taking the product of the `r` lines with `l'`
  -- and adding to this the vertical line obtained by the focus point and `l`.
  exact Combinatorics.Line.exists_mono_in_high_dimension'.refine.{u, v} κ r ι ι' C l' C' hl' s sr h

-- ~ 0.2s
/-- The Hales-Jewett theorem. This version has a restriction on universe levels which is necessary
for the proof. See `exists_mono_in_high_dimension` for a fully universe-polymorphic version. -/
theorem exists_mono_in_high_dimension' :
     (α : Type u) [Finite α] (κ : Type max v u) [Finite κ],
       (ι : Type) (_ : Fintype ι),  C : (ι  α)  κ,  l : Line α ι, l.IsMono C :=
-- The proof proceeds by induction on `α`.
  Finite.induction_empty_option
  (-- We have to show that the theorem is invariant under `α ≃ α'` for the induction to work.
  fun {α α'} e =>
    forall_imp fun κ =>
      forall_imp fun _ =>
        Exists.imp fun ι =>
          Exists.imp fun _ h C =>
            let l, c, lc := h fun v => C (e  v)
            l.map e, c, e.forall_congr_left.mp fun x => by rw [ lc x, Line.map_apply]⟩)
  (by
    -- This deals with the degenerate case where `α` is empty.
    intro κ _
    by_cases h : Nonempty κ
    · refine' Unit, inferInstance, fun C => default, Classical.arbitrary _, PEmpty.rec⟩⟩
    · exact Empty, inferInstance, fun C => (h C (Empty.rec)⟩).elim⟩)
  (by
    -- Now we have to show that the theorem holds for `Option α` if it holds for `α`.
    intro α _ ihα κ _
    cases nonempty_fintype κ
    -- Later we'll need `α` to be nonempty. So we first deal with the trivial case where `α` is
    -- empty.
    -- Then `Option α` has only one element, so any line is monochromatic.
    by_cases h : Nonempty α
    case neg =>
      refine' Unit, inferInstance, fun C => diagonal _ Unit, C fun _ => none, ?_⟩⟩
      rintro (_ | a⟩)
      · rfl
      · exact (h a⟩).elim
    -- The key idea is to show that for every `r`, in high dimension we can either find
    -- `r` color focused lines or a monochromatic line.
    suffices key :
       r : ,
         (ι : Type) (_ : Fintype ι),
           C : (ι  Option α)  κ,
            ( s : ColorFocused C, Multiset.card s.lines = r)   l, IsMono C l
    -- Given the key claim, we simply take `r = |κ| + 1`. We cannot have this many distinct colors
    -- so we must be in the second case, where there is a monochromatic line.
    · obtain ι, _inst,  := key (Fintype.card κ + 1)
      refine' ι, _inst, fun C => ( C).resolve_left _
      rintro s, sr
      apply Nat.not_succ_le_self (Fintype.card κ)
      rw [ Nat.add_one,  sr,  Multiset.card_map,  Finset.card_mk]
      exact Finset.card_le_univ _, s.distinct_colors
    exact exists_mono_in_high_dimension'.key.{u, v} (α := α) (κ := κ) @ihα h)

Kevin Buzzard (Aug 09 2023 at 10:21):

So you are saying that if you split up the argument then you can avoid the secret time hole. Here is my attempt to minimise the secret time hole:

import Mathlib.Combinatorics.HalesJewett

-- 44K heartbeats, 4.3 seconds
/-
    [] [4.407894s] theorem extracted_1 {α : Type} [Finite α] [...] ▼
      [step] [0.713942s] suffices key : [...] ▶
      [Kernel] [0.025385s] typechecking declaration
-/
count_heartbeats in
set_option trace.profiler true in
set_option profiler true in
set_option pp.oneline true in
theorem Combinatorics.Line.extracted_1
    {α : Type} [Finite α]
    (ihα :  (κ : Type) [Finite κ],  (ι : Type) (_ : Fintype ι),
       (C : (ι  α)  κ),  l : Line α ι, l.IsMono C)
    (κ : Type) [Finite κ] (h : Nonempty α) :
      (ι : Type) (_ : Fintype ι),  (C : (ι  Option α)  κ),  l, IsMono C l := by
  suffices key :
     r : ,
       (ι : Type) (_ : Fintype ι),
         C : (ι  Option α)  κ,
          ( s : ColorFocused C, Multiset.card s.lines = r)   l, IsMono C l
  -- Given the key claim, we simply take `r = |κ| + 1`. We cannot have this many distinct colors
  -- so we must be in the second case, where there is a monochromatic line.
  · classical
    let inst37 : Fintype κ := Fintype.ofFinite κ
    obtain ι, _inst,  := key (Fintype.card κ + 1)
    refine' ι, _inst, fun C => ( C).resolve_left _
    rintro s, sr
    apply Nat.not_succ_le_self (Fintype.card κ)
    rw [ Nat.add_one,  sr,  Multiset.card_map,  Finset.card_mk]
    exact Finset.card_le_univ _, s.distinct_colors
  -- We now prove the key claim, by induction on `r`.
  intro r
  induction' r with r ihr
  -- The base case `r = 0` is trivial as the empty collection is color-focused.
  · exact Empty, inferInstance, fun C => Or.inl default, Multiset.card_zero⟩⟩
  -- Supposing the key claim holds for `r`, we need to show it for `r+1`. First pick a high
  -- enough dimension `ι` for `r`.
  obtain ι, _inst,  := ihr
  classical
  let iFα : Fintype α := Fintype.ofFinite α
  let iFκ : Fintype κ := Fintype.ofFinite κ
--    let moo : Fintype (ι → Option α) := Pi.fintype
--    let foo : Fintype ((ι → Option α) → κ) := Pi.fintype
  -- Then since the theorem holds for `α` with any number of colors, pick a dimension `ι'` such
  -- that `ι' → α` always has a monochromatic line whenever it is `(ι → Option α) → κ`-colored.
  specialize ihα ((ι  Option α)  κ)
  obtain ι', _inst, hι' := ihα
  -- We claim that `ι ⊕ ι'` works for `Option α` and `κ`-coloring.
  refine' Sum ι ι', inferInstance, _
  intro C
  -- A `κ`-coloring of `ι ⊕ ι' → Option α` induces an `(ι → Option α) → κ`-coloring of `ι' → α`.
  specialize hι' fun v' v => C (Sum.elim v (some  v'))
  -- By choice of `ι'` this coloring has a monochromatic line `l'` with color class `C'`, where
  -- `C'` is a `κ`-coloring of `ι → α`.
  obtain l', C', hl' := hι'
  -- If `C'` has a monochromatic line, then so does `C`. We use this in two places below.
  have mono_of_mono : ( l, IsMono C' l)   l, IsMono C l := by
    rintro l, c, hl
    refine' l.horizontal (some  l' (Classical.arbitrary α)), c, fun x => _
    rw [Line.horizontal_apply,  hl,  hl']
  -- By choice of `ι`, `C'` either has `r` color-focused lines or a monochromatic line.
  specialize  C'
  rcases  with (⟨s, sr | h)
  on_goal 2 => exact Or.inr (mono_of_mono h)
  -- Here we assume `C'` has `r` color focused lines. We split into cases depending on whether
  -- one of these `r` lines has the same color as the focus point.
  by_cases h :  p  s.lines, (p : AlmostMono _).color = C' s.focus
  -- If so then this is a `C'`-monochromatic line and we are done.
  · obtain p, p_mem, hp := h
    refine' Or.inr (mono_of_mono p.line, p.color, _⟩)
    rintro (_ | _)
    rw [hp, s.is_focused p p_mem]
    apply p.has_color
  -- If not, we get `r+1` color focused lines by taking the product of the `r` lines with `l'`
  -- and adding to this the vertical line obtained by the focus point and `l`.
  refine' Or.inl ⟨⟨(s.lines.map _).cons ⟨(l'.map some).vertical s.focus, C' s.focus, fun x => _⟩,
          Sum.elim s.focus (l'.map some none), _, _⟩, _
  -- Porting note: Needed to reorder the following two goals
  -- The product lines are almost monochromatic.
  · refine' fun p => p.line.prod (l'.map some), p.color, fun x => _
    rw [Line.prod_apply, Line.map_apply,  p.has_color,  congr_fun (hl' x)]
  -- The vertical line is almost monochromatic.
  · rw [vertical_apply,  congr_fun (hl' x), Line.map_apply]
  -- Our `r+1` lines have the same endpoint.
  · simp_rw [Multiset.mem_cons, Multiset.mem_map]
    rintro _ (rfl | q, hq, rfl⟩)
    · simp only [vertical_apply]
    · simp only [prod_apply, s.is_focused q hq]
  -- Our `r+1` lines have distinct colors (this is why we needed to split into cases above).
  · rw [Multiset.map_cons, Multiset.map_map, Multiset.nodup_cons, Multiset.mem_map]
    exact fun q, hq, he => h q, hq, he⟩, s.distinct_colors
  -- Finally, we really do have `r+1` lines!
  · rw [Multiset.card_cons, Multiset.card_map, sr]

This proof goes: "suffices blah, <proof>, <proof>". If you replace either of the proofs with sorry then the secret time hole disappears. But with both sorrys filled you get this funny trace

    [] [4.407894s] theorem extracted_1 {α : Type} [Finite α] [...] 
      [step] [0.713942s] suffices key : [...] 
      [Kernel] [0.025385s] typechecking declaration

which doesn't add up.

Anne Baanen (Aug 09 2023 at 10:22):

Yep, that also matches my experience a couple weeks ago.

Sebastian Ullrich (Aug 09 2023 at 10:47):

That does point to the unused variables linter as it ignores decls with sorry. Does turning off linter.unusedVariables fix it then?

Anne Baanen (Aug 09 2023 at 12:07):

On both the current and modified version of the file, set_option linter.unusedVariables false does not actually seem to change the Elab.command time, although the Elab.lint time indeed goes from 3s to 0.2s.

Sebastian Ullrich (Aug 09 2023 at 14:15):

Uh, interesting?

Sebastian Ullrich (Aug 09 2023 at 14:16):

I'm kind of at a loss of words. Does the plain profiler output change?

Anne Baanen (Aug 09 2023 at 15:47):

Let me check... (By the way, the VelCom queue says "No worker registered :( I will not be able to benchmark anything! Please setup a runner and point it to this instance.", is that supposed to happen / a known issue?)

Matthew Ballard (Aug 09 2023 at 15:51):

Uh oh

Anne Baanen (Aug 09 2023 at 15:53):

With set_option profiler true:

 219:1-219:1: information:
tactic execution of Lean.Parser.Tactic.rewriteSeq took 228ms
linting took 2.77s
elaboration took 7.75s

With set_option profiler true; set_option linter.unusedVariables false:

 221:1-221:1: information:
tactic execution of Lean.Parser.Tactic.rewriteSeq took 219ms
elaboration took 7.97s

Matthew Ballard (Aug 09 2023 at 15:55):

This file gets almost 20% faster with #6370 if I remember correctly

Matthew Ballard (Aug 09 2023 at 15:55):

Maybe useful info

Anne Baanen (Aug 09 2023 at 15:57):

Good point! There is certainly some universe trickery inside these theorems so that sounds likely to have an impact.

Kevin Buzzard (Aug 09 2023 at 16:44):

In my "minimal" example above I have stripped out all the universes (for precisely this reason)

Matthew Ballard (Aug 09 2023 at 19:28):

Anne Baanen said:

Let me check... (By the way, the VelCom queue says "No worker registered :( I will not be able to benchmark anything! Please setup a runner and point it to this instance.", is that supposed to happen / a known issue?)

I know you can't do anything now but FYI @Sebastian Ullrich if you haven't seen it

Sebastian Ullrich (Aug 09 2023 at 20:15):

Nothing I can do about it right now, please bear with it a little while longer

Matthew Ballard (Aug 09 2023 at 20:18):

Of course.


Last updated: Dec 20 2023 at 11:08 UTC