Skip serializing bulk edits when debug logging is off by KRRT7 · Pull Request #341765 · microsoft/vscode · GitHub
Skip to content

Skip serializing bulk edits when debug logging is off - #341765

Merged
Connor Peet (connor4312) merged 1 commit into
microsoft:mainfrom
KRRT7:bulkedit-skip-debug-serialization
Oct 15, 2026
Merged

Connor Peet (connor4312) merged 1 commit into
microsoft:mainfrom
KRRT7:bulkedit-skip-debug-serialization

Conversation

@KRRT7

Copy link
Copy Markdown

Every bulk edit serializes its full edit list with JSON.stringify, even when debug logging is off. Each BulkEdit._perform*Edits call did:

this._logService.debug('_performTextEdits', JSON.stringify(edits));

The string is built before debug() checks the log level, so it is built and then thrown away at the default Info level. For Search Replace All that is one stringify of every match on the UI thread. On 10k matches, it was the largest JS self-time entry in the profile.

This PR moves the four call sites into a _logEdits helper that only serializes when canLog(getLevel(), LogLevel.Debug) is true. canLog also handles LogLevel.Off. Log output is unchanged when debug logging is on.

Measurements

The benchmark ran BulkEditService.apply with real TextModels and ModelService. Only the I/O and UI services were stubbed. Each Replace All was 10k foo→bar edits. Before and after were built from main (02001a6) and this branch as identical bundles on both machines. Each process ran 10 warmup applies, then 50 measured ones. Before and after alternated each round, with 15 rounds per side.

Instructions per apply (hardware counter, startup and warmup subtracted by differencing 0 vs 50 iterations):

Scenario Linux, EPYC 9575F VM (perf, instructions:u) macOS, Apple M3 (time -l)
100 files × 100 matches 350.3M → 256.7M (−26.7%, 95% CI [−26.8, −26.7]) 348.7M → 259.7M (−25.5%, [−26.4, −25.5])
1000 files × 10 matches 416.4M → 314.4M (−24.5%, [−24.5, −24.5]) 413.2M → 314.8M (−23.8%, [−24.0, −23.7])

Wall-clock per apply (per-process median):

Scenario Linux (pinned to 1 core) macOS
100 × 100 41.6 → 32.4 ms (−22.1%, [−26.1, −19.4]) 16.8 → 12.4 ms (−26.2%, [−28.6, −25.2])
1000 × 10 61.1 → 54.0 ms (−11.7%, [−15.3, −7.5]) 20.0 → 15.7 ms (−21.2%, [−23.0, −20.0])

In all eight comparisons, every after run beat every before run. Mann–Whitney p ≤ 4e-05; the confidence intervals are bootstrap 95%. In CPU profiles, self time in _performTextEdits dropped from 17–23% to about 0% on both machines. V8 attributes the JSON.stringify built-in to its caller, so that is where the stringify cost showed up.

These numbers cover only the bulk-edit step of Replace All, not the full end-to-end operation.

How to test

  • Search for a common token across a workspace, then Replace All: the edits apply and the files save as before.
  • With the default log level (Info), the Window log has no _performTextEdits line after Replace All.
  • Launched with --log debug, Replace All still writes the [debug] _performTextEdits [...] line with every edit serialized (6 of 6 in a 3-file test) to the Window log.
  • npm run typecheck-client is clean. scripts/test.sh --grep "BulkEdit|BulkFileEdits|BulkCellEdits|MainThreadEditors|MainThreadBulkEdits" passes.

Copilot AI balanced review requested due to automatic review settings October 15, 2026 13:04
BulkEdit called JSON.stringify(edits) for every _perform*Edits call and
passed the result to ILogService.debug, so the full edit list was
serialized even when debug logging was disabled and the string was
discarded. For Search "Replace All" this means one large stringify of
every match on the UI thread. Only serialize when debug logging is on.

Co-authored-by: wtfyd-agent[bot] <328989958+wtfyd-agent[bot]@users.noreply.github.com>
Signed-off-by: Kevin Turcios <turcioskevinr@gmail.com>
@KRRT7
Kevin Turcios (KRRT7) force-pushed the bulkedit-skip-debug-serialization branch from bef6fd0 to 7befad3 Compare October 15, 2026 13:07

Copilot AI left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

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

🟢 Approval recommended

The narrow logging guard preserves existing behavior, and no actionable issues remain.

0 open findings

What changed in this PR

Avoids unnecessary bulk-edit serialization when debug logging is disabled, reducing overhead for operations such as Search Replace All.

Changes:

  • Centralizes four logging calls in _logEdits.
  • Guards serialization with canLog, preserving enabled debug output and edit execution.
File Description
src/​vs/​workbench/​contrib/​bulkEdit/​browser/​bulkEditService.ts Adds guarded logging for file, text, notebook cell, and attachment edits.

🧠 Review effort: Balanced


💡 Configure MCP servers for context-aware, tailored reviews. Learn more in the docs.

@connor4312
Connor Peet (connor4312) merged commit 989f3f0 into microsoft:main Oct 15, 2026
35 checks passed
@vs-code-engineering vs-code-engineering Bot added this to the 1.143.0 milestone Oct 15, 2026
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.

5 participants