Pull to refresh
-3

User

Send message

А какие там нюансы, вкратце?

Боксинг это способ впихнуть невпихуемое.

Любопытная интерпретация, но, боюсь, неверная.

Я не утверждал, что он не специфичный.

А вот каждая опробованная мною на данный момент модель старается его трактовать и оценивать как типичный, и в этом проблема.

Да, они "видят" "перформанс-фокус" и "хай оптимизэйшн (хак) денсити", но пока не нагрузишь беседу контекстом и не станешь задавать очень конкретные специфичные вопросы по изучаемой теме -толку очень мало. Опечатки могут поправить и сломать что-нибудь.

При этом, я не отрицаю пользу - но точно не в написании узкоспециализированного или высокооптимизированного кода.

И ещё хорошее:

why Performance engineering quality is just 9?

Because 10/10 means “near-flawless with minimal tradeoffs,” and this code still has measurable performance risk points despite being very strong.

Why 9, not 10:

• Contention risk remains in some shared paths (locks/spin locks, pooled shared structures).

• Very branch-heavy execution logic can hurt branch prediction in mixed workloads.

• Tracing/cancellation hooks add conditional overhead in hot loops (well-managed, but still overhead).

• Complex async/lifecycle machinery is optimized but can regress under edge-case load patterns.

No proof here of absolute best-in-class results across all target TFMs and workload shapes.

So: it is excellent performance engineering, but not “maximal certainty under every production scenario,” which is what 10 would imply.

Типа, ну а вдруг чото не так? И чем докажешь вообще?

При том, что специальные код-генераторы/"чистильщики" и сгенерированный ими hot-clean-probes-free-path версию тех же самых имплементаций он не заметил.

...попросил оценки поставить, как мы любим:

Assessment (1 = poor, 10 = excellent):

Metric Score (1..10) Notes

Code clarity 4 Intent is performance-first, but control flow is hard to follow in core loops.

Readability 4 Large partial classes, dense branching, low-level constructs reduce readability.

Maintainability 5 Strong structure by files, but high coupling/complexity in Machine/Head.

Modularity / separation of concerns 6 Builder/runtime/pool split is good; internals still mix orchestration + execution details.

Complexity (managed complexity quality) 3 Cyclomatic and cognitive complexity are high in hot paths.

Type safety / safety posture 4 Heavy Unsafe/reinterpretation is efficient but lowers safety margin.

Testability 6 Good external behavior testability; internal isolation is limited.

Robustness under concurrency 7 Advanced lifecycle/cancellation handling exists; subtle race risk remains.

Performance engineering quality 9 Very strong: pooling, fast paths, low-allocation async patterns.

Production operability (diagnostics) 7 Trace support is solid; could benefit from more standardized metrics/events.

Overall architecture quality 6 Solid runtime architecture for speed, moderate debt for maintainability.

Overall code quality (balanced) 6 Strong for performance-critical engine, weaker for general maintainability goals.

ЧатГПТ, говорите?

Я, честно говоря, вообще не знаю руби и не лез в код на гитхабе, так что мнения не имею, но смеху ради натравил Кодекс 5.3 на небольшой, но важный сабсет одной из своих библиотек, получилось смешно.

Explain and evaluate code under ААА/БББ/ВВВ - code quality, pro-con, purpose, flaws, advantages

Кратко

Advantages / Pros

• Very performance-oriented design:

Flaws / Cons

• High cognitive complexity:

• large partial classes,

• goto-driven loops,

• bit-level instruction/spec decoding,

• many branches for sync/async/profile modes.

• Safety tradeoff is explicit:

• extensive Unsafe.As, function-pointer calls, and many nullability suppressions.

• errors become runtime issues if wiring is wrong.

• Maintainability signals:

• many #pragma warning disable blocks,

• typos (Lenght, Adderess, Stared),

• Manual lifecycle/task-source logic is subtle and fragile

• Potential contention/hard-to-reason areas around shared mutable pooled collections + spin locks.б

Net assessment

