Skip to content

perf: снизить память и время генерации SARIF (analyze) — #4248#4275

Merged
nixel2007 merged 4 commits into
developfrom
perf/sarif-analyze-memory
Jul 15, 2026
Merged

perf: снизить память и время генерации SARIF (analyze) — #4248#4275
nixel2007 merged 4 commits into
developfrom
perf/sarif-analyze-memory

Conversation

@sfaqer

@sfaqer sfaqer commented Jul 15, 2026

Copy link
Copy Markdown
Member

Что и зачем

Issue #4248: analyze -r sarif на крупной конфигурации (23090 файлов, ~1.5M диагностик) растёт до 14–16 ГБ RSS и не доживает до записи SARIF. Разбор выявил два наложенных источника памяти; PR устраняет оба.

1. perf(reporters): потоковая сериализация SARIF

SarifReporter строил в памяти List<Result> (по одному Result на диагностику — на такой конфигурации миллионы) и целиком сериализовал дерево SarifSchema210 с INDENT_OUTPUT. Это создавало второй полный граф Result/Location/Region/Message поверх уже вычисленных lsp4j-диагностик — главный источник пика памяти при генерации отчёта, плюс раздутый файл и медленная запись из-за pretty-print.

Теперь отчёт пишется потоково через JsonGenerator: каркас (tool/invocations/…) + результаты по одному по мере обхода fileinfos, без промежуточного списка и без отступов. Дополнительно — дедуп строк в createResult (uri один раз на файл, текст сообщения один раз вместо двух).

2. perf(cli): разморозка документов в analyze

populateContext замораживает документы (freezeComputedData) ради переиспользования вторичных данных (сложность, метрики, данные подавления) в LSP-режиме. В пакетном анализе документ остаётся замороженным и в getFileInfoFromFile, поэтому финальный tryClearDocument не освобождает эти данные — они накапливаются на всю конфигурацию и в фазе анализа доводят RSS до OOM.

После построения FileInfo эти данные больше не нужны (метрики уже захвачены в FileInfo, сложность репортёрам не требуется) — размораживаем документ перед tryClearDocument.

Замеры

SARIF-репортёр (synthetic-харнесс, 500 000 диагностик):

время пик heap размер файла
было 27.8 с 1516 МБ 666 МБ
стало 1.8 с 951 МБ 429 МБ

→ 15× по времени, −37% пик, −36% файл. Ключевое: пик больше не масштабируется вторым деревом Result.

Фаза анализа (реальный analyze на SSL 3.1, 2197 модулей, Typo off, -Xmx4g):

время max live-heap (used-after-GC) sarif
без разморозки 45 с 2184 МБ 88 МБ
с разморозкой 47 с 1819 МБ 88 МБ

→ −365 МБ (−17%) удержанного heap при неизменных диагностиках и времени; на конфигурации из issue масштабируется в гигабайты.

Совместимость

Формат SARIF не изменился (кроме отступов) — существующий SarifReporterTest читает отчёт обратно в SarifSchema210 и проходит. AnalyzeCommandTest (4/4) зелёный.

Осталось за рамками

Стриминг-контракт всех репортёров (чтобы не копить все FileInfo/диагностики в AnalysisInfo) — бо́льший рефакторинг, кандидат в отдельный issue.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Performance Improvements
    • Reduced memory usage during batch analysis by releasing previously frozen document reuse data after each file is processed.
    • Improved SARIF report generation by streaming JSON directly to the output file, reducing peak memory and improving scalability for large reports.

sfaqer and others added 2 commits July 15, 2026 19:57
…льтатов

SarifReporter строил в памяти List<Result> (по одному Result на диагностику,
на крупной конфигурации — миллионы) и целиком сериализовал дерево SarifSchema210
с INDENT_OUTPUT. Это создавало второй полный граф Result/Location/Region поверх
уже вычисленных диагностик — главный источник пика памяти при генерации отчёта.

Теперь отчёт пишется потоково через JsonGenerator: каркас + результаты по одному
по мере обхода fileinfos, без промежуточного списка и без pretty-print. Плюс дедуп
строк в createResult (uri считается один раз на файл, message — один раз).

Замер (synthetic, 500k диагностик): время 27.8с->1.8с (15x), пик heap 1516->951 МБ
(-37%), размер файла 666->429 МБ (-36%). Формат неизменен (readback-тест зелёный).

См. #4248.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
…ых данных

populateContext замораживает документы (freezeComputedData) ради переиспользования
вторичных данных (сложность, метрики, данные подавления) в LSP-режиме. В пакетном
анализе документ остаётся замороженным и в getFileInfoFromFile, поэтому финальный
tryClearDocument не освобождает эти данные — они накапливаются на всю конфигурацию
и в фазе анализа доводят RSS до OOM на крупных конфигурациях.

После построения FileInfo эти данные больше не нужны (метрики уже захвачены в FileInfo,
сложность репортёрам не требуется) — размораживаем документ перед tryClearDocument.

Замер (реальный analyze на SSL 3.1, 2197 модулей, Typo off): max live-heap (used-after-GC)
2184->1819 МБ (-365 МБ, -17%) при неизменных диагностиках и времени. На конфигурации из
issue масштабируется в гигабайты.

См. #4248.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

Warning

Review limit reached

@sfaqer, you've reached your PR review limit, so we couldn't start this review.

Next review available in: 7 minutes

Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available.
You're only billed for reviews past your plan's rate limits ($0.25/file).

How can I continue?

After more reviews become available, a review can be triggered using the @coderabbitai review command as a PR comment. Alternatively, push new commits to this PR.

To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews.

How do review limits work?

CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability.

For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window.

Please refer docs for additional details.

Review details
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: 9683a8b6-254f-4712-bc3a-f06132a91dd2

📥 Commits

Reviewing files that changed from the base of the PR and between bf589e5 and 9a20ccb.

📒 Files selected for processing (1)
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/cli/AnalyzeCommand.java
📝 Walkthrough

Walkthrough

Batch analysis now releases frozen document data after FileInfo creation. SARIF reporting streams JSON directly to disk and emits diagnostic results individually instead of constructing the complete report in memory.

Changes

Analysis reporting updates

Layer / File(s) Summary
Release frozen document data
src/main/java/com/github/_1c_syntax/bsl/languageserver/cli/AnalyzeCommand.java
Batch analysis unfreezes computed document data after creating FileInfo.
Stream SARIF results
src/main/java/com/github/_1c_syntax/bsl/languageserver/reporters/SarifReporter.java
SARIF output uses Jackson’s JsonGenerator to write report metadata and diagnostic results incrementally; result construction now creates individual results for immediate emission.

Estimated code review effort: 3 (Moderate) | ~20 minutes

Sequence Diagram(s)

sequenceDiagram
  participant SarifReporter
  participant AnalysisInfo
  participant JsonGenerator
  SarifReporter->>JsonGenerator: Open output and write SARIF header
  SarifReporter->>AnalysisInfo: Iterate files and diagnostics
  AnalysisInfo-->>SarifReporter: Provide diagnostic data
  SarifReporter->>JsonGenerator: Write each result immediately
Loading
🚥 Pre-merge checks | ✅ 5
✅ Passed checks (5 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title clearly matches the main change: reducing memory and time for SARIF generation in analyze.
Docstring Coverage ✅ Passed No functions found in the changed files to evaluate docstring coverage. Skipping docstring coverage check.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/sarif-analyze-memory

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

Comment @coderabbitai help to get the list of available commands.

@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Test Results

 3 642 files  + 6   3 642 suites  +6   1h 50m 40s ⏱️ + 6m 1s
 3 608 tests +14   3 590 ✅ +14   18 💤 ±0  0 ❌ ±0 
21 648 runs  +84  21 536 ✅ +84  112 💤 ±0  0 ❌ ±0 

Results for commit 9a20ccb. ± Comparison against base commit c4fb3b7.

♻️ This comment has been updated with latest results.

sfaqer and others added 2 commits July 15, 2026 20:47
По ревью: разница в размере файла была именно от pretty-print, а не от данных.
Отступы возвращены через настройку маппера (JsonMapper.builder().enable(INDENT_OUTPUT)) —
createGenerator применяет их и к потоковой записи. Стриминг сохранён: генератор пишет
результаты по одному, память не растёт. Без отступов отчёт на миллионы результатов —
одна строка в сотни МБ, которую не открыть в редакторе.

См. #4248.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
Заморозка documentContext в populateContext — намеренная оптимизация: между populate
и вычислением диагностик файл не меняется, поэтому ленивые построенные данные не
пересчитываются (флаг влияет только на очистку). Фикс её НЕ отменяет — разморозка
происходит только на финальном tryClearDocument, после того как getDiagnostics/getMetrics
уже прочитаны и захвачены в FileInfo. Прежний комментарий вводил в заблуждение.

См. #4248.

Co-Authored-By: Claude Opus 4.8 <[email protected]>
@sonarqubecloud

Copy link
Copy Markdown

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

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants