Skip to content

[refactor](be) remove CHAR padding on read - #63291

Merged
yiguolei merged 1 commit into
apache:masterfrom
csun5285:feature/shrink-char-padding-pushdown
Jun 1, 2026
Merged

[refactor](be) remove CHAR padding on read #63291
yiguolei merged 1 commit into
apache:masterfrom
csun5285:feature/shrink-char-padding-pushdown

Conversation

@csun5285

@csun5285csun5285 commented May 15, 2026

Copy link
Copy Markdown
Contributor
  • [doc] note CHAR BloomFilter no longer takes effect since 4.1.2 doris-website#3759

  • Problem: The CHAR padding contract leaked from the storage layer into the
    compute / predicate layers — every scan stripped padding at the Block level,
    while predicates re-padded values to match the on-disk shape. Logic was spread
    out and wasted work on every read.

    • Fix: On-disk format unchanged. The convertor still pads CHAR to the schema
      length on write, but the strip is pushed down to the page pre-decoder — the
      page cache holds unpadded data. All shrink_* / pad_* code above the page cache
      (SegmentIterator, Block, RowCursor, predicates) is removed.
    • BloomFilter: BF probing is skipped (return true, fall back to scan) for CHAR
      predicates — the BF hashes padded bytes but predicate values are unpadded, so
      the probe would never match. Other indexes (ZoneMap / inverted / bitmap) are
      unaffected.

What problem does this PR solve?

Issue Number: close #xxx

Related PR: #xxx

Problem Summary:

Release note

None

Check List (For Author)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Explain why:
      • This is a refactor/code format and no logic has been changed.
      • Previous test can cover this change.
      • No code files have been changed.
      • Other reason
  • Behavior changed:

    • No.
    • Yes.
  • 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

@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?

@csun5285

Copy link
Copy Markdown
ContributorAuthor

run buildall

@csun5285csun5285 reopened this May 15, 2026
@csun5285

Copy link
Copy Markdown
ContributorAuthor

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage `` 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 31623 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 80fd402175dd8b3a63a7726f77068e72641c52c3, data reload: false
------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL ============================================
q1	17804	3903	3869	3869
q2	q3	10920	1463	820	820
q4	4810	484	343	343
q5	10630	2320	2194	2194
q6	408	178	136	136
q7	977	791	624	624
q8	9590	1678	1697	1678
q9	7030	4974	4918	4918
q10	6454	2125	1792	1792
q11	442	276	242	242
q12	645	429	297	297
q13	18171	3371	2748	2748
q14	265	256	230	230
q15	q16	808	788	715	715
q17	998	982	922	922
q18	6848	5733	6007	5733
q19	1259	1361	1106	1106
q20	504	402	258	258
q21	5847	2678	2679	2678
q22	475	379	320	320
Total cold run time: 104885 ms
Total hot run time: 31623 ms
----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL ============================================
q1	4551	4673	4434	4434
q2	q3	4871	5216	4597	4597
q4	2149	2217	1470	1470
q5	4794	4652	4718	4652
q6	241	182	131	131
q7	1824	1600	1398	1398
q8	2228	1918	1921	1918
q9	7291	7307	7201	7201
q10	4509	4420	4018	4018
q11	533	380	344	344
q12	712	717	505	505
q13	3043	3407	2772	2772
q14	296	293	248	248
q15	q16	680	705	603	603
q17	1270	1235	1231	1231
q18	7842	6963	6952	6952
q19	1136	1164	1076	1076
q20	2214	2216	1915	1915
q21	5319	4576	4400	4400
q22	530	466	412	412
Total cold run time: 56033 ms
Total hot run time: 50277 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 168893 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 80fd402175dd8b3a63a7726f77068e72641c52c3, data reload: false
query5	4328	642	515	515
query6	329	238	197	197
query7	4289	564	304	304
query8	315	227	219	219
query9	8823	4042	3975	3975
query10	461	341	289	289
query11	5841	2357	2130	2130
query12	179	131	125	125
query13	1263	610	439	439
query14	5891	5393	5035	5035
query14_1	4360	4364	4306	4306
query15	208	205	188	188
query16	1003	465	450	450
query17	1175	765	615	615
query18	2596	504	368	368
query19	217	207	181	181
query20	145	134	133	133
query21	215	144	118	118
query22	13591	13547	13418	13418
query23	17252	16354	15995	15995
query23_1	16220	16262	16114	16114
query24	7389	1782	1295	1295
query24_1	1293	1263	1303	1263
query25	539	481	414	414
query26	1300	326	185	185
query27	2664	566	347	347
query28	4458	1964	1980	1964
query29	979	608	491	491
query30	313	242	204	204
query31	1117	1068	929	929
query32	88	76	72	72
query33	541	372	303	303
query34	1160	1142	615	615
query35	770	782	674	674
query36	1366	1334	1202	1202
query37	151	101	90	90
query38	3198	3144	3043	3043
query39	931	912	904	904
query39_1	878	868	859	859
query40	228	149	126	126
query41	67	64	78	64
query42	111	110	109	109
query43	320	321	292	292
query44 query45	208	200	192	192
query46	1033	1181	718	718
query47	2351	2342	2243	2243
query48	390	418	292	292
query49	631	488	391	391
query50	998	349	242	242
query51	4329	4325	4213	4213
query52	107	107	93	93
query53	258	289	197	197
query54	312	269	253	253
query55	95	97	88	88
query56	302	311	298	298
query57	1398	1406	1339	1339
query58	315	281	263	263
query59	1552	1616	1453	1453
query60	321	322	317	317
query61	160	158	152	152
query62	687	620	574	574
query63	256	201	198	198
query64	2396	829	637	637
query65 query66	1679	483	357	357
query67	30053	30007	29891	29891
query68 query69	461	344	306	306
query70	1044	969	940	940
query71	317	278	269	269
query72	3034	2812	2569	2569
query73	805	770	424	424
query74	5065	4914	4734	4734
query75	2666	2575	2284	2284
query76	2288	1142	749	749
query77	396	411	334	334
query78	12062	12116	11540	11540
query79	1208	1017	700	700
query80	580	534	448	448
query81	449	275	240	240
query82	239	159	120	120
query83	279	277	249	249
query84	261	142	111	111
query85	849	572	450	450
query86	382	344	321	321
query87	3371	3330	3287	3287
query88	3482	2661	2657	2657
query89	423	389	336	336
query90	2160	180	176	176
query91	173	164	141	141
query92	80	76	73	73
query93	1343	1523	944	944
query94	528	353	318	318
query95	666	478	349	349
query96	1002	760	305	305
query97	2686	2682	2585	2585
query98	236	227	223	223
query99	1126	1126	982	982
Total cold run time: 251038 ms
Total hot run time: 168893 ms

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 83.33% (125/150) 🎉

Increment coverage report
Complete coverage report

CategoryCoverage
Function Coverage73.60% (27805/37779)
Line Coverage57.49% (301005/523543)
Region Coverage54.59% (250928/459698)
Branch Coverage56.20% (108639/193292)

@hello-stephen

Copy link
Copy Markdown
Contributor

FE Regression Coverage Report

Increment line coverage 0.00% (0/33) 🎉
Increment coverage report
Complete coverage report

@csun5285
csun5285force-pushed the feature/shrink-char-padding-pushdown branch 2 times, most recently from fb80b35 to 2fb1707CompareMay 16, 2026 01:46
@csun5285

Copy link
Copy Markdown
ContributorAuthor

run buildall

@hello-stephen

Copy link
Copy Markdown
Contributor

FE UT Coverage Report

Increment line coverage `` 🎉
Increment coverage report
Complete coverage report

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-H: Total hot run time: 31114 ms
machine: 'aliyun_ecs.c7a.8xlarge_32C64G'
scripts: https://github.com/apache/doris/tree/master/tools/tpch-tools
Tpch sf100 test result on commit 2fb170769c6af38fe25a6a3cfc6da410c0cbca61, data reload: false
------ Round 1 ----------------------------------
orders	Doris	NULL	NULL	0	0	0	NULL	0	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL ============================================
q1	17588	3958	3950	3950
q2	q3	10979	1368	805	805
q4	4741	477	361	361
q5	10357	2306	2112	2112
q6	370	180	138	138
q7	957	787	632	632
q8	9691	1829	1471	1471
q9	6947	4954	4970	4954
q10	6485	2122	1856	1856
q11	445	266	247	247
q12	686	418	291	291
q13	18283	3352	2803	2803
q14	263	258	237	237
q15	q16	813	767	711	711
q17	879	940	988	940
q18	7132	5791	5559	5559
q19	1181	1246	961	961
q20	521	407	258	258
q21	5756	2532	2609	2532
q22	432	359	296	296
Total cold run time: 104506 ms
Total hot run time: 31114 ms
----- Round 2, with runtime_filter_mode=off -----
orders	Doris	NULL	NULL	150000000	42	6422171781	NULL	22778155	NULL	NULL	2023-12-26 18:27:23	2023-12-26 18:42:55	NULL	utf-8	NULL	NULL ============================================
q1	4195	4119	4211	4119
q2	q3	4535	4901	4365	4365
q4	2083	2200	1390	1390
q5	4377	4281	5107	4281
q6	243	190	140	140
q7	1980	1799	1585	1585
q8	2424	2132	2057	2057
q9	7844	7790	7570	7570
q10	4592	4531	4098	4098
q11	588	411	370	370
q12	750	742	516	516
q13	3302	3721	2948	2948
q14	303	309	272	272
q15	q16	731	738	659	659
q17	1316	1332	1322	1322
q18	7819	7336	6810	6810
q19	1167	1096	1135	1096
q20	2217	2218	1936	1936
q21	5339	4657	4480	4480
q22	520	466	407	407
Total cold run time: 56325 ms
Total hot run time: 50421 ms

@hello-stephen

Copy link
Copy Markdown
Contributor
TPC-DS: Total hot run time: 168927 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 2fb170769c6af38fe25a6a3cfc6da410c0cbca61, data reload: false
query5	4338	655	537	537
query6	324	216	202	202
query7	4320	535	299	299
query8	322	222	206	206
query9	8848	4030	3988	3988
query10	454	344	301	301
query11	5750	2337	2147	2147
query12	191	130	127	127
query13	1320	595	447	447
query14	5879	5322	5037	5037
query14_1	4318	4282	4294	4282
query15	206	202	184	184
query16	1030	455	406	406
query17	1112	732	587	587
query18	2698	487	346	346
query19	213	192	161	161
query20	139	132	128	128
query21	210	137	120	120
query22	13549	13548	13345	13345
query23	17064	16434	16037	16037
query23_1	16220	16338	16025	16025
query24	7431	1764	1313	1313
query24_1	1292	1325	1320	1320
query25	592	510	451	451
query26	1321	322	174	174
query27	2728	567	340	340
query28	4434	1971	1967	1967
query29	1047	670	527	527
query30	312	242	204	204
query31	1126	1081	948	948
query32	95	77	76	76
query33	552	367	321	321
query34	1171	1124	659	659
query35	801	815	683	683
query36	1341	1380	1179	1179
query37	154	104	104	104
query38	3166	3142	3076	3076
query39	926	939	910	910
query39_1	893	880	892	880
query40	238	161	133	133
query41	77	68	69	68
query42	118	111	112	111
query43	334	332	293	293
query44 query45	217	208	196	196
query46	1045	1222	746	746
query47	2357	2369	2217	2217
query48	404	423	293	293
query49	648	514	409	409
query50	1046	348	260	260
query51	4300	4299	4185	4185
query52	108	111	99	99
query53	262	286	211	211
query54	333	289	274	274
query55	96	93	85	85
query56	314	323	310	310
query57	1430	1428	1333	1333
query58	318	286	276	276
query59	1586	1708	1450	1450
query60	368	317	309	309
query61	160	159	157	157
query62	673	624	573	573
query63	244	197	204	197
query64	2385	804	629	629
query65 query66	1679	478	367	367
query67	30131	30120	29885	29885
query68 query69	474	366	295	295
query70	967	957	973	957
query71	313	276	267	267
query72	2909	2745	2387	2387
query73	866	758	444	444
query74	5033	4928	4776	4776
query75	2692	2593	2294	2294
query76	2287	1163	763	763
query77	401	398	329	329
query78	12169	12181	11650	11650
query79	1499	1038	761	761
query80	915	560	459	459
query81	513	282	247	247
query82	1362	161	127	127
query83	347	278	250	250
query84	262	140	110	110
query85	911	546	456	456
query86	426	359	321	321
query87	3370	3440	3213	3213
query88	3552	2708	2660	2660
query89	447	390	340	340
query90	1795	182	185	182
query91	177	164	140	140
query92	81	80	71	71
query93	1600	1518	841	841
query94	621	357	321	321
query95	679	383	349	349
query96	1044	822	323	323
query97	2718	2737	2569	2569
query98	242	225	230	225
query99	1120	1100	983	983
Total cold run time: 253512 ms
Total hot run time: 168927 ms

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 82.31% (121/147) 🎉