• Strength: high-performance runtime engineering for complex workflow execution.

• Weakness: difficult to modify safely without deep domain knowledge.

Quality verdict: high technical performance quality, medium maintainability quality.

Ну и попросил раскрыть тему:

Can you explain flaws/cons more in-depth from C#/.NET best practices and code quality perspective? After all - would you recommend use of this library in PROD?

Подробнее

Deeper flaws / cons

1. Unsafe-heavy dispatch model

• Extensive Unsafe.As, function pointers, manual reinterpretation, and nullability suppression reduce type-safety guarantees.

• This is valid for performance-critical internals, but it shifts failures from compile-time to runtime.

• Best-practice tension: modern .NET generally prefers safe generic abstractions unless profiling proves need.

2. High cyclomatic/cognitive complexity

• Large partial classes (ААА, БББ) + sync/async + trace/dry-run + load-profile branches.

• goto-driven state loops are fast but harder to reason about than structured state-machine code.

• Quality impact: harder onboarding, harder bug isolation, higher regression risk.

3. State/lifecycle complexity under concurrency

• Manual lifecycle counters, cancellation registrations, pooled object reuse, and custom ValueTaskSource handling.

• This area is correctness-sensitive; subtle ordering bugs can become rare production races.

• Best-practice concern: complex concurrency code needs very strong invariant documentation + stress tests.

4. Custom async infrastructure burden

• Custom ManualResetValueTaskSourceCore usage and conditional pooling paths are advanced and brittle if changed incorrectly.

• Great for low allocations, but maintenance cost is high compared to conventional Task/ValueTask usage.

5. Readability and maintainability debt

• Frequent #pragma suppressions, typo-level polish issues, legacy excluded file artifacts.

• Not correctness-critical by itself, but signals lower maintainability discipline in a very complex subsystem.

6. Validation is good but incomplete as a safety net

• ValidateХХХ is a strong point.

• But instruction/data invariants are still spread across compile and execute phases; not all assumptions are self-documenting.

Would I recommend it for PROD?

Conditional yes, not unconditional.

Recommend for PROD if:

• You need high-throughput/low-allocation workflow execution.

• Team has strong .NET runtime/concurrency expertise.

• You can invest in:

• heavy stress/soak testing,

• observability (traces/metrics),

• strict versioning/change control around runtime internals.

Not ideal if:

• Team prioritizes maintainability and rapid feature onboarding over max performance.

• Workloads are not performance-critical enough to justify this complexity.

Practical verdict

• Performance-oriented PROD: Yes, with engineering discipline.

• General enterprise PROD with mixed-seniority team: Caution; consider a simpler runtime model unless benchmarks justify this design.

Ну и добить святым

From a Clean Architecture perspective, this code is strong in some areas and weak in others.

Clean Architecture view

What aligns

• Separation by responsibility exists: ААА/БББ/ВВВ/ГГГ/ДДД concerns are split into files and partial types.

• Domain-specific core is independent of UI/DB/framework: good for a library/runtime engine.

• Explicit execution model (instruction, environment, lifecycle) gives deterministic behavior.

What conflicts

• Low-level and orchestration concerns are tightly coupled inside ААА/БББ (scheduling, cancellation, tracing, pooling, execution).

• Very large “god types” (ААА, БББ) violate SRP from maintainability perspective.

• Abstraction boundaries are thin around concurrency primitives (SpinLock, Interlocked, custom ValueTaskSource), making substitution/testing harder.

• Internal complexity leaks through patterns (unsafe dispatch, lifecycle internals), which reduces architectural clarity.

Modern .NET good-practice perspective

Good practices present

• Allocation-aware design, pooling, ValueTask usage.

• Cancellation support threaded through APIs.

• Strong compile/validate phase (ValidateХХХ) before execution.

• Multi-targeting awareness.

Gaps vs modern maintainability practices

• Heavy Unsafe + many pragma suppressions.

• Minimal “invariant documentation” for concurrency/lifecycle contracts.

