Skip to content

MLP214: Serial TeX compile keys the local fork could precompile in parallel

qual reports MLP214 when, under a knowledge profile whose fork-capability overlay declares the submit-all/collect TeX API, at least 4 distinct MathTex / SingleStringMathTex / Tex compile keys are constructed serially before the scene's first play on a cold cache. Each distinct cold key blocks on one external TeX compiler + dvisvgm run; the fork's API submits every formula first and lets the later constructions join their in-flight jobs (measured on the calibration machine: 16 distinct cold formulas 120.7 s → 11.7 s, docs/research/perf-evidence.md — evidence, not a portable constant).

  • Default severity: info
  • Minimum confidence: high
  • Implementation phase: 3
  • Required capabilities: qualified-calls, lifecycle, local-fork-overlay
  • Fix: none — the DESIGN forbids a generic autofix here; the advice cites the curated entry points (manim.mobject.text.tex_mobject.MathTex.precompile, manim.utils.tex_file_writing.tex_to_svg_file_async) in the explanation

Fork gate (inert under upstream_0_20)

The rule reads KnowledgeProfile::tex_parallel_compile(), which is None for every profile that does not declare the capability — under upstream_0_20 the rule can never fire, so it never suggests an API the selected profile does not have (DESIGN 7.3). The declared entry_points are validated at profile load time to be curated symbols of the resolved profile.

Counting rules

  • Distinctness is by compile key, not by construction. The key is the effective tex_environment plus the arg_separator-joined TeX strings (fork defaults: MathTex/SingleStringMathTex join with " " into align*, Tex joins with "" into center). Constructing the same formula twice is ONE compile job.
  • Only literal-provable keys count. Every TeX argument must be a plain string literal; arg_separator / tex_environment must be literal or absent. An f-string or otherwise dynamic key, a *args / **kwargs splat, or a substrings_to_isolate / tex_to_color_map / tex_template keyword (which change the generated TeX file in ways the rule does not model) makes the construction non-countable — it never contributes to the threshold, and never blocks other keys either (the true distinct-job count can only be higher).
  • Cold cache only. The resource model starts cold at scene entry (first render, CacheAssumption::Cold, DESIGN 5.5). A construction that is not on every path (branch- or loop-dependent, Presence::Maybe) leaves its key's temperature unknown, and unknown is silence — never a counted cold job. Keys constructed after the first play are outside the pre-play prefix and are not counted. On a warm disk cache the advice is moot but harmless: the capability declares that an already-cached key returns an immediately resolved future.
  • "Before first use" is read from the interpreter's event trace, so constructions inside inlined helpers count in true program order. The provable prefix ends at the first play or the first unresolved call that may itself reach Scene.play; a scene with summary-derived plays (recursion / depth-cap / unresolvable helper fallbacks) is skipped entirely — a hidden play would break the ordering proof.

One diagnostic is emitted per scene, anchored at the first counted construction; the other distinct keys are attached as related locations, and the evidence lists the counted keys, the curated entry points, the cache assumption, and the first-use boundary.

Interaction with the Cairo fork pipeline

When the capability declares in_flight_blocks_cairo_fork, the explanation adds that every future should be collected before the first play: in-flight TeX workers force the fork's Cairo fork-per-play pipeline into serial fallback. This is explanation text, not a separate diagnostic (the fork-loss causality itself belongs to MLP225's cost report).

Example

class Demo(Scene):
    def construct(self):
        first = MathTex(r"\int_0^1 x^2 \, dx")     # MLP214 anchors here
        second = MathTex(r"\sum_{n=1}^{\infty} \frac{1}{n^2}")
        third = MathTex(r"e^{i\pi} + 1 = 0")
        fourth = Tex(r"Euler's identity")
        again = MathTex(r"\int_0^1 x^2 \, dx")     # duplicate: ONE job
        self.play(Write(first), Write(second), Write(third), Write(fourth))

Silent near misses: three distinct keys; duplicate-only batches; keys built after the first play; f-string keys; a tex_template keyword; branch-dependent constructions; any profile without the declared capability.