Skip to content

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

Open
float3 wants to merge 8 commits into
cuthbertLab:masterfrom
float3:help/meter-1972-slow-paths
Open

float3 wants to merge 8 commits into
cuthbertLab:masterfrom
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.

@mscuthbert

Copy link
Copy Markdown
Member

I've moved the Walk frames directly contribution to #2208 -- since it isn't directly related to MeterTerminal immutability, but makes all FrozenObjects benefit.

@mscuthbert
mscuthbert deleted the branch cuthbertLab:meter-speed-2026 November 3, 2026 03:53
@mscuthbert mscuthbert closed this Nov 3, 2026
@mscuthbert mscuthbert reopened this Nov 3, 2026
@mscuthbert
mscuthbert changed the base branch from meter-speed-2026 to master November 3, 2026 04:01
@mscuthbert

Copy link
Copy Markdown
Member

branch is set to master after 1972 is merged!

mscuthbert pushed a commit to float3/music21 that referenced this pull request Nov 3, 2026
inspect.stack() reads the source file and line for every frame, which made each attribute set inside FrozenDuration.__init__ take milliseconds. Following f_back finds the same __init__/__new__/__setstate__ frame without reading files: creating a FrozenDuration goes from about 12 ms to 0.02 ms.

From cuthbertLab#2200 by hill (float3).

(cherry picked from commit 3f99b5a)

AI-assisted (Claude)
The frame walk moved to cuthbertLab#2208, which applies it to every FrozenObject.
…meter-1972-slow-paths

# Conflicts:
#	music21/meter/base.py
#	music21/meter/core.py
# Conflicts:
#	music21/meter/base.py
#	music21/meter/core.py
@float3 float3 closed this Nov 4, 2026
@float3
float3 deleted the help/meter-1972-slow-paths branch November 4, 2026 15:29
@float3
float3 restored the help/meter-1972-slow-paths branch November 6, 2026 12:24
@float3

float3 commented Nov 6, 2026 •

Copy link
Copy Markdown
Contributor Author

accidentally closed; reopening

@float3 float3 reopened this Nov 6, 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.

3 participants