MLP210: fixed-count loop issues many short sequential plays¶
qual reports MLP210 when a self.play(...) statement is a
direct child of a for loop whose literal trip count (an int-literal
range(...) or a display literal) proves at least 8 iterations: every
iteration pays the full play startup — animation compile, hash, begin
starting copies, cleanup — before a single frame renders.
- Default severity:
info - Minimum confidence:
medium(the loop itself may be conditionally reached; the per-iteration count is proven) - Implementation phase:
3 - Fix: none (grouping into one play changes pacing choices)
Threshold (documented constant in rules/performance/loop_plays.rs):
MLP210_LOOP_PLAYS_GATE = N_plays >= 8.
Gates that keep the count honest: the play must be a direct statement
of the loop body (a play behind an if does not run once per
iteration), the body must contain no break / continue / return /
raise, and the trip count must be a literal. Per DESIGN 7.3, the
per-play partial-stream boundary cost is not included — no
OutputState fact proves a per-play output stream exists (a continuous
writer has none) — so the overhead is described qualitatively.
Wrong¶
class Demo(Scene):
def construct(self):
square = Square()
for _ in range(10):
self.play(FadeIn(square), run_time=0.5)
Right¶
class Demo(Scene):
def construct(self):
dots = VGroup(*[Dot() for _ in range(10)])
self.play(LaggedStart(*[FadeIn(dot) for dot in dots]), run_time=5)
AnimationGroup runs the animations together; LaggedStart reproduces
the one-after-another stagger — either way the play startup is paid
once.