Skip to content

[fix](be) Preserve floating point values in CASE branch selection - #67896

Open
HappenLee wants to merge 2 commits into
apache:masterfrom
HappenLee:fix-case-float-nonfinite
Open

[fix](be) Preserve floating point values in CASE branch selection#67896
HappenLee wants to merge 2 commits into
apache:masterfrom
HappenLee:fix-case-float-nonfinite

Conversation

@HappenLee

@HappenLee HappenLee commented Sep 11, 2026

Copy link
Copy Markdown
Contributor

What problem does this PR solve?

Issue Number: N/A

Related PR: N/A

Problem Summary: A non-nullable FLOAT/DOUBLE CASE can return NaN for a finite selected branch when an unselected branch contains Infinity or NaN. For example, with ordinary CASE evaluation, selecting 1, an overflowing multiplication, and 2 over three rows returns NaN, Infinity, NaN instead of 1, Infinity, 2. The same result assembly also loses the sign of selected negative zero.

The result assembly multiplies each branch value by a zero/one mask and adds it to the result. IEEE-754 arithmetic makes 0 * Infinity and 0 * NaN equal NaN. Use conditional stores to copy the selected floating point value without arithmetic. DATE, DATETIME, DATEV2, DATETIMEV2, TIMESTAMP_NS and TIMESTAMPTZ share this conditional-store loop, replacing their previous ternary assignments. This form allows AVX2 masked loads/stores, while a ternary assignment can become a conditional pointer load that inhibits vectorization.

Retain the existing default-value initialization and the arithmetic accumulator for the other numeric types. Removing the redundant initialization for the direct-store types is deferred.

Add bitwise unit tests, SQL regression tests and a benchmark that directly calls the production result assembly function. The typed unit tests cover all eight types with both uint8_t and uint16_t branch indices.

Release note

Fix incorrect FLOAT/DOUBLE CASE results caused by unselected non-finite branch values, and preserve selected negative zero.

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (details below)
    • No need to test or manual test.

    Validation of the date/time follow-up:

    • All 48 VCaseSelectionTest* tests pass via ./run-be-ut.sh -j 48 --run --filter='VCaseSelectionTest*' with ASAN. Coverage includes all eight types, both index widths, constants, boundary values, vector tails and 255/257 branches.
    • clang-format 16, header hygiene, git diff --check and clang-tidy pass. For clang-tidy, the header was explicitly mapped to the generated selection-test compiler flags to avoid failed automatic header inference.
    • ./build.sh --be -j 48 with ASAN compiled and linked the BE successfully. Full build/packaging did not pass: the later kuromoji_build_dict process reported an ASAN double-free during OpenBLAS/OpenMP initialization (dlsym / _dlerror_run), before entering main. The separate standard ASAN unit-test build and execution passed.
    • Default-value initialization and the remaining arithmetic accumulator are unchanged.

    Earlier validation of the floating point fix (before the date/time follow-up):

    • All 12 original floating point unit tests fail on the original implementation and pass with ASAN after the fix. These cases are retained in the expanded typed suite. Coverage includes non-finite values, signed zero and subnormal values.
    • test_case_float_nonfinite and test_short_circuit_evaluation pass. The original implementation fails the new SQL regression. Golden output was generated and independently verified through short-circuit evaluation using the regression runner. These SQL suites were not rerun for the date/time follow-up.
    • ASAN BE build, clang-format 16, header hygiene and clang-tidy checks pass for the earlier revision.
    • RELEASE benchmarks built with Clang 21.1.8 and -O3 -msse4.2 -mavx2 on Xeon Platinum 8457C cover 42 scenarios. With identical input, a fixed CPU, five repetitions per run and before/after/after/before ordering, median CPU time decreases by approximately 5%–39% (18.5% geometric mean reduction). This measures floating point result assembly on this AVX2 machine, including result allocation; these figures do not measure the date/time follow-up.
    • Disassembly of all four floating point/index-width specializations in the linked binaries confirms vmaskmovps/pd, replacing floating point multiply/add instructions without fast-math.
  • Behavior changed:

    • No.
    • Yes. Return the selected floating point value without contamination from other branches or loss of negative zero. Date/time selection preserves the existing results.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For Reviewer who merge this PR)

  • Confirm the release note
  • Confirm test cases
  • Confirm document
  • Add branch pick label

### What problem does this PR solve?

Issue Number: N/A

Related PR: N/A

Problem Summary: Non-nullable FLOAT/DOUBLE CASE assembles results by multiplying
branch values by zero or one and adding them to a zero-initialized result. An
unselected Infinity or NaN therefore contaminates a finite selected result, and
a selected negative zero loses its sign. For example, a three-row CASE selecting
1, an overflowing multiplication, and 2 returns NaN, Infinity, NaN instead.

Use conditional stores for floating point columns so that only selected values
are copied. This preserves their bits and lets Clang generate AVX2 masked
loads/stores; a ternary source/result load can inhibit vectorization. Keep the
other type paths unchanged. Add bitwise unit tests, SQL regression coverage and
a benchmark that invokes the production result assembly function.

### Release note

Fix incorrect FLOAT/DOUBLE CASE results caused by unselected non-finite branch
values, and preserve selected negative zero.

### Check List (For Author)

- Test: 12 ASAN unit tests; test_case_float_nonfinite and
  test_short_circuit_evaluation regression suites passed. The original code
  fails all 12 new unit tests and the new SQL regression. Golden output was
  generated and verified through short-circuit evaluation using the runner.
  ASAN BE build, clang-format 16, header hygiene and clang-tidy passed.
  RELEASE AVX2 benchmarks on Xeon Platinum 8457C cover 42 scenarios using
  identical input, fixed CPU and repeated before/after/after/before runs;
  median CPU time decreased by 5% to 39%. Inspected all four floating point
  and index-width specializations in the linked binaries for masked SIMD.
- Behavior changed: Yes, return only the selected floating point branch value.
- Does this need documentation: No
@hello-stephen

Copy link
Copy Markdown
Contributor

Thank you for your contribution to Apache Doris.
Don't know what should be done next? See How to process your PR.

Please clearly describe your PR:

  1. What problem was fixed (it's best to include specific error reporting information). How it was fixed.
  2. Which behaviors were modified. What was the previous behavior, what is it now, why was it modified, and what possible impacts might there be.
  3. What features were added. Why was this function added?
  4. Which code was refactored and why was this part of the code refactored?
  5. Which functions were optimized and what is the difference before and after the optimization?

@HappenLee

Copy link
Copy Markdown
Contributor Author

/review

@HappenLee

Copy link
Copy Markdown
Contributor Author

run buildall

@github-actions github-actions Bot left a comment

Copy link
Copy Markdown
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Review result

Request changes for one hot-path performance issue. The IEEE-754 correctness change itself is sound: ordinary CASE now copies the selected FLOAT/DOUBLE value without arithmetic contamination, while nullable/no-ELSE and short-circuit routes retain direct insertion/selector semantics.

Critical checkpoints

  • Goal and proof: The implementation fixes unselected NaN/Infinity contamination and selected negative-zero loss. The bitwise typed unit matrix and ordered SQL cases cover FLOAT/DOUBLE, constants, non-finite values, signed zero, tails, both index widths, THEN/ELSE/no-ELSE, and both evaluation modes.
  • Scope and clarity: The change is focused, but the old accumulator initialization should not remain on the new direct-store specialization; see the inline finding.
  • Concurrency and lifecycle: No new threads, shared mutable state, locks, atomics, static initialization dependency, ownership cycle, or special teardown is introduced.
  • Configuration and compatibility: No configuration, FE-BE field, function symbol, storage/serialization format, persistence, transaction, or rolling-upgrade contract changes.
  • Parallel and conditional paths: Ordinary CASE builds a total compact branch index; nullable/no-ELSE uses update_result_normal; short-circuit CASE partitions selectors and directly fills result positions. These paths remain semantically consistent.
  • Tests and expected results: Test discovery and private-access build flags are valid, and the checked golden output matches the query loop and row counts. The 4,099-row SQL comment does not force a physical batch boundary under the default batch size, but that is not a substantive gap because the kernel is stateless, the three-row SQL exposes the old bug, and direct tests cover 4,095/4,096/4,099 tails.
  • Performance and memory: No new untracked owning buffer is introduced. MF-1 is the sole issue: a redundant O(rows) zero-write pass remains before total direct assignment. The benchmark inputs and stated AVX2 scope are otherwise structurally valid.
  • Errors, data correctness, observability, and writes: No Status/error path, persisted state, data-write atomicity, visibility/version rule, or observability requirement is changed. No other substantiated issue was found.
  • User focus: No additional user-provided focus was supplied.

This was a complete static review only; the review runner prohibits local builds and tests. Author-reported test/benchmark results were not independently executed. At submission time, BE UT, compile, and performance CI are still pending. Two full review rounds converged below the three-round cap, with all candidates adjudicated and the live base/head reverified at 0ff005b8d0bbaed15e435ebb92ce9b39502ed362.

Comment thread be/src/exprs/vcase_expr.h
@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 16989 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 0ff005b8d0bbaed15e435ebb92ce9b39502ed362, data reload: false

------ Round 1 ----------------------------------
============================================
q1	17651	2980	3001	2980
q2	2061	253	216	216
q3	10275	882	522	522
q4	4670	267	206	206
q5	7657	583	389	389
q6	139	121	98	98
q7	529	502	402	402
q8	9239	907	890	890
q9	3459	2439	2433	2433
q10	6545	868	740	740
q11	401	202	183	183
q12	626	266	204	204
q13	18139	1598	1192	1192
q14	163	158	143	143
q15	q16	462	406	385	385
q17	1396	918	806	806
q18	3194	2342	2313	2313
q19	1284	929	815	815
q20	374	290	201	201
q21	5607	1640	1897	1640
q22	333	272	231	231
Total cold run time: 94204 ms
Total hot run time: 16989 ms

----- Round 2, with runtime_filter_mode=off -----
============================================
q1	3371	3283	3296	3283
q2	519	421	393	393
q3	2266	2360	2281	2281
q4	1255	1244	927	927
q5	2253	2177	2159	2159
q6	177	122	90	90
q7	1034	962	895	895
q8	1606	1410	1410	1410
q9	3261	3228	3214	3214
q10	1974	1925	1719	1719
q11	361	275	263	263
q12	465	446	352	352
q13	1521	1562	1185	1185
q14	186	173	161	161
q15	q16	406	406	364	364
q17	3678	3376	3249	3249
q18	5086	4579	5161	4579
q19	935	856	864	856
q20	1065	1012	877	877
q21	3937	3227	3251	3227
q22	409	352	330	330
Total cold run time: 35765 ms
Total hot run time: 31814 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 83147 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpcds-tools
TPC-DS sf100 test result on commit 0ff005b8d0bbaed15e435ebb92ce9b39502ed362, data reload: false

query5	4246	409	324	324
query6	385	134	120	120
query7	4976	400	229	229
query8	297	127	133	127
query9	8693	2880	2841	2841
query10	390	228	208	208
query11	5399	1068	932	932
query12	113	71	80	71
query13	1185	460	329	329
query14	6084	2237	2119	2119
query14_1	2029	2003	1998	1998
query15	174	123	114	114
query16	912	365	346	346
query17	808	422	349	349
query18	2324	311	228	228
query19	160	136	100	100
query20	75	68	71	68
query21	199	97	83	83
query22	5613	5471	5628	5471
query23	6916	6437	6193	6193
query23_1	6296	6185	6199	6185
query24	7284	1091	762	762
query24_1	787	769	761	761
query25	446	274	221	221
query26	1233	224	128	128
query27	2785	374	255	255
query28	4726	1485	1499	1485
query29	915	417	323	323
query30	241	152	127	127
query31	819	399	332	332
query32	125	74	72	72
query33	443	202	174	174
query34	992	815	495	495
query35	401	412	355	355
query36	577	571	555	555
query37	118	75	67	67
query38	1031	855	852	852
query39	479	464	466	464
query39_1	485	465	465	465
query40	195	86	73	73
query41	53	54	50	50
query42	72	74	67	67
query43	237	243	214	214
query44	983	519	533	519
query45	112	107	102	102
query46	757	803	517	517
query47	757	771	704	704
query48	301	298	227	227
query49	537	228	180	180
query50	739	256	200	200
query51	8036	8018	8052	8018
query52	68	72	59	59
query53	189	209	197	197
query54	230	168	166	166
query55	73	63	56	56
query56	210	189	171	171
query57	677	711	676	676
query58	206	177	170	170
query59	1247	1274	1150	1150
query60	256	204	199	199
query61	162	128	139	128
query62	371	197	180	180
query63	172	146	141	141
query64	2829	782	665	665
query65	1660	1660	1649	1649
query66	1763	253	207	207
query67	10049	10035	10008	10008
query68	2999	1126	660	660
query69	343	223	200	200
query70	670	637	618	618
query71	246	180	163	163
query72	2300	1732	1558	1558
query73	611	587	353	353
query74	1964	1233	1147	1147
query75	1185	1122	975	975
query76	2351	705	523	523
query77	254	267	212	212
query78	4070	3801	3410	3410
query79	2699	835	612	612
query80	1575	321	270	270
query81	517	158	136	136
query82	618	123	97	97
query83	282	206	192	192
query84	288	108	85	85
query85	777	327	276	276
query86	479	177	168	168
query87	1030	989	893	893
query88	2935	2109	2083	2083
query89	295	193	174	174
query90	1986	123	123	123
query91	131	115	98	98
query92	93	66	70	66
query93	1740	1078	644	644
query94	629	240	225	225
query95	538	330	226	226
query96	771	553	272	272
query97	1086	1056	1036	1036
query98	168	139	132	132
query99	415	349	307	307
Total cold run time: 179550 ms
Total hot run time: 83147 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
ClickBench: Total hot run time: 14.76 s
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/clickbench-tools
ClickBench test result on commit 0ff005b8d0bbaed15e435ebb92ce9b39502ed362, data reload: false

query1	0.01	0.00	0.01
query2	0.08	0.04	0.04
query3	0.25	0.11	0.11
query4	1.60	0.09	0.10
query5	0.17	0.15	0.15
query6	1.28	0.71	0.68
query7	0.03	0.00	0.01
query8	0.05	0.03	0.03
query9	0.29	0.21	0.21
query10	0.38	0.36	0.34
query11	0.16	0.11	0.12
query12	0.14	0.12	0.11
query13	0.33	0.31	0.30
query14	0.45	0.46	0.46
query15	0.37	0.35	0.36
query16	0.20	0.22	0.22
query17	0.70	0.70	0.68
query18	0.18	0.17	0.17
query19	1.15	1.11	1.14
query20	0.02	0.01	0.01
query21	15.44	0.15	0.13
query22	5.06	0.04	0.04
query23	16.17	0.26	0.11
query24	3.01	0.33	0.26
query25	0.12	0.04	0.04
query26	0.84	0.16	0.12
query27	0.04	0.03	0.04
query28	3.68	0.56	0.28
query29	12.47	3.22	2.56
query30	0.26	0.12	0.12
query31	2.76	0.38	0.18
query32	3.50	0.32	0.23
query33	1.47	1.40	1.52
query34	15.34	2.18	1.81
query35	1.77	1.81	1.75
query36	0.46	0.29	0.30
query37	0.06	0.04	0.04
query38	0.05	0.03	0.03
query39	0.03	0.03	0.03
query40	0.12	0.07	0.08
query41	0.07	0.02	0.02
query42	0.03	0.03	0.02
query43	0.03	0.03	0.03
Total cold run time: 90.62 s
Total hot run time: 14.76 s

### What problem does this PR solve?

Issue Number: N/A

Related PR: apache#67896

Problem Summary: Date/time CASE result assembly uses a ternary assignment
while FLOAT/DOUBLE uses conditional stores. Share the existing conditional
store loop for DATE, DATETIME, DATEV2, DATETIMEV2, TIMESTAMP_NS and
TIMESTAMPTZ to copy only the selected value and remove duplicated branch
handling. Preserve the default-value initialization and the arithmetic
accumulator for the remaining numeric types. Extend the existing typed
selection tests to cover all eight types with both branch-index widths.

### Release note

None

### Check List (For Author)

- Test: Unit Test and Manual test
    - All 48 VCaseSelectionTest* tests pass with ASAN through run-be-ut.sh.
    - clang-format 16, header hygiene, git diff --check and clang-tidy pass.
      Header analysis uses the generated selection-test compiler flags.
    - ASAN BE compilation and linking pass; full packaging is blocked by
      an ASAN double-free in kuromoji_build_dict during OpenMP initialization.
- Behavior changed: No; selected date/time values are preserved.
- Does this need documentation: No
@HappenLee

Copy link
Copy Markdown
Contributor Author

run buildall

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.

2 participants