airborne12 opened a new pull request, #70268:
URL: https://github.com/apache/doris/pull/70268

   > Stacked PR in the SNII / text analysis / ANN CLucene removal series (index 
at the end). It depends on #70267 (T8), whose commits show in this diff until 
they merge; review only this PR's own commits: `53b3cdc2ec0` [improvement](be) 
Match the ik dictionaries through a flat trie. It stays a draft until its 
dependencies merge.
   
   ### What problem does this PR solve?
   
   Issue Number: None
   
   Related PR: #70267
   
   Problem Summary:
   
   The ik tokenizer walked dictionaries of DictSegment nodes,
   each a map or array of child pointers, reached them through
   Dictionary::getSingleton() for every character, asked a chain of range tests
   for each character's type, regularized every word, kept its candidate hits in
   a std::list and its results in a std::queue rebuilt for each value, and built
   a std::string for every word it handed out. The tokens do not change.
   
   - dic/FlatDict.h: the dictionaries as breadth-first flat tries of 12-byte
     nodes, children sorted (scanned up to 8, bisected above), with a table of
     0x10000 entries for the root's children. match() and isWord() keep the hit
     semantics of the old walk. DictSegment is now a load-time builder: each
     loader fills one, turns it into a FlatDict and frees it, and a load is 
still
     published only after it succeeds, so a failed load can be tried again. The
     FlatDict constructor lives in DictSegment.cpp, since a new source file 
would
     move the Storage unity batches. dic/Hit.h holds (const FlatDict*, node).
   - core/CharacterUtil: identifyCharType reads a table of 0x10000 entries built
     once for the BMP, the block rules above it; a character's type is taken
     once, on the regularized character; regularizeInPlace returns a word with 
no
     0xE3 or 0xEF byte and no ASCII capital untouched, and regularizeString 
wraps
     it with its old signature.
   - core/IKSegmenter, AnalyzeContext, CJKSegmenter, CN_QuantifierSegmenter: the
     Dictionary is taken once by reference instead of through getSingleton() on
     every character; the four segmenters are members called in the old order;
     an idle quantifier segmenter only releases its lock on a character that is
     not Chinese, and the surrogate pair segmenter only runs on surrogate pairs;
     CJKSegmenter's hits are a vector compacted in place in the same order; the
     results are one vector reused across windows and values; words are handed
     out as views, and Lexeme keeps no string.
   - IKTokenizer: the words of a value lie back to back in one buffer reused for
     every value and are lowercased there, in lenient and strict mode alike; a
     value that does not decode still throws at reset().
   
   Kept as they decide tokens: charEnd is the current index after matchWithHit
   and the index plus one after matchInMainDict, which the quantifier path reads
   as its next index; the 4096-byte windows with BUFF_EXHAUST_CRITICAL; the
   255-byte cut at a character boundary; lowercase always on for dictionary
   matching, so "Hello ABC" with lower_case=false still gives hello and ABC.
   
   An IKTokenizer or IKSegmenter now needs a loaded dictionary when it is made,
   not at its first Chinese character; the factory and the builtin preset load
   it first.
   
   ### Release note
   
   The ik parser tokenizes about 1.8 times as fast, and its dictionaries take
   about a tenth of the memory.
   
   ### Check List (For Author)
   
   - Test <!-- At least one of them must be included. -->
       - [ ] Regression test
       - [x] Unit Test
       - [x] 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?  -->
   
       Details:
       - BE UT under ASAN: the analysis, index and search suites with the T5 to
         T9 ones, 4213 of 4220 tests pass and the 7 skipped are the opt-in ones;
         the code point scan passes with ANALYSIS_GOLDEN_FULL=1. New:
         IKFlatDictTest (FlatDict against DictSegment for every word, prefix and
         suffix of main.dic, quantifier.dic, stopword.dic and 
extra_stopword.dic,
         every step and length compared; 20k random strings against a trie with
         supplementary-plane words and nodes of more than 8 children; no
         DictSegment left after Dictionary::initial; a tokenizer keeps working
         with the singleton gone, so the per-character path does not reach it)
         and IKCharacterUtilTest (the type table against the block rules on 
every
         rune up to 0x1FFFFF; regularizeString against the old implementation on
         300k random byte strings). ik_anayzer_test gains "Hello ABC" (hello,
         ABC without lowercase; hello, abc with it), a character on the 
4096-byte
         boundary at offsets 4088 to 4100 for é, 中 and 😀, and 你好\xE4\xB8
         throwing. IndexFileWriterTest.CacheEvictionTest failed once in a run of
         the wider suite list and passes on its own and in the later runs: it
         depends on what earlier tests leave in a shared searcher cache.
       - The analysis golden files are unchanged, and the benchmark's digest 
mode
         gives the same tokens as the T1 baseline binary in all 5,240,060 rows,
         ik_smart and ik_max_word among them.
       - The new code was also compared with the old ik code directly, outside
         the BE build: 28,659,978 values in 8 modes (ik_smart and ik_max_word,
         lower_case on and off, strict and lenient), through kept and new
         streams: every dictionary line alone and joined, random strings with
         invalid bytes, every offset around the 4096-byte boundary and all six
         corpora in full. 523M tokens and 211,200 identical exceptions, 0
         differences; an ASAN/UBSan run of 7.5M values found none, and no
         sanitizer report. In that harness, measured as thread CPU time with old
         and new taking turns on each 128 KiB chunk (the host load reached about
         410, which made wall time useless; old against old gave 0.97 to 1.00),
         the new code is 1.71 to 1.92 times as fast on every corpus in both
         modes, and the loaded dictionaries take 6.0 MB instead of 64.2 MB.
       - Throughput of the benchmark binary of this change with
         be/benchmark/analyzer_ab.py: five alternating rounds, the median of the
         per-round ratios on each corpus and their geometric mean, reuse/ and
         row/, at a host load of 50 to 135. Against the T1 baseline: ik_max_word
         1.84 and 1.74 (zh_wiki 1.58 and 1.54), ik_smart 1.80 and 1.70 (zh_wiki
         1.50 and 1.42), no corpus below 1.42. Against T8: ik_max_word 1.93 and
         1.76, ik_smart 1.89 and 1.75.
       - check-build-hygiene.sh passes; clang-format 16 leaves the changed files
         as they are; run-clang-tidy.sh reports nothing on the changed lines.
   
   - Behavior changed:
       - [x] No.
       - [ ] Yes.
           Every token of ik_smart and ik_max_word is the same as before 
(above).
   
   - Does this need documentation?
       - [x] No.
       - [ ] Yes.
   
   ### Check List (For Reviewer who merge this PR)
   
   - [ ] Confirm the release note
   - [ ] Confirm test cases
   - [ ] Confirm document
   - [ ] Add branch pick label
   
   🤖 Generated with [Claude Code](https://claude.com/claude-code)
   


-- 
This is an automated message from the Apache Git Service.
To respond to the message, please log on to GitHub and use the
URL above to go to the specific comment.

To unsubscribe, e-mail: [email protected]

For queries about this service, please contact Infrastructure at:
[email protected]


---------------------------------------------------------------------
To unsubscribe, e-mail: [email protected]
For additional commands, e-mail: [email protected]

Reply via email to