Increment coverage report
Complete coverage report

CategoryCoverage
Function Coverage71.77% (27115/37779)
Line Coverage55.09% (288429/523537)
Region Coverage52.32% (240497/459692)
Branch Coverage53.50% (103416/193288)

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 82.31% (121/147) 🎉

Increment coverage report
Complete coverage report

CategoryCoverage
Function Coverage71.78% (27119/37779)
Line Coverage55.11% (288532/523537)
Region Coverage52.31% (240442/459692)
Branch Coverage53.52% (103457/193288)

Comment threadbe/src/core/column/column_dictionary.h
Comment threadbe/src/core/column/predicate_column.h
Comment threadbe/src/core/column/column_dictionary.h
Comment threadbe/src/core/column/predicate_column.h Outdated
Comment threadbe/src/core/column/predicate_column.h Outdated
Comment threadbe/src/core/data_type_serde/data_type_string_serde.cpp Outdated
Comment threadbe/src/core/data_type_serde/data_type_string_serde.cpp
Comment threadbe/src/service/point_query_executor.cpp Outdated
@csun5285
csun5285force-pushed the feature/shrink-char-padding-pushdown branch 3 times, most recently from ce3ec6e to 3b3a6e4CompareMay 21, 2026 09:53
@csun5285csun5285 changed the title [Draft](be) push CHAR padding strip down to page decoder[refactor](be) push CHAR padding strip down to page decoderMay 21, 2026
@github-actions

Copy link
Copy Markdown
Contributor

PR approved by anyone and no changes requested.

@hello-stephen

Copy link
Copy Markdown
Contributor

BE Regression && UT Coverage Report

Increment line coverage 81.09% (163/201) 🎉

Increment coverage report
Complete coverage report

CategoryCoverage
Function Coverage72.01% (27423/38080)
Line Coverage55.37% (292781/528773)
Region Coverage52.45% (244223/465648)
Branch Coverage53.67% (105066/195750)

github-actions[bot]
github-actionsBot previously requested changes May 29, 2026

@github-actionsgithub-actionsBot 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.

I found correctness issues that can cause false pruning or value truncation.

Critical checkpoint conclusions:

  • Goal/test: the PR aims to make CHAR values unpadded at read/predicate time, but the current implementation does not fully preserve string/CHAR byte correctness and does not cover the inverted-index path.
  • Scope/focus: the change is broad across readers, predicates, and page decoders; some paths now rely on predecode-only normalization, which exposes missed index/read paths.
  • Concurrency/lifecycle/config/compatibility: no new concurrency, lifecycle, config, or wire/storage-format compatibility issue was identified beyond reading existing padded storage.
  • Parallel paths: bloom filters were disabled for unpadded CHAR predicates, but string inverted-index predicates still query unpadded values against padded indexed terms.
  • Tests: added unit tests cover several decoder/serde cases, but they do not cover the failing inverted-index scenario or embedded-NUL truncation.
  • Observability/transactions/writes: not applicable for this PR.

User focus: no additional user-provided review focus was supplied.

Comment threadbe/src/core/data_type_serde/data_type_string_serde.cpp
Comment threadbe/src/storage/segment/binary_plain_page_v2_pre_decoder.h
Comment threadbe/src/storage/predicate/predicate_creator_comparison.cpp
@csun5285

Copy link
Copy Markdown
ContributorAuthor
  1. a\0z cases aren't being considered here — these would also produce errors in the previous approach as well.
  2. Padding has no effect in the inverted index

