Skip to content

syntax-tree-based flattening rules dominate rule-checking time #800

Description

@jackfirth

The rule application timing instrumentation from #798 (implemented in #799) immediately surfaced a hotspot. Running Resyntax on a large file from the DrRacket codebase (drracket-test/tests/drracket/syncheck-test.rkt, ~2150 lines) spent ~7.5 seconds total checking 179 rules, and the four syntax-tree-based flattening rules account for 51% of that time:

Rule Total time
nested-and-to-flat-and 1009 ms
nested-or/c-to-flat-or/c 946 ms
nested-and/c-to-flat-and/c 927 ms
nested-or-to-flat-or 921 ms
let-to-define (next slowest) 136 ms

Every other rule falls in a smooth tail from ~136 ms down to ~12 ms, so these four are each roughly 7× more expensive than any ordinary syntax-parse-based rule.

Cause

The syntax-tree syntax class in default-recommendations/private/syntax-tree.rkt parses subterms before checking the head identifier:

(pattern (id:id subtree ...)
  #:declare subtree (syntax-tree branch-identifier)
  #:when (free-identifier=? #'id branch-identifier)
  #:cut
  ...)

syntax-parse matches the pattern left to right, so for every list form the rule is tested against, it first recursively parses every subform as a syntax-tree (descending through the entire subtree), and only afterwards runs the #:when check that rejects forms whose head isn't the branch identifier. Nearly all of that recursive work is thrown away, and since rules are tested against every visited form, the cost compounds on large files. All four flattening rules pay this penalty independently.

Suggested fix

Fail fast on the head identifier before recursively parsing subtrees, e.g. by checking free-identifier=? immediately after matching id (via ~fail/~and on the head position, or by matching subforms as plain patterns first and only reparsing them as syntax-trees after the head check passes). The rank/leaf attributes only need to be computed for forms that actually have a matching head.

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