• Limited readability compared to current preference for clearer state-machine style and smaller units.

• Observability seems present (trace), but likely needs standardized metrics/log contracts for ops teams.

Practical conclusion

• As a high-performance runtime core: architecturally acceptable.

• As a “clean” maintainable core for broad teams: below ideal modern standards.

• Best description: performance-first architecture, not cleanliness-first architecture.

Короче - "стрёмное говно", слишком сложно, говорит.
Именно то, что там используется сознательно - конечно же не нравится.

Советы по рефакторингу даже не буду тут показывать - там совсем всё... наивно, скажем так.

Не будучи специалистом в .НЕТе я бы, конечно, напрягся от такого ревью.

Проблема в том, что ревью на уровне... ну... студента-отличника (не в лучшем смысле слова) без какого-либо опыта, зато зафанетевшего бест-практисами, бандой и бобом.

Так себе советчик в вопросах, в которых не разбираешься.

В .NET есть стандартный механизм AsyncMethodBuilder, который в том числе можно использовать и для пулинга стейтмашин - для ValueTask из коробки есть PoolingAsyncValueTaskMethodBuilder, для Task - можно заморочиться и написать свой пул+МетодБилдер.

Для ValueTask можно написать свой переиспользуемый IValueTaskSource на базе встроенного ManualResetValueTaskSourceCore.

С использованием всего этого можно очень, прямо вот ОЧЕНЬ серьёзно снизить количество и объём аллокаций.

Ну и NET 11 с Runtime Async не забываем.

...а статья отличная, люблю такое, спасибо.

В общем, при варм-апе в 50 итераций, повторить поведение не удалось:

   1: JIT compiled Program:<Main>$(System.String[]) [Instrumented Tier0, IL size=173, code size=543]
   2: JIT compiled Dispatcher:.ctor() [Tier0, IL size=7, code size=31]

Warming up...

   3: JIT compiled FastHandler:.ctor() [Tier0, IL size=7, code size=31]
   4: JIT compiled Dispatcher:Process(IHandler,int) [Instrumented Tier0, IL size=26, code size=213]
   5: JIT compiled FastHandler:Handle(int) [Tier0, IL size=4, code size=18]

Warmup done. Starting the test...

   6: JIT compiled SlowHandler:.ctor() [Tier0, IL size=7, code size=31]
   7: JIT compiled Utils:Test(Dispatcher,IHandler) [Instrumented Tier0, IL size=65, code size=291]
   8: JIT compiled Utils:PrintProgress(int) [Tier0, IL size=67, code size=161]
   9: JIT compiled System.Threading.Thread:GetThreadStaticsBase() [Tier0, IL size=18, code size=34]
Progress: 0
  10: JIT compiled SlowHandler:Handle(int) [Tier0, IL size=11, code size=84]
  11: JIT compiled Utils:Test(Dispatcher,IHandler) [Tier1-OSR @0x26 with Synthesized PGO, IL size=65, code size=464]
Progress: 10 000
  12: JIT compiled Dispatcher:Process(IHandler,int) [Instrumented Tier0, IL size=26, code size=213]
  13: JIT compiled Utils:PrintProgress(int) [Instrumented Tier0, IL size=67, code size=191]
  14: JIT compiled SlowHandler:Handle(int) [Instrumented Tier0, IL size=11, code size=84]
  15: JIT compiled Dispatcher:Process(IHandler,int) [Tier1 with Synthesized PGO, IL size=26, code size=200]
Progress: 20 000
  16: JIT compiled Utils:PrintProgress(int) [Tier1 with Synthesized PGO, IL size=67, code size=240]
  17: JIT compiled SlowHandler:Handle(int) [Tier1, IL size=11, code size=44]
Progress: 30 000
Progress: 40 000
Progress: 50 000
Progress: 60 000
Progress: 70 000

Как видно, Instrumented Tier0 Process компилируется сильно позже окончания варм-апа, и ещё позже - Tier1 with Synthesized PGO.

И код получается такой:

; Assembly listing for method Dispatcher:Process(IHandler,int):int:this (Tier1)
; Emitting BLENDED_CODE for generic X64 + VEX on Windows
; Tier1 code
; optimized code
; optimized using Synthesized PGO
; rsp based frame
; fully interruptible
; with Synthesized PGO: fgCalledCount is 111728
; 0 inlinees with PGO data; 1 single block inlinees; 0 inlinees without PGO data

G_M000_IG01:
       push     rdi
       push     rsi
       push     rbp
       push     rbx
       sub      rsp, 40
Progress: 20 000rsi, rdx

       mov      ebx, r8d

G_M000_IG02:
       xor      edi, edi
       xor      ebp, ebp
       test     ebx, ebx
       jle      SHORT G_M000_IG06

G_M000_IG03:
       test     rsi, rsi
       je       SHORT G_M000_IG08

G_M000_IG04:
       mov      rcx, 0xD1FFAB1E
       cmp      qword ptr [rsi], rcx
       jne      SHORT G_M000_IG08
       align    [0 bytes for IG05]

G_M000_IG05:
       vxorps   xmm0, xmm0, xmm0
       vcvtsi2sd xmm0, xmm0, ebp
       vsqrtsd  xmm0, xmm0, xmm0
       vcmpordsd xmm1, xmm0, xmm0
       vandpd   xmm1, xmm1, xmm0
       mov      ecx, 0xD1FFAB1E
       vcvttsd2si edx, xmm1
       vucomisd xmm0, qword ptr [reloc @RWD00]
       cmovb    ecx, edx
       inc      ecx
       add      edi, ecx
       inc      ebp
       cmp      ebp, ebx
       jl       SHORT G_M000_IG05

G_M000_IG06:
       mov      eax, edi

G_M000_IG07:
       add      rsp, 40
       pop      rbx
       pop      rbp
       pop      rsi
       pop      rdi
       ret

G_M000_IG08:
       mov      rcx, 0xD1FFAB1E
       cmp      qword ptr [rsi], rcx
       jne      SHORT G_M000_IG10
       vxorps   xmm0, xmm0, xmm0
       vcvtsi2sd xmm0, xmm0, ebp
       vsqrtsd  xmm0, xmm0, xmm0
       vcmpordsd xmm1, xmm0, xmm0
       vandpd   xmm1, xmm1, xmm0
       mov      ecx, 0xD1FFAB1E
       vcvttsd2si edx, xmm1
       vucomisd xmm0, qword ptr [reloc @RWD00]
       cmovb    ecx, edx
       inc      ecx

G_M000_IG09:
       add      edi, ecx
       inc      ebp
       cmp      ebp, ebx
       jl       SHORT G_M000_IG08
       jmp      SHORT G_M000_IG06

G_M000_IG10:
       mov      rcx, rsi
       mov      edx, ebp
       mov      r11, 0xD1FFAB1E
       call     [r11]IHandler:Handle(int):int:this
       mov      ecx, eax
       jmp      SHORT G_M000_IG09

RWD00   dq      41E0000000000000h

; Total bytes of code 194

  15: JIT compiled Dispatcher:Process(IHandler,int) [Tier1 with Synthesized PGO, IL size=26, code size=194]


При варм-апе в 100 000 итераций лог и код такой же:

   1: JIT compiled Program:<Main>$(System.String[]) [Instrumented Tier0, IL size=186, code size=557]
   2: JIT compiled Dispatcher:.ctor() [Tier0, IL size=7, code size=31]

Warming up...

   3: JIT compiled FastHandler:.ctor() [Tier0, IL size=7, code size=31]
   4: JIT compiled Dispatcher:Process(IHandler,int) [Instrumented Tier0, IL size=26, code size=213]
   5: JIT compiled FastHandler:Handle(int) [Tier0, IL size=4, code size=18]
   6: JIT compiled Program:<Main>$(System.String[]) [Tier1-OSR @0x3a with Synthesized PGO, IL size=186, code size=3197]

Warmup done. Starting the test...

   7: JIT compiled Utils:Test(Dispatcher,IHandler) [Instrumented Tier0, IL size=65, code size=291]
   8: JIT compiled Utils:PrintProgress(int) [Tier0, IL size=67, code size=161]
   9: JIT compiled System.Threading.Thread:GetThreadStaticsBase() [Tier0, IL size=18, code size=34]
Progress: 0
  10: JIT compiled SlowHandler:Handle(int) [Tier0, IL size=11, code size=84]
  11: JIT compiled Utils:Test(Dispatcher,IHandler) [Tier1-OSR @0x26 with Synthesized PGO, IL size=65, code size=464]
Progress: 10 000
  12: JIT compiled Dispatcher:Process(IHandler,int) [Instrumented Tier0, IL size=26, code size=213]
  13: JIT compiled Utils:PrintProgress(int) [Instrumented Tier0, IL size=67, code size=191]
  14: JIT compiled SlowHandler:Handle(int) [Instrumented Tier0, IL size=11, code size=84]
  15: JIT compiled Dispatcher:Process(IHandler,int) [Tier1 with Synthesized PGO, IL size=26, code size=194]
Progress: 20 000
  16: JIT compiled Utils:PrintProgress(int) [Tier1 with Synthesized PGO, IL size=67, code size=240]
  17: JIT compiled SlowHandler:Handle(int) [Tier1, IL size=11, code size=44]

...да и под 9.0.17 и 8.0.28 разницы во времени исполнения нет.
RyuJIT, вероятно, самый свежий работает, вне зависимости от таргета?

АСМ всё ещё не читал.

Под 10.0.9 оба варианта прогрева дают одинаковое время выполнения нагрузки.
"На моей машине [не] работает", короче ;)
Windows 11 (10.0.26200.8655/25H2/2025Update/HudsonValley2)
12th Gen Intel Core i9-12900HK 2.50GHz, 1 CPU, 20 logical and 14 physical cores
.NET SDK 10.0.301
[Host] : .NET 10.0.9 (10.0.9, 10.0.926.27113), X64 RyuJIT x86-64-v3

АСМ пока вдумчиво не читал, гляну позже.

Ну ладно, на вкус и цвет, как говорится.

Если будет не лень, пожалуйста, вкратце - какие конкретно механизмы и почему? И чем они предпочтительнее того же ЕФ.

Мне для общего развития и понимания.

И что? Усложняет миграцию с Джавы?

Я просто пытаюсь понять, в чём собственно этот "минус".

Из минусов шарпа - это отсутствие популярного ORM фреймворка, реализующего спецификацию JPA .

Чисто из любопытства: а зачем оно в НЕТе?

Что, не приведёт имплицитно инт в флоту?
Я не специалист, на с# приводит и результат ожидаемо лучший.

...побитовые операции с флотом тоже вполне себе существуют.

Умножается оно здесь на 1 (удовлетворяет условию) или на 0 (не удовлетворяет), таким образом получаем сумму весов котов, удовлетворяющих условию.

А так, ещё можно вместо умножения попробовать делать - (отрицание) и & (побитовое И).

Можно, и даже побитовое NOT. Всё это можно сделать без ветвлений, парой-тройкой арифметических/битовых операций. Иногда ОЧЕНЬ ускоряет, иногда - нет.

Тут ещё стоит не забывать про буферы декодера и блока обработки циклов (как его там) - там сдвиг команд на пару байтов туда-сюда может СИЛЬНО влиять на производительность.

vpcompressd

Да, пропустил, спасибо за науку.

Жаль, что нарочно на такое .NET JIT (пока) не спровоцировать, мне бы пригодилось кое-где.

Именно так. Загрузили вектор в регистр, одной инструкцией сравниваем значения с заранее подговленным вектором с размноженной константой, другой копируем подходящие в выходной массив (подряд).

Можно по-подробнее, псевдокод?

1
23 ...

Information

Rating
Does not participate
Registered
Activity