Skip to content

[opt](hive) vectorize single-char hive text split and cache KMP prefix table - #66164

Closed
Baymine wants to merge 1 commit into
apache:masterfrom
Baymine:improvement/vectorize-hive-text-split
Closed

[opt](hive) vectorize single-char hive text split and cache KMP prefix table#66164
Baymine wants to merge 1 commit into
apache:masterfrom
Baymine:improvement/vectorize-hive-text-split

Conversation

@Baymine

@Baymine Baymine commented Jul 28, 2026

Copy link
Copy Markdown
Contributor

Proposed changes

Issue Number: close #66163

Problem Summary

HiveTextFieldSplitter splits every Hive text line on the hot scan path. This PR removes two avoidable costs, both behavior-preserving (split results are identical):

  1. Single-char separator, no escape char (common case). The escape_char == 0 branch of _split_field_single_char scanned the line byte-by-byte. It now uses memchr to locate each separator, letting glibc use its vectorized search. The escape path is unchanged and still uses is_hive_text_separator_escaped.

  2. Multi-char separator (MultiDelimitSerDe). _split_field_multi_char rebuilt the KMP prefix table from the separator on every line. It is now computed once in the constructor (_kmp_next) and reused per call. This is a strict win (no downside).

Benchmark (memchr vs for+if, single-char path)

There is an existing note in new_plain_text_line_reader.cpp that for + if beats memchr "under normal short fields case". A micro-benchmark of the split loop (plain -O2, 4M iters/case) shows the tradeoff precisely:

field width for+if memchr speedup
3 bytes (tiny) 182 ns 192 ns 0.95x
6 bytes 210 ns 189 ns 1.11x
20 bytes 283 ns 175 ns 1.62x
100 bytes 958 ns 175 ns 5.48x
400 bytes 949 ns 58 ns 16.3x

memchr wins for field widths >= ~6 bytes (the normal case for real Hive columns: strings, numbers, timestamps, paths) and is dramatically faster for wide values. The only regression is ~5% for pathologically tiny 1-3 byte fields, which matches the existing note. If reviewers prefer, the fast path can be gated behind a small line-length threshold to keep for + if for tiny lines; happy to add that.

Release note

None

Check List (For committer)

  • Test

    • Regression test
    • Unit Test
    • Manual test (add detailed scripts or steps below)
    • No need to test or manual test. Reason:
  • Behavior changed:

    • No.
    • Yes.
  • Does this need documentation?

    • No.
    • Yes.

Check List (For author)

  • Unit Test: be/test/format/text/hive_text_field_splitter_test.cpp gains 5 cases covering the memchr fast-path boundaries (Hive \x01 default separator, long fields spanning SIMD chunks, consecutive/leading/trailing separators, multi-byte UTF-8), the escape vs no-escape divergence, the cached KMP table reused across rows, and the multi-char escape boundary conditions. HiveTextFieldSplitterTest 10/10 pass locally.

  • Manual test: micro-benchmark of the split loop (results above).

  • I have unit-tested and manually tested this change.

…x table

Issue Number: close apache#66163

Problem Summary:
Hive text field splitting has two avoidable costs on the hot scan path:
1. The single-char separator path scanned the line byte-by-byte even when no
   escape char is configured (the common case). Replace that loop with a
   memchr-based scan when escape_char == 0, which lets the C library use a
   vectorized separator search. The escape path is unchanged and still uses
   is_hive_text_separator_escaped.
2. The multi-char (MultiDelimitSerDe) path rebuilt the KMP prefix table on every
   line. Precompute it once in the constructor and reuse it per call.

Both changes are behavior-preserving; split results are identical.

Release note: None
@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?

@Baymine

Baymine commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

run buildall

@Baymine

Baymine commented Jul 28, 2026

Copy link
Copy Markdown
Contributor Author

Closing this: a more rigorous benchmark (interleaved runs, pinned core) shows the memchr fast path regresses ~10-14% for sub-6-byte fields (break-even at ~6 bytes, only a clear win at >=20 bytes), which confirms the existing 'for+if beats memchr for short fields' note. Will revisit with a width-gated hybrid if pursued.

@Baymine Baymine closed this Jul 28, 2026
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.

[Enhancement] Vectorize single-char Hive text field split and cache the KMP prefix table

2 participants