Skip to content

perf(reader): CI skip-list кэши → get-first ConcurrentHashMap#644

Merged
nixel2007 merged 2 commits into
developfrom
perf/transformationutils-hashmap-cache
Jul 15, 2026
Merged

perf(reader): CI skip-list кэши → get-first ConcurrentHashMap#644
nixel2007 merged 2 commits into
developfrom
perf/transformationutils-hashmap-cache

Conversation

@nixel2007

Copy link
Copy Markdown
Member

Описание

Профилирование чтения (JFR на cpm) показало, что кэши TransformationUtils
(METHODS/TYPES/SETTERS) на ConcurrentSkipListMap c CASE_INSENSITIVE_ORDER
крупнейший прикладной hotspot чтения: ~6 % CPU на ConcurrentSkipListMap.doGet +
StringLatin1.compareToCI при поиске метода/типа по имени XML-узла.

Регистронезависимость нужна только при первом резолве (скан equalsIgnoreCase по методам
класса), а не на каждый поиск. Переводим кэши на ConcurrentHashMap с точным ключом-именем
узла (имена узлов повторяются и интернированы Xerces) — поиск O(1) по хешу без CI-сравнений.

Доступ к кэшу — get()-first, а не computeIfAbsent с захватывающей лямбдой:
ConcurrentHashMap.computeIfAbsent не встраивается JIT-ом, из-за чего лямбда отображения
перестаёт скаляризоваться escape-анализом и аллоцируется на каждый вызов (профиль поймал
405 сэмплов аллокации лямбд, +170 МБ/чтение у наивного варианта). get()-first убирает это.

Стекнут на #643 (setter-descriptor cache) — та же область TransformationUtils. База PR —
ветка #643; после её мержа переведу базу на develop.

Замеры (JMH MDClassesBenchmark на cpm; CPU — доля JFR-сэмплов, устойчива к шуму машины)

метрика before (#643) after
доля CI-поиска в CPU чтения 6.0 % 1.1 % (−4.9 п.п.)
аллокации кэш-лямбд 0 0 (наивный CHM давал 405 сэмплов / +170 МБ — устранено)
аллокации всего 5078 МБ/чтение 5077 МБ/чтение (без изменений)
wall (avgt) 9065 мс 8887 мс (в пределах шума)

CPU-выигрыш реальный и измерен через долю сэмплов (не зависит от разброса wall). Wall — в
пределах шума машины (чтение параллельное, GC 5 ГБ/op).

Связанные задачи

Closes

Чеклист

Общие

  • Ветка PR обновлена из develop
  • Отладочные, закомментированные и прочие, не имеющие смысла участки кода удалены
  • Изменения покрыты тестами (TransformationUtilsTest; интеграционные MDOReaderTest зелёные)
  • Обязательные действия перед коммитом выполнены (gradlew precommit) — прогонял reader-тесты

Дополнительно

Регресс аллокации лямбд у наивного ConcurrentHashMap.computeIfAbsent пойман профилированием —
в PR только исправленный get()-first вариант.

…t ConcurrentHashMap

Профилирование чтения показало, что кэши TransformationUtils (METHODS/TYPES/SETTERS)
на ConcurrentSkipListMap с компаратором CASE_INSENSITIVE_ORDER — крупнейший прикладной
hotspot чтения: ~6% CPU уходит на ConcurrentSkipListMap.doGet + StringLatin1.compareToCI
при поиске метода/типа по имени XML-узла.

Регистронезависимость нужна лишь при первом резолве (скан equalsIgnoreCase по методам
класса), а не на каждый поиск в кэше. Переводим кэши на ConcurrentHashMap с точным
ключом-именем узла (имена узлов повторяются и интернированы Xerces) — поиск O(1) по хешу
без CI-сравнений. Первый резолв по-прежнему матчит имя регистронезависимо.

Доступ к кэшу — get()-first, а не computeIfAbsent с захватывающей лямбдой:
ConcurrentHashMap.computeIfAbsent не встраивается JIT-ом, из-за чего лямбда-функция
отображения перестаёт скаляризоваться escape-анализом и аллоцируется на каждый вызов.
На попадании в кэш (горячий путь) лямбда не создаётся.

Замеры (JMH MDClassesBenchmark на cpm, JFR sample share — устойчив к шуму машины):
- доля CI-поиска в CPU чтения: 6.0% -> 1.1% (-4.9 п.п.);
- аллокации: без изменений (5078 -> 5077 МБ/чтение), лямбды кэша не аллоцируются;
- wall: 9065 -> 8887 мс (в пределах шума).

Поведение не меняется. Тесты TransformationUtilsTest и интеграционные MDOReaderTest зелёные.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLvkKQCwBjTHwuZ4EGBQK6
@coderabbitai

coderabbitai Bot commented Jul 15, 2026

Copy link
Copy Markdown

Important

Review skipped

Auto reviews are disabled on base/target branches other than the default branch.

Please check the settings in the CodeRabbit UI or the .coderabbit.yaml file in this repository. To trigger a single review, invoke the @coderabbitai review command.

⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: a8c5b447-3eb8-408e-b4f0-21c5e5b58ecf

You can disable this status message by setting the reviews.review_status to false in the CodeRabbit configuration file.

Use the checkbox below for a quick retry:

  • 🔍 Trigger review
✨ Finishing Touches
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/transformationutils-hashmap-cache

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.

Comment thread src/main/java/com/github/_1c_syntax/bsl/reader/common/TransformationUtils.java Outdated
@github-actions

github-actions Bot commented Jul 15, 2026

Copy link
Copy Markdown
Contributor

Test Results

  516 files  ±0    516 suites  ±0   12m 46s ⏱️ -5s
1 256 tests ±0  1 253 ✅ ±0   3 💤 ±0  0 ❌ ±0 
7 560 runs  ±0  7 542 ✅ ±0  18 💤 ±0  0 ❌ ±0 

Results for commit 61dbe30. ± Comparison against base commit 4c3abce.

♻️ This comment has been updated with latest results.

get()-first проверял значение кэша (Optional) на null — Sonar S2789 (Optional не
должен быть null). Убираем Optional из значений кэшей METHODS/TYPES/SETTERS: храним
сырые Method/Type/SetterDescriptor, а отрицательный результат — через сентинелы
(в ConcurrentHashMap нельзя хранить null). Перф opt-A сохранён: CI-поиск ~1% CPU,
аллокации 5078 МБ/чтение (без изменений), лямбды кэша не аллоцируются.

Заодно упрощён perClassCache: лямбда k -> new ConcurrentHashMap<>() незахватывающая
(синглтон), поэтому computeIfAbsent там безопасен по аллокациям — get()-first обёртка
не нужна.

Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
Claude-Session: https://claude.ai/code/session_01HLvkKQCwBjTHwuZ4EGBQK6
@sonarqubecloud

Copy link
Copy Markdown

Base automatically changed from perf/transformationutils-setter-cache to develop July 15, 2026 19:11
@nixel2007
nixel2007 merged commit 124819c into develop Jul 15, 2026
19 checks passed
@nixel2007
nixel2007 deleted the perf/transformationutils-hashmap-cache branch July 15, 2026 19:12
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.

1 participant