Optimises blittable types for multi-dim arrays - #390
Merged
alex-clickhouse merged 3 commits intoAug 4, 2026
Merged
Conversation
Contributor
There was a problem hiding this comment.
Pull request overview
This PR introduces an internal performance optimization to the binary write path for rectangular CLR multidimensional arrays (T[,], T[,,], …) when the leaf element type is a fixed-width primitive, eliminating per-element boxing and virtual dispatch by blitting contiguous innermost rows directly to the output stream.
Changes:
- Added a blittable fast-path in
MultiDimArrayHelper.WriteMultidimensionalfor fixed-width primitive leaves on little-endian hosts. - Added unit + integration tests to assert byte-for-byte equivalence vs the existing jagged/boxing path and to guard allocations.
- Added a BenchmarkDotNet benchmark to measure jagged boxing vs multidim blitting, and documented the improvement in CHANGELOG/RELEASENOTES.
Reviewed changes
Copilot reviewed 6 out of 6 changed files in this pull request and generated 2 comments.
Show a summary per file
| File | Description |
|---|---|
| ClickHouse.Driver/Types/MultiDimArrayHelper.cs | Adds the blit-based fast path for blittable primitive leaves during binary serialization. |
| ClickHouse.Driver.Tests/Types/MultiDimArrayHelperTests.cs | Adds equivalence tests across supported leaf types and an allocation regression test. |
| ClickHouse.Driver.Tests/SQL/NestedArrayParameterTests.cs | Adds end-to-end binary insert round-trip coverage for the blit path across primitive leaf types. |
| ClickHouse.Driver.Benchmark/MultidimArrayInsert.cs | Adds a benchmark comparing jagged (boxing) vs multidim (blit) inserts. |
| CHANGELOG.md | Documents the optimization for release notes. |
| RELEASENOTES.md | Documents the optimization for NuGet package release notes. |
7 tasks
Codecov Report✅ All modified and coverable lines are covered by tests. 📢 Thoughts on this report? Let us know! |
Collaborator
|
Looks great, thank you! |
alex-clickhouse
approved these changes
Aug 4, 2026
alex-clickhouse
added a commit
that referenced
this pull request
Aug 5, 2026
main added 22 new Unreleased entries since this branch was cut. Each is now its own changelog.d/ fragment, extracted verbatim by line number rather than retyped, so the assembled Unreleased section reproduces main's exactly (as a set of lines; sorting by PR number reorders entries within their sections). New fragments, one per (PR, category): #390 improvements multidim blittable inserts #472 improvements per-scalar Span<byte> reads #484 fixes byte[]/TimeOnly HTTP parameters #485 fixes JSON strings under ReadStringsAsByteArrays #490 breaking raw results return compressed bytes #490 features AcceptEncoding response compression #490 improvements lz4 by default, HttpClient, errors, deflate #492 fixes HTTP response disposal #493 fixes Enum type declarations #494 fixes raw-stream double dispose #497 fixes GetSchema("Columns") restrictions #498 fixes JSON paths starting with setting names #503 fixes quoted JSON typed paths #504 fixes quoted Tuple/Nested element names #509 fixes {name:Type} scanner vs server lexer #511 fixes {name:Type} hints after a non-hint brace #513 fixes @name placeholders, heredocs, $ in names #390's entry was appended to the *released* v1.3.0 section on main (v1.3.0 shipped 2026-06-29), so it would have documented an unreleased change under a shipped version and never appeared in 1.4.0's notes. It moves to Unreleased as a fragment; the rest of v1.3.0 is byte-identical. RELEASENOTES.md regenerated with --sync-notes. `--check` passes, the solution builds, and the packed .nupkg's releaseNotes open on v1.3.0 with no Unreleased stub and no #390 bullet.
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.
Summary
Removes the per-element boxing on multidimensional array inserts (#367).
Rectangular CLR arrays (
int[,],double[,,], …) of fixed-width primitive leaves —Int8/16/32/64,UInt8/16/32/64,Float32/64,Boolare now serialized by blitting each contiguous inner row in a single write instead of boxing every element throughArray.GetValue(int[]).Output bytes are unchanged; it's an internal optimization with no public API change. Jagged shapes, nullable/LowCardinality/wrapped leaves, big-endian hosts, and
Int128/UInt128fall through to the existing path.Not included —
Int128/UInt128: these are BigInteger-backed in the driver, and BigInteger is unblittable (it's a sign int + a heapuint[], not an inline 16-byte value). Bringing them into the fast path means switching their FrameworkType toSystem.Int128/System.UInt128, which is a breaking change to the read return type and needs net6 handling (conditional compilation or dropping net6 support).Checklist
Delete items not relevant to your PR: