Skip to content

perf(types): компактное представление односложного TypeSet#4188

Merged
nixel2007 merged 1 commit into
developfrom
perf/typeset-compact
Jun 22, 2026
Merged

perf(types): компактное представление односложного TypeSet#4188
nixel2007 merged 1 commit into
developfrom
perf/typeset-compact

Conversation

@nixel2007

@nixel2007 nixel2007 commented Jun 22, 2026

Copy link
Copy Markdown
Member

Что и зачем

TypeSet (returnType членов, типы выражений) — один из самых массовых объектов в системе типов. Канонический конструктор оборачивал любой набор ссылок в Collections.unmodifiableSet(new LinkedHashSet<>(...)), а LinkedHashSet внутри — это LinkedHashMap + 16-слотовый Node[]-массив. На конфигурации ERP-масштаба это ~6 млн односложных TypeSet, каждый из которых тратит ~241 байт инфраструктуры коллекции на одну полезную (и без того интернированную) TypeRef.

Изменение

  • Для наборов размера 0 и 1 — компактное неизменяемое представление (Collections.emptySet / Set.of), без LinkedHashSet и обёртки.
  • Для 2+ ссылок — порядок вставки по-прежнему сохраняется через LinkedHashSet.
  • Фабрики of() больше не аллоцируют LinkedHashSet дважды для односложного входа.
  • Контракт refs() : Set<TypeRef> и record-семантика equals/hashCode не меняются.

Замер (analyze на nixel2007/cpm, 13 090 файлов, -Xmx10g)

метрика baseline этот PR
live heap 5.04 ГБ 4.35 ГБ (−13.7%)
объектов 139.0M 128.2M
TypeSet-машинерия (LinkedHashSet+Map+Entry+Node[]+Unmodifiable) ~1.3 ГБ Set12 146 МБ
Object[] allocation pressure 74% 68%

JSON-отчёт analyze побайтно идентичен baseline (160 526 263 байт). TypeSetTest — 18/0/0.

Ортогонален #B (дедуп регистрации) и #C (интернирование) — складывается с ними; вместе A+B+C дают −45.1% heap.

🤖 Generated with Claude Code

Summary by CodeRabbit

  • Refactor
    • Optimized internal type reference management in TypeSet to use more efficient collection representations, reducing memory overhead for different set sizes while preserving insertion order and immutability guarantees.

Канонический конструктор TypeSet оборачивал любой набор ссылок в
Collections.unmodifiableSet(new LinkedHashSet<>(...)), а LinkedHashSet —
это LinkedHashMap с 16-слотовым Node[]-массивом. Для односложных наборов
(подавляющее большинство returnType'ов членов) это ~241 байт инфраструктуры
коллекции на одну полезную ссылку.

Теперь для наборов размера 0 и 1 используется компактное неизменяемое
представление (Collections.emptySet / Set.of), без LinkedHashSet и обёртки;
для двух и более ссылок сохраняется порядок вставки через LinkedHashSet.
Фабрики of() больше не аллоцируют LinkedHashSet дважды для односложного входа.
Контракт refs() как Set<TypeRef> и record-семантика equals/hashCode сохранены.

Замер на конфигурации nixel2007/cpm (13090 файлов, analyze): кластер
LinkedHashSet+LinkedHashMap+Entry+Node[]+UnmodifiableSet (~1.3 ГБ) схлопывается
в ImmutableCollections$Set12 (146 МБ); live heap 5.04 ГБ → 4.35 ГБ (-13.7%),
давление аллокаций Object[] 74% → 68%. JSON-отчёт побайтно идентичен.

Co-Authored-By: Claude Opus 4.8 (1M context) <[email protected]>
Claude-Session: https://claude.ai/code/session_01EWA4hy5YvUBU1CXkdr4npc
@coderabbitai

coderabbitai Bot commented Jun 22, 2026

Copy link
Copy Markdown
Contributor

Review Change Stack

📝 Walkthrough

Walkthrough

TypeSet replaces the unconditional unmodifiable LinkedHashSet wrapping of refs with a compactRefs method that returns Collections.emptySet() for empty sets, Set.of(...) for singletons, and an unmodifiable LinkedHashSet copy otherwise. The of factory overloads adopt switch-based size dispatch accordingly.

Changes

TypeSet refs normalization

Layer / File(s) Summary
compactRefs strategy and constructor/factory wiring
src/main/java/com/github/_1c_syntax/bsl/languageserver/types/model/TypeSet.java
Constructor now calls compactRefs(refs) instead of always building an unmodifiable LinkedHashSet; of(TypeRef...) and of(Collection<TypeRef>) use switch on size; compactRefs selects Collections.emptySet(), Set.of(...), or unmodifiable LinkedHashSet based on input cardinality.

Estimated code review effort

🎯 2 (Simple) | ⏱️ ~5 minutes

Poem

A rabbit once carried a set far too wide,
Now empty goes empty and singles step light,
For many, a LinkedHash still guards inside,
compactRefs chooses the bundle just right.
🐇✨ Less weight in the warren tonight!

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 20.00% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed The title directly describes the main optimization in the PR: implementing compact representation for single-element TypeSet instances, which is the core change in the changeset.
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.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch perf/typeset-compact

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.

@coderabbitai coderabbitai Bot 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.

Actionable comments posted: 1

🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/github/_1c_syntax/bsl/languageserver/types/model/TypeSet.java`:
- Around line 93-94: The TypeSet class has inconsistent null-handling between
the singleton and multi-element code paths: the singleton case using
Set.of(refs[0]) throws NullPointerException on null, while the multi-element
case using LinkedHashSet silently accepts null. Add explicit
Objects.requireNonNull(...) validation in the case 1 branch (for the singleton
path), in the default branch (for the multi-element path), and in the canonical
constructor to ensure consistent null rejection across all TypeSet creation
paths. This will prevent size-dependent behavior and ensure null inputs are
consistently rejected regardless of which factory method or path is used.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Path: .coderabbit.yaml

Review profile: CHILL

Plan: Pro

Run ID: e2b298d7-7409-4b7b-a2b6-6800063ed163

📥 Commits

Reviewing files that changed from the base of the PR and between 49181d7 and 085fa76.

📒 Files selected for processing (1)
  • src/main/java/com/github/_1c_syntax/bsl/languageserver/types/model/TypeSet.java

@nixel2007
nixel2007 merged commit 8f3d45a into develop Jun 22, 2026
37 of 38 checks passed
@nixel2007
nixel2007 deleted the perf/typeset-compact branch June 22, 2026 22:30
@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.

1 participant