You signed in with another tab or window. Reload to refresh your session.You signed out in another tab or window. Reload to refresh your session.You switched accounts on another tab or window. Reload to refresh your session.Dismiss alert
Browse filesBrowse the repository at this point in the historyBrowse files
authored
PERF: Optimize checked temporal fetch construction (#795)
### Work Item / Issue Reference
> GitHub Issue: #554
>
> ADO Task:
[AB#48255](https://sqlclientdrivers.visualstudio.com/c6d89619-62de-46a0-8b46-70b92a84d85e/_workitems/edit/48255)
-------------------------------------------------------------------
### Summary
**Reduce Python object-construction overhead when fetching DATE, TIME,
and TIMESTAMP values.** Previously, native temporal fields were
converted into Python arguments and passed through a generic call to an
already-cached constructor. Repeated imports were not the bottleneck.
The new helper uses checked CPython construction APIs when the cached
constructor is the exact standard type; substituted constructors retain
the original call. Six row-wise/batch conversion sites change. Field
validation and final object allocation remain. NULLs, precision,
timezone/fold, ownership, and exception behavior are preserved;
DATETIMEOFFSET, UUID, Decimal, and text are untouched by this PR.
```mermaid
flowchart LR
A["Native temporal fields after NULL checks"] --> B["Before: Python arguments and generic cached-constructor call"]
A --> C{"After: exact standard type?"}
C -->|"Yes"| D["Direct checked CPython construction"]
C -->|"No: original fallback"| B
B --> E["Validated Python object in result row"]
D --> E
```
#### Fresh measurements
Temporal cases contain NULLs every seventh row. Pure cases have eight
temporal columns; the row-wise case adds one harmless MAX column. Mixed
has DATE/TIME/DATETIME2/DATETIMEOFFSET; narrow is an unchanged
int/text/float control.
| Workload / path | API / requested batch | Rows × columns | Before →
after fetch time | Reduction |
| --- | --- | ---: | ---: | ---: |
| DATE / bounded | `fetchmany(1000)` | 4,000 × 8 | 6.060 → 5.050 ms |
**16.67%** |
| TIME(7) / bounded | `fetchmany(1000)` | 4,000 × 8 | 6.815 → 5.479 ms |
**19.60%** |
| DATETIME2(7) / bounded | `fetchmany(1000)` | 4,000 × 8 | 8.077 → 5.359
ms | **33.65%** |
| DATETIME2(7) / MAX-forced row-wise | `fetchall()` / all remaining |
4,000 × 9 | 13.906 → 9.562 ms | **31.24%** |
| Mixed temporal | Repeated `fetchone()` / 1 | 4,000 × 4 | 234.946 →
231.518 ms | 1.46%; inconclusive |
| Unchanged narrow | `fetchmany(1000)` | 10,000 × 3 | 6.493 → 6.634 ms |
**-2.16%** |
**Method/build:** September 21 Docker Linux x64; Python 3.13.15,
pybind11 3.0.1, GCC 12.2 Release `-O3 -DNDEBUG`, profiling OFF, SQL
Server 16.0.4225.2, ODBC 18.6.2.1. Main `c963ee1e` versus PR `5aaa6aae`:
10 counterbalanced pairs × 5 samples × 14 cases, totaling 1,400
validated drains. Reductions are ratios of medians, excluding
execute/validation; no outlier removal or retries. Fallback checks
observed three callbacks per temporal type with 3/4/7 positional
arguments—not optimized-path or allocation counts.
**Limits:** identical-build A/A calibration was noisy (speed-ratio
interval 0.882×–1.345×). Mixed `fetchone` and bounded DATE `fetchall`
remain inconclusive; unchanged narrow many/all had negative point
estimates with intervals spanning zero. These are scoped bulk-temporal
gains, not universal speedups or a no-regression guarantee.
Both builds passed six fresh-process compatibility modes covering
boundaries, NULLs, types, substitutions, exceptions, recovery, and both
fetch paths. No full-suite or all-OS success is claimed. Complete
samples, intervals, provenance, and historical limitations remain in
retained local evidence.
---------
Co-authored-by: Copilot App <223556219+Copilot@users.noreply.github.com>
Co-authored-by: Gaurav Sharma <sharmag@microsoft.com>
0 commit comments