Skip to content

rule proposal: a loop that scans a list it carries unchanged, once per element (per-item membership scan) #196

Description

@ngngardner

Found while profiling bolt lint (autoresearch branch autoresearch/bolt-lint-perf, autoresearch/loop-260924-2130/summary.md). This was the largest single cost in the run, and no current rule sees it.

Shape

A recursive def walks list A and carries list B through unchanged (+uses, +read, +seen). On every step it passes B to a walk: a looping def of the file, or a Base walk such as List.contains or List.find. That's O(|A|·|B|), and the output is still right.

def check.go(binds: List<&2, Bind.Bind>, +uses: List<&2, Bind.Use>, ..) -> List<&2, F.Finding>:
  match binds:
    case Con{Bind.Bind{+name, +line, +col, +kind, note}, rest}:
      +more = check.go(rest, uses, fl, path)
      ... Bool.not(used(uses, line, col)) ...   # a scan of every use, per binder

Cost here

  • unused on bolt/rules/PROOF.bend: 22k binders × 94k uses took 135 s of a 208 s run. With a trie of use targets as a sound fast path (a scan only on a miss), it takes 0.4 s.
  • The coverage/unsafe closure's fresh asked has(read, d) of every file for each edge. Asking the short lists first and stopping early took coverage from 10.2 s to 2.75 s.

Proposed rule (suspicious)

Report a def that calls itself and, in the same step, passes one of its parameters to a walk, where that parameter goes into the self-call unchanged (the same name, in the same slot). The rule would be quiet in these cases:

  • the carried list is a literal, or a fixed table (that's table/hoist territory);
  • laws and proofs;
  • the call sits in a thunk (_u => ..) that only runs on a rare branch;
  • possibly, a size hint in the def says the list is small.

Message idea: "used walks uses on every step of check.go, which carries it unchanged: that's |binds|·|uses|. Index it once outside the loop, or merge two sorted walks."

A new code (U013?) under suspicious.

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

      Milestone

      No milestone

      Relationships

      None yet

      Development

      No branches or pull requests

      Issue actions