Skip to content

feat: direct varData properties (#162) + foreach vs callback bench (#156)#163

Merged
pedrosakuma merged 3 commits into
mainfrom
bench/foreach-vs-callback-156
Apr 29, 2026
Merged

feat: direct varData properties (#162) + foreach vs callback bench (#156)#163
pedrosakuma merged 3 commits into
mainfrom
bench/foreach-vs-callback-156

Conversation

@pedrosakuma
Copy link
Copy Markdown
Owner

Summary

Two related changes around the v1.5.0 enumerator work, shipped together as v1.6.0.

feat: direct varData properties on {Msg}DataReader (#162)

Expose top-level <data> fields as zero-allocation direct properties alongside ReadGroups. Each property recomputes its offset by chaining the existing Skip{Group} helpers (O(1) per simple group) plus per-data SkipData{Prev} helpers — fully stateless, safe regardless of access order, no closure capture.

if (NewOrderData.TryParse(buffer, out var reader))
{
    foreach (ref readonly var leg in reader.Legs) { /* ... */ }
    var coid = reader.ClientOrderId.VarData;   // ReadOnlySpan<byte>, zero-alloc
}

Gating (same as #156 enumerators): all top-level groups must be simple (no nested groups, no group-level varData). Also valid when the message has only varData. When a varData name collides with a reserved member of the reader struct (e.g. <data name="data"> clashing with the Data block accessor) the direct property is silently skipped; ReadGroups remains usable.

For repeated reads, cache the returned value locally (documented in the XML doc on the property).

bench: foreach vs callback group decode (#156)

Adds a benchmark validating the perf premise of the v1.5.0 foreach enumerator API.

Tests

  • 336 tests passing (187 unit + 149 integration, +2 new for direct varData properties).
  • New tests cover: direct property access matches callback bytes, stateless across multiple accesses, works before/after group iteration, works with empty group + varData.

Release

  • Bumps version to 1.6.0.
  • CHANGELOG entry under [1.6.0] - 2026-04-29.
  • README updated with new example.

pedrosakuma and others added 3 commits April 25, 2026 19:14
Adds GroupForeachVsCallbackBenchmarks comparing the v1.5.0 foreach-style
group enumerator against the original ReadGroups callback API on
MarketDataData (two simple top-level groups), parameterized over
GroupSize ∈ {10, 50, 100}.

Results on AMD EPYC 7763 / .NET 9 (GroupSize=100):

  Callback         999 ns   152 B   1.00x
  Foreach          184 ns     0 B   0.18x
  Foreach + break    3 ns     0 B   0.003x

Foreach is ~5x faster on full iteration and eliminates the 152 B
per-call closure allocation. Early break is essentially free because
each group property does an O(1) skip rather than running every entry.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Expose top-level <data> fields as zero-allocation direct properties on
the {Msg}DataReader ref struct, alongside the existing ReadGroups
callback API. Each property recomputes its offset by chaining the
existing Skip{Group} helpers (O(1) per simple group) plus per-data
SkipData{Prev} helpers — fully stateless, safe regardless of access
order, no closure capture.

Gated on the same condition as the foreach group enumerators (#156):
all top-level groups must be simple (no nested groups, no group-level
varData). Also valid when the message has no top-level groups.

Property emission is suppressed when a varData name collides with a
reserved member of the reader struct (e.g. <data name="data"> would
clash with the Data block accessor); ReadGroups remains usable for
those fields. SkipData helpers are still emitted for skipped names so
that subsequent varData properties resolve correctly.

Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Copilot <223556219+Copilot@users.noreply.github.com>
@pedrosakuma pedrosakuma merged commit 5657d43 into main Apr 29, 2026
1 check passed
@pedrosakuma pedrosakuma deleted the bench/foreach-vs-callback-156 branch April 29, 2026 13:50
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