Add fetch-throughput A/B ODBC benchmark harness (mssql-odbc vs msodbcsql18) - #190
Draft
Saurabh Singh (saurabh500) wants to merge 2 commits into
Draft
Add fetch-throughput A/B ODBC benchmark harness (mssql-odbc vs msodbcsql18)#190Saurabh Singh (saurabh500) wants to merge 2 commits into
Saurabh Singh (saurabh500) wants to merge 2 commits into
Conversation
…sql18) Add a standalone C++ benchmark that times the SQLFetch + SQLGetData drain over a large result set and runs an A/B between the native ODBC Driver 18 for SQL Server and the Rust mssql-odbc dev driver in one invocation. It reuses the e2e harness config/diagnostics plumbing but is a plain executable (not a CTest test). Each leg loads its driver DLL directly (its own tiny driver manager) so the unregistered dev driver runs with no admin/registry and both legs share an identical, DM-free code path. Reports raw per-rep ms/rows-per-sec, per-driver medians, and the rust/native ratio; a checksum over decoded values guards against elision and confirms both legs did equal work. run_bench.ps1 builds the Rust driver + benchmark and runs both legs. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a3c262b9-1962-4d93-b08e-e3bc84aa4ac0
A debug mssql-odbc build is 10-30x slower purely from codegen and swamps the real driver-path cost; document that the Rust leg must be a --release build so the A/B baseline reflects the driver, not debug overhead. Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com> Copilot-Session: a3c262b9-1962-4d93-b08e-e3bc84aa4ac0
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
Adds a standalone C/C++ ODBC benchmark (
mssql-odbc/tests/e2e/bench/fetch_bench.cpp+run_bench.ps1) that times the fetch / row-throughput path — theSQLFetch+SQLGetDatadrain over a large result set — and runs an A/B between the native ODBC Driver 18 for SQL Server and the Rustmssql-odbcdev driver in a single invocation.This establishes the BEFORE baseline that the sans-I/O core work is meant to beat — it is not a final verdict on the Rust driver. It is independent of the in-flight sans-I/O refactor — this branch is off
mainand is not stacked on #186.Design
ODBCTestConfigfor server/uid/pwd/database, diagnostics helpers) but is a plain executable — a normalctestrun does not invoke it. New non-CTestfetch_benchCMake target.SELECT TOP (N)of anint/bigint/varchar/nvarchar/floatrow set materialised server-side (asys.all_objectsself cross-join), so the client spends its time decoding rows. Every column is retrieved viaSQLGetDataas character data — the one column path both drivers share.LoadLibrarys the driver DLL directly and calls its exportedSQLxxxWentry points as a tiny DM (--driver LABEL=dll:PATH). This runs the unregistered Rust dev driver with zero admin rights and keeps both legs on an identical, DM-free code path (a fairer compare). A bare-name leg routes throughodbc32.dll(the real DM) instead.run_bench.ps1takes either DLL path as a param (-RustDll/-NativeDll, or-Release) so the Rust leg can be re-pointed at future sync-core builds without editing the script.QueryPerformanceCounteraround each execute+drain rep; ≥9 reps with the first (warmup) discarded; every retained rep printed (ms + rows/sec) plus the median; per-driver deltas <15% treated as noise. A checksum over all decoded values is summed so nothing is optimised away — it must stay685409346on both legs on every (re-)measure, so a decode regression can never silently change what's being compared.Baseline results (Windows, live SQL Server on
127.0.0.1,1433, msodbcsql18 installed)Build + connect succeeded for both drivers. Release-vs-release — both optimised builds, the only honest comparison. 200k rows, 9 reps, checksum
685409346identical on both legs.C:\Windows\System32\msodbcsql18.dll)mssql-odbc, main @ async/block_on, RELEASE build (target\release\msodbcsql18.dll) — the BEFORE baseline the sans-I/O core is meant to beatRaw per-rep (rep 1 = discarded warmup):
A/B ratio (rust median / native median) = 1.94x. Across two independent release runs the Rust median was 670.89 ms / 672.20 ms (≈0.2% apart, reps tight 659–684 ms); native floats more run-to-run (321–346 ms median), so the ratio lands in ~1.9–2.1x (~2x).
Framing: unoptimized
main(async/block_on) is ~2x slower on this fetch/decode path — the starting line, not the score. The sans-I/O sync core plus #186 batching/recycling are the intervention meant to close it.Interpretation — the gap is addressable decode-CPU headroom, not async tax: the async reactor round-trip (~10 µs) is per network refill, not per row, so it can't account for a per-row delta. In release the Rust leg is ~3.4 µs/row vs native ~1.7 µs/row; that remaining ~1.7 µs/row scales with row count, which points at per-row decode CPU — exactly what the sync core + batching/recycling target.
Notes
datetime2is intentionally excluded from the column set: the Rust Phase-1SQLGetDatacannot yet convert it to text, and including it would make the two A/B legs diverge.localhost("No such host is known");run_bench.ps1auto-rewriteslocalhost→127.0.0.1.Co-authored-by: Copilot App 223556219+Copilot@users.noreply.github.com