Repository navigation
Take holonsoft.FluentDateTime out of the conversion paths - #13
Merged
Merged
Conversation
Nothing in the suite touched them. BenchmarkPoco has no DateTime field, so the DateTime path, which is the one that reaches into holonsoft.FluentDateTime, was never measured, and neither were the Guid and decimal handlers. Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
The same non optimized package problem applied to holonsoft.FluentDateTime,
which sat in both conversion directions and which no benchmark covered:
BenchmarkPoco has no DateTime field, so nothing in the suite ever ran that
code. The benchmark rows came first, so this could be measured rather than
assumed.
The two uses were very different. Reading used DateTimeExtensions.UnixEpoch,
a static readonly field, and reading a static field costs nothing even in an
unoptimized assembly. Writing used dtf.ToUnixTimeSeconds(), a real extension
method, so a non inlinable call for every DateTime field of every message.
Both now use their in box equivalents, DateTime.UnixEpoch and
new DateTimeOffset(dtf).ToUnixTimeSeconds(), which is literally what the
extension method did. DateTime.UnixEpoch is the same instant with the same
DateTimeKind.Utc, so no decoded value changes. The package reference is gone
with them, so FluentDateTime no longer reaches a consumer of this library.
A/B, back to back, with the ten rows the change cannot reach as the control:
Write DateTime/Guid/decimal LE 66.70 ns -> 60.04 ns -10.0%
Write DateTime/Guid/decimal BE 65.51 ns -> 61.27 ns -6.5%
Read DateTime/Guid/decimal LE 58.18 ns -> 59.42 ns +2.1%
Read DateTime/Guid/decimal BE 58.66 ns -> 58.08 ns -1.0%
The control rows all stay within 3%, drifting slightly upward in the second
run. The write rows moved against that drift, the read rows did not move,
which is what the code predicts.
TestDateTimeConversion asserts the DateTimeKind directly. The golden vectors
structurally cannot: the Kind is not in the bytes, so a decoder returning the
right instant with the wrong Kind writes the very same frame back out while
every consumer handing the value to a DateTimeOffset reinterprets it as local
time.
Co-Authored-By: Claude Opus 5 (1M context) <noreply@anthropic.com>
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
What
DateTime,Guidanddecimal, which nothing in the suite measured (BenchmarkPocohas no DateTime field).DateTimeExtensions.UnixEpoch→DateTime.UnixEpoch,dtf.ToUnixTimeSeconds()→new DateTimeOffset(dtf).ToUnixTimeSeconds(), which is literally what the extension method did.holonsoft.FluentDateTimepackage reference is gone, so it no longer reaches a consumer of this library.TestDateTimeConversion, six tests pinningDateTimeKindand the round trip.Why
Same finding as #12: the package is published as a non optimized build, so the JIT can neither optimize nor inline anything inside it. The benchmark came first so the change could be measured rather than assumed.
The two uses turned out to be very different:
UnixEpoch, astatic readonly DateTime. Reading a static field costs nothing even in an unoptimized assembly, there is no call to inline.ToUnixTimeSeconds(), a real extension method, so a non inlinable call for every DateTime field of every message.Numbers
A/B of the same tree with and without the change, back to back:
The other ten rows are the control: the change cannot reach them and they all stay within 3%, drifting slightly upward in the second run. The two write rows moved outside their error bars and against that drift, the two read rows did not move. That is what the code predicts. About 3 ns per DateTime field written.
Modest on its own. The point is as much that a Debug built assembly leaves the dependency graph.
Safety
DateTime.UnixEpochis1970-01-01T00:00:00withDateTimeKind.Utc, the same instant and the same kind FluentDateTime declared, so no decoded value changes. 102/102 tests green, golden vectors unmoved.The golden vectors structurally cannot cover this. The
Kindof a decoded DateTime is not in the bytes: a decoder returning the right instant with the wrongKindwrites the very same frame back out, so a byte exact vector stays green while every consumer that hands the value to aDateTimeOffsetsilently reinterprets it as local time.ProtocolDateTimeFielddeclares the Kind, which makes it part of the protocol contract, so the new tests assert it directly, plus round trips in both widths, both byte orders, and a timestamp before the epoch.🤖 Generated with Claude Code