Is there an existing issue for this?
Describe the bug
Nullable scalar result decoding is quadratic in PyMilvus 2.6.16, 2.6.17, and current master.
decode_range() calls decode_cell() for every logical row. For every valid nullable row, decode_cell() calculates physical_index() by scanning valid_data[:logical_index]. Ordinary scalar payloads use the logical index and do not use that physical index, so a result with N nullable scalar values performs O(N²) validity-mask work on one Python core.
The regression affects nullable BOOL, integer, floating-point, string/VARCHAR/TEXT, TIMESTAMPTZ, and GEOMETRY output fields. Nullable JSON and ARRAY decoding also performs the unnecessary physical-index calculation in the per-cell path.
The behavior entered in commit 7b8bb8af and was first released in PyMilvus 2.6.16. PyMilvus 2.6.15 used a bulk scalar slice followed by one validity pass.
Expected Behavior
Nullable scalar result decoding should be O(N): bulk-slice the scalar protobuf column, then apply valid_data once. Physical-index calculation should remain only in vector branches that use compact physical storage.
Steps/Code To Reproduce behavior
Construct a mocked Search response with:
- `nq=1`
- `top_k=100000`
- one output field: `VARCHAR(max_length=256, nullable=True)`
- 100,000 strings of length 256
- `valid_data=[True] * 100000`
Then construct the public search result through `MilvusClient.search()` and measure client-side result decoding.
Observed on master at `9f7fca59`:
- current decoder: 183.352 s
- linear bulk scalar decoder: 228.632-237.972 ms
- improvement: 770x-802x, about 99.88% lower latency
The repository benchmark added by the proposed fix is:
`tests/benchmark/test_search_bench.py::TestSearchBench::test_search_nullable_varchar_256_topk_100000`
Environment details
- Hardware/Software conditions: Linux x86_64
- Method of installation: source checkout
- PyMilvus version: master at `9f7fca59`; also reproduced in 2.6.16 and 2.6.17
- Python: 3.14.5
- protobuf: 5.29.6
- Milvus server: not required; benchmark isolates mocked client-side result construction
Anything else?
The proposed fix preserves physical-index handling for nullable dense and sparse vectors, adds a deterministic regression test for linear validity traversal, and adds the exact 100,000-row benchmark.
Is there an existing issue for this?
Describe the bug
Nullable scalar result decoding is quadratic in PyMilvus 2.6.16, 2.6.17, and current master.
decode_range()callsdecode_cell()for every logical row. For every valid nullable row,decode_cell()calculatesphysical_index()by scanningvalid_data[:logical_index]. Ordinary scalar payloads use the logical index and do not use that physical index, so a result with N nullable scalar values performs O(N²) validity-mask work on one Python core.The regression affects nullable BOOL, integer, floating-point, string/VARCHAR/TEXT, TIMESTAMPTZ, and GEOMETRY output fields. Nullable JSON and ARRAY decoding also performs the unnecessary physical-index calculation in the per-cell path.
The behavior entered in commit
7b8bb8afand was first released in PyMilvus 2.6.16. PyMilvus 2.6.15 used a bulk scalar slice followed by one validity pass.Expected Behavior
Nullable scalar result decoding should be O(N): bulk-slice the scalar protobuf column, then apply
valid_dataonce. Physical-index calculation should remain only in vector branches that use compact physical storage.Steps/Code To Reproduce behavior
Environment details
Anything else?
The proposed fix preserves physical-index handling for nullable dense and sparse vectors, adds a deterministic regression test for linear validity traversal, and adds the exact 100,000-row benchmark.