shbhar opened a new pull request, #16859:
URL: https://github.com/apache/lucene/pull/16859

   ## Summary
   I work in Amazon advertising, where we have some low latency use cases for 
exact primary key ID lookups as part of other querying. For many types of IDs 
like UUIDs or ASINs which are mostly random and dont share prefixes/suffixes, 
standard block tree based lookup within `TermQuery` or `TermInSetQuery` pays 
extra overhead over just a normal hash table without the benefits, so in this 
POC I tried to make `PrimaryKeyHashPostingsFormat` as a wrapper format (similar 
to `BloomFilteringPostingsFormat` design) where `prepareSeekExact`/`seekExact` 
goes through a hash table path instead of the wrapped one (default of 
Lucene104PostingsFormat with block tree), but all other behavior like for 
seekCeil, prefix query, etc is still delegated.
   
   A quick summary of results for UUID16 ids is below  and full details with 
more ids are later. The UUID16 & batch querying are new tasks I added for this 
work in luceneutil. Bloom is the `BloomFilteringPostingsFormat` wrapped over 
default.
   
   | luceneutil task (wikimedium10m, 15 segments, uuid16 IDs) | host | QPS 
default | QPS Bloom | QPS PrimaryKeyHash | Bloom vs default | PrimaryKeyHash vs 
default |
   |---|---|---|---|---|---|---|
   | PKLookup (4,000 IDs per task) | c8g.4xlarge | 116.6 | 193.6 | 618.2 | 
+65.7% | +425.9% |
   | | c8i.4xlarge | 156.1 | 294.4 | 896.6 | +89.3% | +474.4% |
   | PKLookupBatch (the same 4,000 IDs as one query: TermInSetQuery / 
PrimaryKeyHashInSetQuery) | c8g.4xlarge | 62.2 | 58.5 | 821.2 | -5.2% | 
+1220.2% |
   | | c8i.4xlarge | 76.2 | 75.0 | 1,182.4 | -1.7% | +1454.2% |
   | Index size | | 4.88 GB | 4.89 GB | 5.17 GB | +0.21% | +5.97% |
   
   
   
   ## Key Design Details
   ### Hash Table Design
   This is mostly a typical open adressing hash table design (1 hash table per 
segment per field) with the records/control bytes and linear probing. The 
lucene specific twists are that the table is also partitioned to control memory 
usage during writes/merges and it has an integrated bloom filter to make misses 
a lot cheaper with multiple segments. Miss performance is quite important 
because in realistic indexes with many segments, most lookups will be misses 
even if the key is in the index.
   
   The record layout is of two types mentioned below and since we know all the 
keys at hash table construction time, we choose the appropriate one per segment.
   
   Note: ASCII diagram below generated with AI but looks correct to me
   
   > ```
   > Inline layout (all keys of the field have one length L <= 31):   a hit = 2 
dependent reads
   >
   >  slot:            8      9      10     11     12     13     14     15
   >  control bytes:  [a9]   [ba]   [a7]   [ab]   [00]   [f8]   [ff]   [cb]     
1 byte per slot, 8 read at once
   >                    |      |      |      |      |      |      |      |
   >  records:        [LHR]  [LAX]  [HKG]  [NRT]  [ 0 ]  [ORD]  [ZRH]  [YYZ]    
record i belongs to slot i
   >                  DocID(32 bits) | key bytes (8 x L bits)                   
 position = slot x (4 + L)
   >
   > 
---------------------------------------------------------------------------------------------------------------------
   > Term-order layout (variable lengths, or L > 31):                 a hit = 3 
dependent reads
   >
   >  control bytes:  [a9]   [ba]   [a7]   ...                                  
1 byte per slot
   >  slot words:     [ptr|dist|tag] [ptr|dist|tag] ...                         
8 bytes per slot (0 = empty)
   >                      \             \
   >  records (sorted):   [DocID | len (VInt) | key bytes][DocID | len | key 
bytes] ...   back to back, in term order
   >
   > control byte: 0x00 = empty slot (ends a miss); else 0x80 | 7 fingerprint 
bits of the hash
   > ```
   
   The extra complexity of these 2 layouts is to optimize for what I assume is 