@yiguolei
yiguolei merged commit e072997 into apache:masterJun 1, 2026
33 of 34 checks passed
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 2, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 2, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 2, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 2, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 2, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
zhaorongsheng pushed a commit to zhaorongsheng/doris that referenced this pull request Jun 4, 2026
- apache/doris-website#3759
- Problem: The CHAR padding contract leaked from the storage layer into
the
compute / predicate layers — every scan stripped padding at the Block
level,
while predicates re-padded values to match the on-disk shape. Logic was
spread
out and wasted work on every read. - Fix: On-disk format unchanged. The convertor still pads CHAR to the
schema
length on write, but the strip is pushed down to the page pre-decoder —
the
page cache holds unpadded data. All shrink_* / pad_* code above the page
cache
(SegmentIterator, Block, RowCursor, predicates) is removed. - BloomFilter: BF probing is skipped (return true, fall back to scan)
for CHAR
predicates — the BF hashes padded bytes but predicate values are
unpadded, so
the probe would never match. Other indexes (ZoneMap / inverted / bitmap)
are
unaffected. ### What problem does this PR solve?
Issue Number: close #xxx
Related PR: #xxx
Problem Summary:
### Release note
None
### Check List (For Author)
- Test <!-- At least one of them must be included. -->
- [x] Regression test
- [x] Unit Test
- [ ] Manual test (add detailed scripts or steps below)
- [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
- [ ] Previous test can cover this change.
- [ ] No code files have been changed.
- [ ] Other reason <!-- Add your reason? -->
- Behavior changed:
- [ ] No.
- [ ] Yes. <!-- Explain the behavior change -->
- Does this need documentation?
- [ ] No.
- [ ] Yes. <!-- Add document PR link here. eg:
apache/doris-website#3759>
### Check List (For Reviewer who merge this PR)
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR
should merge into -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 4, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 4, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 4, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 5, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 5, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 6, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 12, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 15, 2026
PredicateColumnType<T> was a storage-layer wrapper that flattened every
predicate column into PaddedPODArray<value_type> so the SIMD-friendly
predicate-eval loops could use a uniform `data_array[i]` access regardless
of the underlying type. The cost was: a parallel column hierarchy with
restricted API, an extra 16 bytes/row (StringRef header) for strings on
top of the actual chars, and the need for callers to thread the column's
"role" (predicate vs output) down through the read path.
This change retires PredicateColumnType entirely:
- Schema::get_predicate_column_ptr now allocates the canonical
PrimitiveTypeTraits<T>::ColumnType (ColumnVector / ColumnDecimal /
ColumnString / ColumnIPv4 / ...). ColumnDictI32 is unchanged: it
still serves the low-cardinality string fast path for predicate eval.
- filter_by_selector is now implemented on ColumnVector<T> and
ColumnDecimal<T> with the same selector-gather semantics
PredicateColumnType used.
- ColumnString does not expose a contiguous StringRef[] array, so per-row
access in predicate evaluators flows through ColumnElementView<Type>
(already in core/column/column_execute_util.h for the compute layer)
rather than a pointer subscript:
ColumnElementView<Type> view {column};
_base_loop_vec<...>(size, flags, null_map, view, _value);
ColumnElementView numeric specialization yields T via pointer
arithmetic; the string specialization yields StringRef via
get_data_at — same `view[i]` call shape, no if-constexpr at the
call site. The compute-layer ColumnElementView gains size() /
operator[] aliases and its TYPE_STRING specialization is generalized
to all is_string_type(PType) via a defaulted bool template param so
TYPE_CHAR / TYPE_VARCHAR / TYPE_JSONB all resolve correctly.
- For HybridSet::find paths that need const T* per row, a small
ColumnPointerCursor<Type> lives next to ColumnElementView. Numeric
specialization holds a const T* (zero copy); string specialization
stages each row into a member StringRef and returns its address
(HybridSet::find consumes synchronously, so the staged-cell reuse
is safe).
- BloomFilter / BitmapFilter find_fixed_len_olap_engine API is
redesigned to take `const IColumn&` instead of a `const char*` that
was reinterpreted as `const T[]`. CommonFindOp / StringFindOp each
specialize per-row access:
CommonFindOp: reads column.get_raw_data() as `const T*`, passes
`[data](int i){return data[i];}` as the accessor.
StringFindOp: `assert_cast<const ColumnString&>(column)`, passes
`[&col](int i){return col.get_data_at(i);}`.
Storage-side BF/Bitmap callers collapse to one line and the previous
workaround (materializing a temporary `vector<StringRef>` to satisfy
the legacy char* API) goes away — saves one jemalloc + N×16-byte
store/load per evaluate on string columns.
- Schema-template CHAR predicate columns previously got trailing-zero
padding stripped on every PredicateColumnType<TYPE_CHAR>::get_data_at
call. ColumnString no longer does that; the strip is handled by
Block::shrink_char_type_column_suffix_zero on the output side (and
page-decoder-level for the read path per upstream PR apache#63291), so no
extra pass is needed here.
- ColumnDictI32::convert_to_predicate_column_if_dictionary now produces
ColumnString for mid-batch dict->plain fallback.
- predicate_column.h / predicate_column_test.cpp deleted; the
PredicateColumnHolderType<T> transition alias and all
core/column/predicate_column.h includes are removed.
On the _base_loop_vec signature: upstream used
`const TArray* __restrict data_array` which worked because
PredicateColumnType<TYPE_STRING> physically held a contiguous StringRef[].
After this change the string side is ColumnElementView<TYPE_STRING> (a
struct value), and `__restrict` is a pointer-only qualifier — so we pass
TArray by value without it. Verified on a Release build (objdump) that
this does NOT regress SIMD: vectorizable types (INT / BIGINT /
dict-encoded string) still emit fully-vectorized loops (vpcmpeqd /
vpcmpeqq, 4× unrolled, 16 elements/iter). The compiler's loop versioning
emits one runtime alias check at function entry (~5 cycles, <0.5% of a
1024-row batch's total cost); the main loop body is identical to the
__restrict version. Non-vectorizable types (LARGEINT / DOUBLE / DECIMAL /
ColumnString memcmp) were scalar regardless of __restrict. See the
comment above _base_loop_vec for full rationale.
Storage compiles clean in both Release and ASAN trees. Targeted UTs:
319 tests across 13 suites (Column* / Predicate* / Segment* /
BloomFilterFunc / BitmapFilterPredicate /
ColumnExecuteUtil): PASS, 0 FAIL.
Co-Authored-By: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
csun5285 added a commit to csun5285/doris that referenced this pull request Jun 16, 2026
- apache/doris-website#3759
- Problem: The CHAR padding contract leaked from the storage layer into
the
compute / predicate layers — every scan stripped padding at the Block
level,
while predicates re-padded values to match the on-disk shape. Logic was
spread
out and wasted work on every read.
- Fix: On-disk format unchanged. The convertor still pads CHAR to the
schema
length on write, but the strip is pushed down to the page pre-decoder —
the
page cache holds unpadded data. All shrink_* / pad_* code above the page
cache
(SegmentIterator, Block, RowCursor, predicates) is removed.
- BloomFilter: BF probing is skipped (return true, fall back to scan)
for CHAR
predicates — the BF hashes padded bytes but predicate values are
unpadded, so
the probe would never match. Other indexes (ZoneMap / inverted / bitmap)
are
unaffected.
Issue Number: close #xxx
Related PR: #xxx
Problem Summary:
None
- Test <!-- At least one of them must be included. -->
- [x] Regression test
- [x] Unit Test
- [ ] Manual test (add detailed scripts or steps below)
- [ ] No need to test or manual test. Explain why:
- [ ] This is a refactor/code format and no logic has been changed.
- [ ] Previous test can cover this change.
- [ ] No code files have been changed.
- [ ] Other reason <!-- Add your reason? -->
- Behavior changed:
- [ ] No.
- [ ] Yes. <!-- Explain the behavior change -->
- Does this need documentation?
- [ ] No.
- [ ] Yes. <!-- Add document PR link here. eg:
apache/doris-website#3759>
- [ ] Confirm the release note
- [ ] Confirm test cases
- [ ] Confirm document
- [ ] Add branch pick label <!-- Add branch pick label that this PR
should merge into -->
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
(cherry picked from commit e072997)
yiguolei pushed a commit that referenced this pull request Jun 17, 2026
…anch-4.1 (#63153#63233#63622#63291#64341) (#64550)
pick from master #63153#63233#63622#63291#64341
---------
Co-authored-by: Claude Opus 4.7 (1M context) <noreply@anthropic.com>
Sign up for freeto join this conversation on GitHub. Already have an account? Sign in to comment

Labels

approvedIndicates a PR has been approved by one committer.dev/4.1.3-mergedkind/behavior-changedreviewed

Projects

None yet

Development

Successfully merging this pull request may close these issues.

5 participants

@csun5285@hello-stephen@yiguolei@gavinchou@bobhan1