Skip to content

Make the two allocation tests deterministic: read their rows from the existing TypedReader - #136

Merged
electricessence merged 2 commits into
masterfrom
test/non-boxing-allocation-fixture
Nov 14, 2026
Merged

electricessence merged 2 commits into
masterfrom
test/non-boxing-allocation-fixture

Conversation

@electricessence

Copy link
Copy Markdown
Collaborator

Make the two allocation tests deterministic by reading their rows from the existing TypedReader instead of a DataTableReader, which boxed an int per row and made them fail at random.

The fixture read ints through DataTableReader.GetInt32, which allocates a 24 B box per row. Tiered JIT removes that box part way through a run, so when it lands between the two back-to-back measurements of MappedQuerySelectTests.TheAsyncProjection_AllocatesNothingPerRow or SingleValueAllocationTests, one side is 24 B per row cheaper and the comparison fails; the library allocates nothing per row.

What changed

  • Ints (in MappedQuerySelectTests) and Rows (in SingleValueAllocationTests) build a TypedReader (TypedReader.Create with ColumnDef columns) instead of a DataTable and DataTableReader. The cells are boxed once, when the rows are built, outside the measured region; the typed getters only unbox them, so a row costs nothing.
  • Thresholds, slack, warm-up and the assertions are unchanged. Nothing under Source/ is touched.
  • The branch has two commits: the first added a purpose-built ArrayReader; the second removes it in favor of the TypedReader the test project already has (the net result is only the two helper changes).

Verification

  • Full Core test project, net11.0 Release, 10 runs: 10 of 10 passed, 10200 tests each.
  • TheAsyncProjection_AllocatesNothingPerRow, net11.0 Release, filtered, 10 runs: 10 of 10 passed.
  • SingleValueAllocationTests, net11.0 Debug, class alone, 10 runs: 10 of 10 passed (12 tests each).
  • Full project once each: net11.0 Debug, net10.0 Release, net10.0 Debug: 10200 passed each.
  • Measured per row (1,000-row ToListAsync, plain vs projected): 8.0 B per row for both (8712 B plain and 9032 B projected at 1,000 rows; 1472 B and 1792 B at 100), the same filtered, with tiered compilation off, and inside the full run. The projection adds a flat 320 B per call and the 24 B per row gap is gone.
  • The guard still guards: with a temporary per-row allocation in the projection path, TheAsyncProjection_AllocatesNothingPerRow fails ("costs 2720 B per call over 100 rows and 24320 B over 1000"); with one in the single-value mapper, 4 of the 12 SingleValueAllocationTests fail (for example "ToArray over 100 rows: 2864 B per call through the terminal, 488 B by hand"). Both changes were reverted and never committed.
  • Release build and style/analyzer format check clean.

Not done

  • RowMapConverterTests.AConverter_AllocatesNothingBeyondItself_PerRow still reads through a DataTableReader and compares bytes of two calls (a converter against none), so it has the same exposure in principle; it did not fail in any run here. AllocationQuickWinTests also uses DataTableReader but only as a positioned record.
  • No change to CI; enabling the tests there is a separate step.

🤖 Generated with Claude Code

electricessence and others added 2 commits November 14, 2026 14:47
… boxes an int per row

The two allocation tests compared bytes measured back to back through a
DataTableReader, whose GetInt32 boxes each value (24 B per row). Tiered JIT
removes that box part way through a measurement, so one side of a comparison
could be 24 B per row cheaper than the other and the test failed at random.

An ArrayReader serves typed getters straight from int[]/long[]/string[], so the
measured bytes are the library's own at every tier. The thresholds, the
warm-up and the tests' claims are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
…ArrayReader

TypedReader already serves typed getters over cells boxed once when the rows
are built, so a row read allocates nothing; ArrayReader duplicated it. Ints
and Rows now build a TypedReader. Thresholds and assertions are unchanged.

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
@electricessence
electricessence merged commit f4ecdfe into master Nov 14, 2026
1 check passed
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