the most common case of primary key which are fixed length and <=31 bytes. In 
benchmarks for index of 10 million UUID16 keys across 15 segments, single key 
hits were almost 25-40% faster on both graviton & intel hosts with inline 
layout compared to term order layout so I think it is worth it.
   
   ### Hash Function and bit distribution
   The current implementation uses a ported XXHash3-64 implementation instead 
of the Murmur3 implementation already in Lucene, since in benchmarks it is 2-3 
times faster for hashing alone on 16 byte keys, and hashing speed/throughput is 
very important in this design. 
   
   Below table shows how the 64-bit hash is used (from the top bit; PB = 
partition bits, 0-10 depending on the number of keys):
   
    | bits of h | width | use |
    |---|---|---|
    | 63 … 64-PB | PB (0-10) | partition |
    | 63-PB … 33-PB | 31 | home slot |
    | 32-PB … 23 | 10-PB | unused |
    | 22 … 16 | 7 | control byte = `0x80 \| these 7 bits` |
    | 15 … 0 | 16 | tag in the slot word (used for term-order layout only) |
   
   
   ### Batch key lookups
   This is the TermInSetQuery replacement for this use case since the 
underlying implementation would use seekCeil and therefore not the hash table 
implementation. It sorts, dedupes and then hashes the keys but then works on 32 
key chunks in stages (bloom filter, control batch then record batch) and not 
one by one to exploit hardware's memory level parallelism by having multiple 
reads in flight. This staged & batched implementation was 2-5x faster for 4096 
batch lookups vs one at a time for the same depending on key lengths and number 
of segments (faster when there are more segments and more misses)
   
   
   ### Bloom filtering
   Similar to bloom filtering postings format but integated into this format 
directly to quickly reject (key, segment) pairs that definitely dont exist than 
what would be possible with control bytes alone because there is no 
slot/probing etc cost for bloom filter. This variation is a [register blocked 
bloom filter](https://dl.acm.org/doi/abs/10.14778/3303753.3303757) which trades 
more false positive rate at the same bits for fewer reads (exactly 1) compared 
to a classic bloom filter, which is important because it has to be much cheaper 
than checking the control bytes itself otherwise it wont be useful. While it 
adds 10 bits per key, it was 2-3x faster than without it on batched key lookups 
(4096 batch) and 15 segment index of uuid16 keys.
   
   ### Index writing
   Hash tables are only built at flush and merge, and for merge we rebuild from 
scratch. We partition hash tables (upto 1024 default) to control the memory 
requirements during build (writers make the table one partition at a time) and 
that way it is bounded by the largest partition.
   
   ## Results
   
   ### Index size & Indexing time
   Since this is a wrapper format, we do expect increase in indexing time and 
index size, but the assumption is that ID fields would usually be a small part 
of the index and the size/time would be dominated by other fields.
   
   ID field size (id-only index, 10M docs, bytes per document). Bloom means the 
bloom filtering format wrapping default.
   
   | key shape | layout | default | Bloom | PrimaryKeyHash (10-bit filter) | 
PrimaryKeyHash / default |
   |---|---|---|---|---|---|
   | seq6 (6-byte sequential) from luceneutil's PKLookup | inline | 2.58 | 3.39 
| 18.49 | 7.2x |
   | asin (10-byte) | inline | 9.35 | 10.22 | 30.60 | 3.3x |
   | uuid16 (16-byte binary) | inline | 18.46 | 19.28 | 47.71 | 2.6x |
   | uuid36 (36-char string) | term order | 28.41 | 29.28 | 82.66 | 2.9x |
   
   Whole index, luceneutil wikimedium10m: +3.45% with its sequential IDs 
(+157.8 MB; Bloom +0.22%), +5.97% with uuid16 IDs (+291.4 MB; Bloom +0.21%), 
+10.48% with uuid36 IDs (+541.5 MB; Bloom +0.20%). A 50M uuid16 field's hash 
data is 1.46 GB.
   
   ID-only index build time (worst case: the documents have nothing but the ID):
   
   | index | host | default | Bloom | PrimaryKeyHash | PrimaryKeyHash / default 
|
   |---|---|---|---|---|---|
   | uuid16 50M, 2 segments | c8g | 63.0 / 66.5 s | 78.5 s | 88.4 / 89.1 s | 
1.34-1.40 |
   | uuid16 10M, 15 flushes | c8g | 9.11 / 9.21 s | 10.65 s | 13.70 / 13.65 s | 
1.48-1.50 |
   | uuid16 10M, 15 flushes | c8i | 7.07 / 7.03 s | 7.95 s | 10.63 / 10.62 s | 
1.50-1.51 |
   | seq6 10M, 15 flushes | c8g | 7.27 / 7.70 s | 10.50 s | 8.84 / 9.11 s | 
1.18-1.22 |
   | asin 10M, 1 segment | c8g | 13.23 s | 14.72 s | 15.86 s | 1.20 |
   | asin 10M, 1 segment | c8i | 9.89 s | 11.86 s | 12.78 s | 1.29 |
   | uuid36 string 10M, 1 segment | c8g | 19.77 s | 23.05 s | 27.25 s | 1.38 |
   
   luceneutil full indexing (whole documents, one sample each): 836.2 -> 877.4 
s (+4.9%) on c8g.24xlarge; 1,043.4 -> 983.4 s
   (-5.8%) on c8g.4xlarge, so within noise.
   
   ### LuceneUtil
   #### PKLookup wikimedium10m
   
   wikimedium10m, 15 segments, 20 JVMs per comparison. `pkhash` is the run 
label for PrimaryKeyHashPostingsFormat. 
   
   c8g (c8g.24xlarge, Graviton4), PK tasks only, 3 independent runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                        PKLookup      625.29      (0.2%)     2014.47     
(14.1%)  222.2% ( 207% -  237%) 0.000</b>
   <b>                        PKLookup      621.49      (0.4%)     2111.25      
(6.1%)  239.7% ( 232% -  247%) 0.000</b>
   <b>                        PKLookup      623.17      (0.5%)     2078.62     
(12.8%)  233.6% ( 219% -  248%) 0.000</b>
   </pre>
   
   c8g.4xlarge, PK tasks only (default vs pkhash, then default vs Bloom):
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                        PKLookup      430.76      (0.6%)      903.18      
(9.4%)  109.7% (  99% -  120%) 0.000</b>
   </pre>
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                        PKLookup      431.69      (0.6%)      228.25      
(0.4%)  -47.1% ( -47% -  -46%) 0.000</b>
   </pre>
   
   c8g.4xlarge, full task set (PKLookup mixed with the other tasks):
   
   <details><summary>all tasks</summary>
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
              BrowseMonthTaxoFacets       41.20      (5.7%)       40.63      
(6.4%)   -1.4% ( -12% -   11%) 0.461
                       OrHighNotMed      856.31      (7.9%)      845.76      
(8.7%)   -1.2% ( -16% -   16%) 0.640
                       OrHighNotLow      809.99      (8.4%)      800.16      
(9.6%)   -1.2% ( -17% -   18%) 0.671
                              range     8044.27      (2.8%)     7956.12      
(3.3%)   -1.1% (  -6% -    5%) 0.257
                      OrHighNotHigh      711.81      (7.4%)      704.23      
(8.3%)   -1.1% ( -15% -   15%) 0.668
                           BM25MSM2       12.74      (2.9%)       12.63      
(2.1%)   -0.9% (  -5% -    4%) 0.258
                      OrNotHighHigh      619.51      (6.9%)      614.11      
(7.7%)   -0.9% ( -14% -   14%) 0.705
              BrowseMonthSSDVFacets       19.25      (5.9%)       19.09      
(6.2%)   -0.8% ( -12% -   11%) 0.668
          BrowseDayOfYearSSDVFacets       19.48      (8.8%)       19.34      
(9.1%)   -0.7% ( -17% -   18%) 0.797
                         HighPhrase       24.07      (1.3%)       23.94      
(2.5%)   -0.6% (  -4% -    3%) 0.377
        BrowseRandomLabelTaxoFacets       16.27      (2.4%)       16.18      
(2.4%)   -0.5% (  -5% -    4%) 0.487
                           HighTerm     1231.46      (4.9%)     1225.32      
(4.3%)   -0.5% (  -9% -    9%) 0.732
                       OrNotHighMed      594.01      (4.2%)      591.41      
(4.6%)   -0.4% (  -8% -    8%) 0.753
                            LowTerm     2097.85      (3.1%)     2089.36      
(2.9%)   -0.4% (  -6% -    5%) 0.670
               HighTermTitleBDVSort       25.67      (0.8%)       25.57      
(1.2%)   -0.4% (  -2% -    1%) 0.236
                         AndHighLow     2012.08      (1.8%)     2004.66      
(2.0%)   -0.4% (  -4% -    3%) 0.541
                          OrHighLow     1150.17      (1.3%)     1146.29      
(1.5%)   -0.3% (  -3% -    2%) 0.440
            AndHighMedDayTaxoFacets       88.14      (0.8%)       87.85      
(1.0%)   -0.3% (  -2% -    1%) 0.260
          BrowseDayOfYearTaxoFacets       18.80      (1.8%)       18.74      
(1.9%)   -0.3% (  -3% -    3%) 0.595
                             Fuzzy2      100.53      (1.1%)      100.22      
(1.1%)   -0.3% (  -2% -    1%) 0.397
                          OrHighMed      422.30      (1.4%)      421.02      
(1.4%)   -0.3% (  -3% -    2%) 0.495
                       OrNotHighLow     1296.88      (1.3%)     1293.09      
(1.5%)   -0.3% (  -3% -    2%) 0.506
               BrowseDateTaxoFacets       18.61      (1.7%)       18.55      
(1.9%)   -0.3% (  -3% -    3%) 0.619
                             IntSet      658.86      (3.1%)      656.99      
(3.2%)   -0.3% (  -6% -    6%) 0.778
                             Fuzzy1      116.09      (1.1%)      115.80      
(1.2%)   -0.2% (  -2% -    2%) 0.496
                          MedPhrase      131.10      (1.2%)      130.81      
(2.1%)   -0.2% (  -3% -    3%) 0.672
                         OrHighHigh      167.85      (3.0%)      167.47      
(2.7%)   -0.2% (  -5% -    5%) 0.804
           AndHighHighDayTaxoFacets       18.29      (0.7%)       18.25      
(0.8%)   -0.2% (  -1% -    1%) 0.384
                  HighTermMonthSort     3561.67      (1.9%)     3556.29      
(2.0%)   -0.2% (  -3% -    3%) 0.805
                          LowPhrase      129.10      (1.7%)      128.91      
(2.0%)   -0.1% (  -3% -    3%) 0.809
                            MedTerm     1503.10      (4.0%)     1501.77      
(3.5%)   -0.1% (  -7% -    7%) 0.941
                         AndHighMed      405.74      (1.4%)      405.46      
(1.3%)   -0.1% (  -2% -    2%) 0.872
                         TermDTSort      413.20      (0.9%)      412.98      
(1.3%)   -0.1% (  -2% -    2%) 0.883
                          ConstMSM2     2224.17      (1.4%)     2222.98      
(1.9%)   -0.1% (  -3% -    3%) 0.919
               BrowseDateSSDVFacets        3.67      (0.7%)        3.67      
(0.7%)   -0.0% (  -1% -    1%) 0.824
                        AndHighHigh      162.24      (1.9%)      162.24      
(1.9%)   -0.0% (  -3% -    3%) 0.999
                   MustNotIODVRange      317.75      (1.7%)      317.76      
(1.8%)    0.0% (  -3% -    3%) 0.993
                             IntNRQ      147.11      (2.0%)      147.14      
(2.2%)    0.0% (  -4% -    4%) 0.974
                    LowSloppyPhrase       45.14      (1.2%)       45.15      
(1.3%)    0.0% (  -2% -    2%) 0.945
               MedTermDayTaxoFacets       72.75      (3.1%)       72.88      
(3.0%)    0.2% (  -5% -    6%) 0.863
                    MedSloppyPhrase       37.44      (1.3%)       37.51      
(1.5%)    0.2% (  -2% -    3%) 0.696
              HighTermDayOfYearSort      599.42      (1.8%)      600.53      
(2.0%)    0.2% (  -3% -    4%) 0.756
                        MedSpanNear       39.51      (2.2%)       39.59      
(2.5%)    0.2% (  -4% -    5%) 0.771
                       HighSpanNear        8.80      (4.8%)        8.82      
(5.1%)    0.3% (  -9% -   10%) 0.866
                LowIntervalsOrdered       51.97      (1.4%)       52.12      
(1.3%)    0.3% (  -2% -    3%) 0.496
                        LowSpanNear       43.50      (1.8%)       43.63      
(1.9%)    0.3% (  -3% -    4%) 0.619
                MedIntervalsOrdered       50.72      (1.5%)       50.89      
(1.3%)    0.3% (  -2% -    3%) 0.451
                   HighSloppyPhrase       21.10      (1.4%)       21.19      
(1.7%)    0.4% (  -2% -    3%) 0.421
             OrHighMedDayTaxoFacets       17.93      (2.2%)       18.01      
(2.1%)    0.5% (  -3% -    4%) 0.505
               HighIntervalsOrdered       23.27      (1.7%)       23.38      
(1.3%)    0.5% (  -2% -    3%) 0.312
                  HighTermTitleSort      193.53      (2.3%)      194.57      
(2.1%)    0.5% (  -3% -    5%) 0.445
                            Respell       94.77      (1.6%)       95.29      
(1.4%)    0.5% (  -2% -    3%) 0.259
                     AndMissingHigh     4917.06      (4.7%)     4953.71      
(5.9%)    0.7% (  -9% -   11%) 0.659
                            Prefix3      464.76      (1.9%)      468.82      
(1.3%)    0.9% (  -2% -    4%) 0.097
        BrowseRandomLabelSSDVFacets       14.96     (10.0%)       15.09     
(10.4%)    0.9% ( -17% -   23%) 0.781
   <b>                        Wildcard      157.53      (1.8%)      159.33      
(0.9%)    1.1% (  -1% -    3%) 0.011</b>
   <b>                        PKLookup      451.26      (1.3%)      720.23      
(1.7%)   59.6% (  55% -   63%) 0.000</b>
   </pre>
   
   </details>
   
   ##### PKLookupBatch (sequential IDs)
   
   PKLookupBatch: the same batches of 4,000 IDs (10% misses) as PKLookup, but 
each batch is one query: TermInSetQuery for default and Bloom, 
PrimaryKeyHashInSetQuery for PrimaryKeyHash (`pkhash`); 20 JVMs per comparison; 
bold = p < 0.05. QPS is directly comparable with the PKLookup rows (same 
batches, batches per second).
   
   c8g.4xlarge (Graviton4)
   
   default vs PrimaryKeyHash, 3 runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch      232.51      (2.9%)     1399.26     
(22.2%)  501.8% ( 463% -  542%) 0.000</b>
   <b>                   PKLookupBatch      242.56      (1.5%)     1500.53     
(27.5%)  518.6% ( 482% -  556%) 0.000</b>
   <b>                   PKLookupBatch      242.88      (3.8%)     1610.90     
(96.8%)  563.2% ( 445% -  690%) 0.000</b>
   </pre>
   
   default vs Bloom, 3 runs:
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch      232.17      (2.2%)      226.05      
(3.4%)   -2.6% (  -8% -    3%) 0.004</b>
   <b>                   PKLookupBatch      243.97      (2.6%)      236.64      
(1.9%)   -3.0% (  -7% -    1%) 0.000</b>
   <b>                   PKLookupBatch      240.86      (2.8%)      235.61      
(2.8%)   -2.2% (  -7% -    3%) 0.014</b>
   </pre>
   
   c8i.4xlarge (Intel Xeon 6)
   
   default vs PrimaryKeyHash, 3 runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch      267.56      (1.6%)     2083.17     
(66.8%)  678.6% ( 600% -  758%) 0.000</b>
   <b>                   PKLookupBatch      267.55      (1.5%)     2015.82     
(73.1%)  653.4% ( 570% -  738%) 0.000</b>
   <b>                   PKLookupBatch      267.80      (2.0%)     2196.34     
(47.9%)  720.1% ( 657% -  785%) 0.000</b>
   </pre>
   
   default vs Bloom, 3 runs:
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch      267.67      (1.3%)      258.44      
(1.8%)   -3.4% (  -6% -    0%) 0.000</b>
   <b>                   PKLookupBatch      270.43      (2.0%)      261.51      
(1.6%)   -3.3% (  -6% -    0%) 0.000</b>
   <b>                   PKLookupBatch      268.06      (1.7%)      259.25      
(1.6%)   -3.3% (  -6% -    0%) 0.000</b>
   </pre>
   
   #### UUID 16
   
   luceneutil PKLookup on wikimedium10m with `-idFormat uuid16` (quick 
luceneutil addition for UUIDs as 16-byte binary terms); 20 JVMs per comparison
   
   c8g.4xlarge (Graviton4)
   
   default vs PrimaryKeyHash (`pkhash`), 3 runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                        PKLookup      117.67      (2.0%)      618.81     
(91.4%)  425.9% ( 325% -  530%) 0.000</b>
   <b>                        PKLookup      116.59      (2.1%)      618.16    
(100.7%)  430.2% ( 320% -  544%) 0.000</b>
   <b>                        PKLookup      115.67      (2.7%)      516.70     
(39.9%)  346.7% ( 295% -  400%) 0.000</b>
   </pre>
   
   default vs Bloom, 3 runs:
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                        PKLookup      116.55      (2.3%)      197.31      
(9.6%)   69.3% (  56% -   83%) 0.000</b>
   <b>                        PKLookup      116.88      (1.9%)      191.98      
(8.4%)   64.2% (  52% -   76%) 0.000</b>
   <b>                        PKLookup      116.84      (2.1%)      193.58      
(8.6%)   65.7% (  53% -   77%) 0.000</b>
   </pre>
   
   c8i.4xlarge (Intel Xeon 6)
   
   default vs PrimaryKeyHash (`pkhash`), 3 runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                        PKLookup      156.49      (1.0%)      897.90     
(12.4%)  473.8% ( 455% -  492%) 0.000</b>
   <b>                        PKLookup      155.78      (1.0%)      896.25     
(11.6%)  475.3% ( 458% -  492%) 0.000</b>
   <b>                        PKLookup      156.10      (1.1%)      896.60     
(12.5%)  474.4% ( 455% -  493%) 0.000</b>
   </pre>
   
   default vs Bloom, 3 runs:
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                        PKLookup      155.51      (1.2%)      294.33      
(2.1%)   89.3% (  84% -   93%) 0.000</b>
   <b>                        PKLookup      156.83      (0.8%)      295.25      
(2.5%)   88.3% (  84% -   92%) 0.000</b>
   <b>                        PKLookup      155.30      (1.2%)      294.37      
(2.4%)   89.5% (  84% -   94%) 0.000</b>
   </pre>
   
   ##### PKLookupBatch (uuid16)
   
   PKLookupBatch: the same batches of 4,000 IDs (10% misses) as PKLookup, but 
each batch is one query: TermInSetQuery for default and Bloom, 
PrimaryKeyHashInSetQuery for PrimaryKeyHash (`pkhash`); 20 JVMs per comparison; 
bold = p < 0.05. QPS is directly comparable with the PKLookup rows (same 
batches, batches per second).
   
   c8g.4xlarge (Graviton4)
   
   default vs PrimaryKeyHash, 3 runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch       61.53      (0.8%)      802.91     
(23.3%) 1205.0% (1171% - 1238%) 0.000</b>
   <b>                   PKLookupBatch       62.20      (0.5%)      821.16     
(25.5%) 1220.2% (1188% - 1252%) 0.000</b>
   <b>                   PKLookupBatch       62.30      (0.9%)      827.13     
(16.6%) 1227.7% (1199% - 1256%) 0.000</b>
   </pre>
   
   default vs Bloom, 3 runs:
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch       61.45      (1.8%)       58.54      
(0.3%)   -4.7% (  -6% -   -2%) 0.000</b>
   <b>                   PKLookupBatch       61.75      (0.7%)       58.51      
(0.6%)   -5.2% (  -6% -   -3%) 0.000</b>
   <b>                   PKLookupBatch       62.14      (0.8%)       58.77      
(0.7%)   -5.4% (  -6% -   -3%) 0.000</b>
   </pre>
   
   c8i.4xlarge (Intel Xeon 6)
   
   default vs PrimaryKeyHash, 3 runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch       76.16      (0.6%)     1179.73     
(72.0%) 1449.1% (1368% - 1530%) 0.000</b>
   <b>                   PKLookupBatch       76.08      (0.5%)     1182.43     
(78.4%) 1454.2% (1368% - 1540%) 0.000</b>
   <b>                   PKLookupBatch       76.46      (0.9%)     1192.71     
(34.0%) 1459.9% (1412% - 1508%) 0.000</b>
   </pre>
   
   default vs Bloom, 3 runs:
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch       76.30      (0.7%)       75.03      
(0.8%)   -1.7% (  -3% -    0%) 0.000</b>
   <b>                   PKLookupBatch       76.69      (0.5%)       75.55      
(1.0%)   -1.5% (  -2% -    0%) 0.000</b>
   <b>                   PKLookupBatch       76.37      (0.6%)       74.94      
(0.8%)   -1.9% (  -3% -    0%) 0.000</b>
   </pre>
   
   #### UUID 36
   
   luceneutil PKLookup on wikimedium10m with `-idFormat uuid36` (the same UUIDs 
as 36-char strings); 20 JVMs per comparison; bold = p < 0.05.
   
   c8g.4xlarge (Graviton4)
   
   default vs PrimaryKeyHash (`pkhash`), 3 runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                        PKLookup       69.09      (0.6%)      243.87      
(4.5%)  253.0% ( 246% -  259%) 0.000</b>
   <b>                        PKLookup       68.81      (0.3%)      245.08      
(5.2%)  256.1% ( 249% -  262%) 0.000</b>
   <b>                        PKLookup       68.98      (0.5%)      246.86      
(5.2%)  257.9% ( 250% -  264%) 0.000</b>
   </pre>
   
   default vs Bloom, 3 runs:
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                        PKLookup       68.87      (0.5%)      135.46      
(1.1%)   96.7% (  94% -   98%) 0.000</b>
   <b>                        PKLookup       69.15      (0.3%)      136.33      
(1.7%)   97.2% (  94% -   99%) 0.000</b>
   <b>                        PKLookup       68.66      (0.6%)      136.56      
(1.9%)   98.9% (  95% -  102%) 0.000</b>
   </pre>
   
   c8i.4xlarge (Intel Xeon 6)
   
   default vs PrimaryKeyHash (`pkhash`), 3 runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                        PKLookup       64.35      (1.3%)      561.23     
(51.4%)  772.2% ( 710% -  835%) 0.000</b>
   <b>                        PKLookup       64.60      (1.3%)      585.49     
(45.6%)  806.4% ( 749% -  864%) 0.000</b>
   <b>                        PKLookup       65.43      (4.1%)      610.29     
(55.9%)  832.7% ( 741% -  931%) 0.000</b>
   </pre>
   
   default vs Bloom, 3 runs:
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                        PKLookup       64.01      (1.3%)      150.43      
(3.0%)  135.0% ( 129% -  141%) 0.000</b>
   <b>                        PKLookup       64.26      (1.6%)      151.12      
(3.7%)  135.2% ( 127% -  142%) 0.000</b>
   <b>                        PKLookup       68.01      (4.5%)      170.25     
(29.5%)  150.3% ( 111% -  192%) 0.000</b>
   </pre>
   
   ##### PKLookupBatch (uuid36)
   
   PKLookupBatch: the same batches of 4,000 IDs (10% misses) as PKLookup, but 
each batch is one query: TermInSetQuery for default and Bloom, 
PrimaryKeyHashInSetQuery for PrimaryKeyHash (`pkhash`); 20 JVMs per comparison; 
bold = p < 0.05. QPS is directly comparable with the PKLookup rows (same 
batches, batches per second).
   
   c8g.4xlarge (Graviton4)
   
   default vs PrimaryKeyHash, 3 runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch       34.34      (0.5%)      658.35     
(30.7%) 1816.9% (1777% - 1856%) 0.000</b>
   <b>                   PKLookupBatch       34.55      (0.6%)      668.98     
(28.0%) 1836.0% (1796% - 1876%) 0.000</b>
   <b>                   PKLookupBatch       34.71      (0.5%)      664.03     
(42.5%) 1813.0% (1761% - 1865%) 0.000</b>
   </pre>
   
   default vs Bloom, 3 runs:
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch       34.56      (0.5%)       33.34      
(0.4%)   -3.5% (  -4% -   -2%) 0.000</b>
   <b>                   PKLookupBatch       34.55      (0.6%)       33.16      
(0.6%)   -4.0% (  -5% -   -2%) 0.000</b>
   <b>                   PKLookupBatch       34.80      (0.5%)       33.34      
(0.6%)   -4.2% (  -5% -   -3%) 0.000</b>
   </pre>
   
   c8i.4xlarge (Intel Xeon 6)
   
   default vs PrimaryKeyHash, 3 runs:
   
   <pre>
                               Task QPS default      StdDev  QPS pkhash      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch       39.01      (1.5%)     1008.73     
(39.8%) 2485.7% (2408% - 2564%) 0.000</b>
   <b>                   PKLookupBatch       38.80      (1.4%)     1003.80     
(44.2%) 2487.0% (2408% - 2567%) 0.000</b>
   <b>                   PKLookupBatch       38.88      (1.3%)     1007.95     
(38.6%) 2492.4% (2420% - 2566%) 0.000</b>
   </pre>
   
   default vs Bloom, 3 runs:
   
   <pre>
                               Task QPS default      StdDev   QPS bloom      
StdDev                Pct diff p-value
   <b>                   PKLookupBatch       39.15      (1.5%)       38.78      
(1.1%)   -0.9% (  -3% -    1%) 0.023</b>
                      PKLookupBatch       38.81      (1.2%)       38.52      
(1.4%)   -0.7% (  -3% -    1%) 0.071
   <b>                   PKLookupBatch       38.95      (1.2%)       38.60      
(1.0%)   -0.9% (  -3% -    1%) 0.013</b>
   </pre>
   
   #### ASINs
   
   Standalone harness only (luceneutil has no ASIN-style ID format):
   
   | index | host | single-key hits | single-key 90% misses | batch 4,096 hits 
| batch 4,096 90% misses |
   |---|---|---|---|---|---|
   | asin 10M, 1 segment | c8g | 1,551 → 5,051 (3.26x) | 4,322 → 5,926 (1.37x) 
| 1,360 → 8,771 (6.45x) | 2,231 → 15,422 (6.91x) |
   | asin 10M, 1 segment | c8i | 2,128 → 3,602 (1.69x) | 3,473 → 5,690 (1.64x) 
| 2,057 → 10,179 (4.95x) | 1,960 → 16,157 (8.24x) |
   
   
   ## Previous related discussions
   Found the following related discussions (summaries by AI):
   
   > Related issues/PRs to link (all apache/lucene):
   > - [#15520](https://github.com/apache/lucene/issues/15520): should Lucene 
offer a helper for primary-key lookups (mikemccand).
   > - [#16128](https://github.com/apache/lucene/issues/16128): promote 
PerThreadPKLookup to core as PrimaryKeyLookup. Open, approved by epotyom, 
waiting on its author. Same API space as PrimaryKeyHashLookup.
   > - [#15820](https://github.com/apache/lucene/issues/15820) / 
[#15824](https://github.com/apache/lucene/issues/15824): trie `seekExact` 4x 
slower for UUID `_id` lookups vs the old FST (open).
   > - [#12513](https://github.com/apache/lucene/issues/12513) / 
[#12688](https://github.com/apache/lucene/issues/12688): tantivy-style terms 
dictionary; mikemccand: "The PKLookup gains are astounding!"
   > - [#5141](https://github.com/apache/lucene/issues/5141) (LUCENE-4069) and 
[#12986](https://github.com/apache/lucene/issues/12986): Bloom postings format 
history; heap at write time (jpountz), back-compat (rmuir).
   > - MemoryPostingsFormat removal and the nightly PKLookup drop, with FST50 
as the answer: 
[luceneutil#22](https://github.com/mikemccand/luceneutil/issues/22).
   > - [#16051](https://github.com/apache/lucene/issues/16051) + dev@ thread 
"TermInSetQuery storage": sandbox TermInSetQuery alternative; uschindler on 
implementation-named query classes.
   > - [#16780](https://github.com/apache/lucene/issues/16780) / 
[#16843](https://github.com/apache/lucene/issues/16843): binary fuse filters to 
replace the Bloom filter (overlaps only our optional filter).
   > - IDVersionPostingsFormat (sandbox): precedent for an ID-specific sandbox 
postings format.
   
   This is just an initial POC implementation with lots of experiments & 
results for various options. Curious to know what the community thinks of this 
general direction and if its worth putting more effort into it.


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