Skip to content

Replace animation frame queue sort with binary heap#319734

Open
Xio-Shark wants to merge 2 commits into
microsoft:mainfrom
Xio-Shark:perf/animation-frame-queue-binary-heap
Open

Replace animation frame queue sort with binary heap#319734
Xio-Shark wants to merge 2 commits into
microsoft:mainfrom
Xio-Shark:perf/animation-frame-queue-binary-heap

Conversation

@Xio-Shark
Copy link
Copy Markdown

Summary

  • Replace the per-frame Array.sort() + Array.shift() pattern in the animation frame runner with a max-heap (AnimationFrameQueue), reducing queue operations from O(n log n) to O(log n) per push/pop.
  • Add an insertion-order counter (_order) to AnimationFrameQueueItem so callbacks with the same priority execute in FIFO order — the previous sort was not stable.
  • Make _runner and _priority readonly since they never change after construction.

Motivation

Every animation frame, the runner called currentQueue.sort() on the entire queue and then shift() the first element. For workloads that schedule many animation frame callbacks (e.g. editor rendering, cursor blinking, overlays), this is wasteful: a heap provides O(log n) insertion and extraction while naturally maintaining priority order.

Changes

  • src/vs/base/browser/dom.ts: New AnimationFrameQueue class backed by a binary max-heap with runsBefore() comparison (priority desc, then insertion order asc). NEXT_QUEUE and CURRENT_QUEUE now hold AnimationFrameQueue instances instead of plain arrays.
  • src/vs/base/test/browser/dom.test.ts: Four new tests covering priority ordering, FIFO stability with 128 items, cancellation, and mid-frame scheduling.

Verification

  • tsgo --project ./src/tsconfig.json --noEmit --skipLibCheck — passed
  • node build/eslint.ts — passed
  • scripts/test.sh --grep "AnimationFrameQueue" — 4/4 passing
  • Full dom test suite — 41/41 passing, no regressions
  • Pre-commit hygiene hook — passed

Test plan

  • CI compile & hygiene checks pass
  • All unit tests pass on Linux / macOS / Windows (Electron + Browser)
  • No regressions in animation frame scheduling behavior

The animation frame runner previously sorted the entire queue
(Array.sort + Array.shift) on every frame, which is O(n log n).
Replace this with a max-heap (AnimationFrameQueue) that provides
O(log n) push and pop while preserving FIFO order within the same
priority level via an insertion-order counter.
Copilot AI review requested due to automatic review settings June 3, 2026 13:42
Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Pull request overview

Note

Copilot was unable to run its full agentic suite in this review.

This PR improves the determinism and performance of animation-frame scheduling by replacing per-iteration array sorting with a stable priority queue, and adds browser tests to validate queue ordering/cancellation behavior.

Changes:

  • Replace array-based animation frame queues (sort+shift) with a stable heap-based queue (push+pop).
  • Preserve insertion order for callbacks with the same priority via an explicit order field.
  • Add browser tests covering priority ordering, stability, cancellation, and re-entrancy (callbacks enqueueing more work during a frame).

Reviewed changes

Copilot reviewed 2 out of 2 changed files in this pull request and generated 2 comments.

File Description
src/vs/base/test/browser/dom.test.ts Adds tests to validate animation-frame queue ordering, stability, and cancellation.
src/vs/base/browser/dom.ts Introduces a stable heap-backed animation frame queue and updates scheduling logic to use it.

Comment on lines +563 to +566
assert.deepStrictEqual(
calls,
items.toSorted((a, b) => b.priority - a.priority || a.index - b.index).map(item => item.index)
);
Comment on lines +426 to +428
setOrder(order: number): void {
this._order = order;
}
- Replace Array.prototype.toSorted with [...items].sort() for broader
  JS runtime compatibility in test environments.
- Guard setOrder against re-assignment to prevent silent heap
  corruption; order is now effectively one-time via a -1 sentinel.
@Xio-Shark
Copy link
Copy Markdown
Author

@microsoft-github-policy-service agree

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

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants