Skip to content

meter: remove O(n^2) setIndex loops and inspect.stack from first TimeSignature build - #2200

Open
float3 wants to merge 5 commits into
cuthbertLab:meter-speed-2026from
float3:help/meter-1972-slow-paths
Open

float3 wants to merge 5 commits into
cuthbertLab:meter-speed-2026from
float3:help/meter-1972-slow-paths

Conversation

@float3

@float3 float3 commented Nov 2, 2026

Copy link
Copy Markdown
Contributor

For #1972.

  • _setDefaultAccentWeights and subdivideNestedHierarchy called setIndex once per accent or beat, and each call copied and hashed the whole partition: O(n^2). Each now rebuilds once.
  • FrozenObject._check_init walks f_back instead of inspect.stack(), which reads source files. The first TimeSignature of each of six common meters goes from about 71 ms to 6 ms.
  • The summedNumerator setter uses modify().
  • Removed MeterCore._reprInternal; ProtoM21Object comes first in the MRO, so it never ran.

Master is not merged. Merging it conflicts in Duration.__eq__, the version, and AGENTS.md; float3:help/meter-speed-2026 shows one way to resolve them.

AI-assisted (Claude)

ProtoM21Object comes before MeterCore in the MRO of both subclasses, and
each subclass defines its own _reprInternal, so this one never ran.
The setter rebuilt the display sequence through makeSequence and the
private _partition; modify(summedNumerator=value) does the same through
the public method.
_setDefaultAccentWeights called setIndex once per accent.  Each call
copied and hashed the whole partition and left an intermediate sequence
in _meterSequenceCache, so a meter with n accents did O(n^2) work and
cached n sequences that nothing uses.  Build the weighted terminals in a
list and call modify(partition=...) once.

Accent, beat and beam sequences for 153 meters match master (apart from
the int/float default weight this branch already changes).
inspect.stack() reads the source file and line for every frame, which
made each attribute set inside FrozenDuration.__init__ take milliseconds.
With MeterTerminal.duration returning a FrozenDuration, the first
TimeSignature of each meter took about 30 ms here (master: 1.5 ms).
Following f_back finds the same __init__/__new__/__setstate__ frame
without reading files: about 6 ms, and music21/meter tests run in 2 s
instead of 11 s.
_subdivideNestedByPaths replaced one path at a time through setIndex, so
each replacement copied and hashed every sequence above it, and cached
each intermediate root.  For n beats that is O(n^2) per level.  Collect
the replacements and rebuild only the sequences above them, once.

subdivideNestedHierarchy gives the same sequences and weights for 108
cases (12 meters, depths 1-4, both normalizeDenominators settings, and a
beatSequence as firstPartitionForm).
@coveralls

Copy link
Copy Markdown

Coverage Status

Coverage is 93.302% — float3:help/meter-1972-slow-paths into cuthbertLab:meter-speed-2026. No base build found for cuthbertLab:meter-speed-2026.

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants