Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

perf(renderer): diff code lines in O(n) via a prev-line map#706

Open
ly-wang19 wants to merge 1 commit into
THU-MAIC:main from
ly-wang19:perf/renderer-code-line-diff
Open

perf(renderer): diff code lines in O(n) via a prev-line map #706
ly-wang19 wants to merge 1 commit into
THU-MAIC:main from
ly-wang19:perf/renderer-code-line-diff

Conversation

@ly-wang19

@ly-wang19 ly-wang19 commented Jun 9, 2026

Copy link
×ばつ prevLines) = O(n2)**. Code blocks routinely run 50-300 lines and this fires repeatedly during the typing animation, so it's an on-render hot path. ## Fix One `Map<id, prevLine>`, one O(n) pass. Insert and replace were already mutually exclusive (a line either isn't in prev → inserted, or is in prev with changed content → replaced), so the merged `if/else if` is behavior-identical and also drops the redundant second iteration. Consistent with the `Set` the first loop already used. ```ts const prevById = new Map(prevLinesRef.current.map((l) => [l.id, l])); for (const line of lines) { const prev = prevById.get(line.id); if (!prev) states.set(line.id, { type: "inserted", timestamp: 0 }); else if (prev.content !== line.content) states.set(line.id, { type: "replaced", timestamp: 0 }); } ``` ## Verification - `prettier` clean, `eslint` clean, `tsc --noEmit` error count unchanged (the only errors are pre-existing unresolved-workspace-module noise; the edited region type-checks clean). - Behavior preserved: identical `states` map for any (prev, current) line pair — `typing` on first render, `inserted` for new ids, `replaced` for same id + changed content, unchanged otherwise. Closes #705 " data-view-component="true"> Copy Markdown
Contributor

Problem

BaseCodeElement's line-diff useEffect runs on every [lines, animate] change — i.e. on each streaming update while a code block types out. It built a Set for the insert check (O(1)), then did a second pass with a linear prevLinesRef.current.find(...) per line for the content/replace check:

const prevIds = new Set(prevLinesRef.current.map((l) => l.id));
for (const line of lines) {
 if (!prevIds.has(line.id)) states.set(line.id, { type: "inserted", timestamp: 0 });
}
for (const line of lines) {
 const prev = prevLinesRef.current.find((p) => p.id === line.id); // O(prevLines) per line
 if (prev && prev.content !== line.content) states.set(line.id, { type: "replaced", timestamp: 0 });
}

That second loop is O(lines ×ばつ prevLines) = O(n2). Code blocks routinely run 50-300 lines and this fires repeatedly during the typing animation, so it's an on-render hot path.

Fix

One Map<id, prevLine>, one O(n) pass. Insert and replace were already mutually exclusive (a line either isn't in prev → inserted, or is in prev with changed content → replaced), so the merged if/else if is behavior-identical and also drops the redundant second iteration. Consistent with the Set the first loop already used.

const prevById = new Map(prevLinesRef.current.map((l) => [l.id, l]));
for (const line of lines) {
 const prev = prevById.get(line.id);
 if (!prev) states.set(line.id, { type: "inserted", timestamp: 0 });
 else if (prev.content !== line.content) states.set(line.id, { type: "replaced", timestamp: 0 });
}

Verification

  • prettier clean, eslint clean, tsc --noEmit error count unchanged (the only errors are pre-existing unresolved-workspace-module noise; the edited region type-checks clean).
  • Behavior preserved: identical states map for any (prev, current) line pair — typing on first render, inserted for new ids, replaced for same id + changed content, unchanged otherwise.

Closes #705

BaseCodeElement diffed each render against the previous one with a
linear prevLinesRef.current.find() per line, on top of a separate Set
pass — O(lines x prevLines). This effect runs on every streaming update
while a code block types out, and code blocks routinely run dozens to
hundreds of lines, so the quadratic cost lands on a render hot path.
Build a single Map<id, prevLine> and fold the insert/replace checks into
one O(n) pass. Behavior is identical: a line missing from the previous
render is "inserted", a line present with changed content is "replaced"
(the two were already mutually exclusive).
Closes THU-MAIC#705 
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Reviewers

No reviews

Assignees

No one assigned

Labels

None yet

Projects

None yet

Milestone

No milestone

Development

Successfully merging this pull request may close these issues.

perf(renderer): O(n2) line diffing in code-block typing animation

1 participant

AltStyle によって変換されたページ (->オリジナル) /