[llvm] [DenseMap] Store occupancy in a packed used-bit array (PR #201281)

Alexis Engelke via llvm-commits llvm-commits at lists.llvm.org
Wed Jun 3 01:49:53 PDT 2026


https://github.com/aengelke commented:

I think the code changes look largely good.

- Pro: This removes the need for getEmptyKey()
- Pro: iteration and find if missed is faster
- Con: insertion and lookup (hit) executes more instructions for small key-value pairs
- Con: rather large code size increase, which (I expect) substantially contributes to the max-rss increase
- Con: it makes the operations that should, ideally be less used (iteration) cheaper at the cost of (ideally) more frequently used operations (insert, find-hit)

I measured sqlite -O0g (+0.42% on c-t-t) on a Zen 4 AMD machine (hyperfine interleaved with perf stat -r10; built with clang 23 from Monday):
```
Benchmark 1: ./clang-main ...
  Time (mean ± σ):     768.0 ms ±   2.7 ms    [User: 709.2 ms, System: 58.4 ms]
  Range (min … max):   763.7 ms … 772.3 ms    10 runs
       766,881,875      task-clock                       #    0.992 CPUs utilized               ( +-  0.12% )
                 6      context-switches                 #    7.824 /sec                        ( +- 11.39% )
                 0      cpu-migrations                   #    0.000 /sec                      
            17,363      page-faults                      #   22.641 K/sec                       ( +-  0.02% )
     3,400,146,015      instructions                     #    1.61  insn per cycle            
                                                  #    0.24  stalled cycles per insn     ( +-  0.02% )
     2,108,737,113      cycles                           #    2.750 GHz                         ( +-  0.12% )
       828,287,269      stalled-cycles-frontend          #   39.28% frontend cycles idle        ( +-  0.24% )
       640,473,796      branches                         #  835.166 M/sec                       ( +-  0.02% )
        19,282,271      branch-misses                    #    3.01% of all branches             ( +-  0.23% )

          0.773348 +- 0.000894 seconds time elapsed  ( +-  0.12% )

Benchmark 2: ./clang-dmub ...
  Time (mean ± σ):     766.2 ms ±   1.5 ms    [User: 705.2 ms, System: 60.7 ms]
  Range (min … max):   764.2 ms … 768.5 ms    10 runs
       762,892,904      task-clock                       #    0.991 CPUs utilized               ( +-  0.10% )
                 8      context-switches                 #   10.486 /sec                        ( +-  7.47% )
                 0      cpu-migrations                   #    0.000 /sec                      
            17,519      page-faults                      #   22.964 K/sec                       ( +-  0.03% )
     3,415,991,033      instructions                     #    1.63  insn per cycle            
                                                  #    0.24  stalled cycles per insn     ( +-  0.02% )
     2,097,698,394      cycles                           #    2.750 GHz                         ( +-  0.10% )
       817,560,241      stalled-cycles-frontend          #   38.97% frontend cycles idle        ( +-  0.15% )
       632,956,100      branches                         #  829.679 M/sec                       ( +-  0.02% )
        18,645,270      branch-misses                    #    2.95% of all branches             ( +-  0.19% )

          0.769438 +- 0.000747 seconds time elapsed  ( +-  0.10% )
```

I also compiled sqlite -O3g (not run on c-t-t) with similar results:
```
  Time (mean ± σ):      9.465 s ±  0.019 s    [User: 9.318 s, System: 0.144 s]
  Range (min … max):    9.438 s …  9.503 s    10 runs
    39,121,269,386      instructions                     #    1.51  insn per cycle            
                                                  #    0.24  stalled cycles per insn     ( +-  0.00% )
    25,938,149,301      cycles                           #    2.750 GHz                         ( +-  0.03% )
     7,834,529,243      branches                         #  830.568 M/sec                       ( +-  0.00% )
       321,152,958      branch-misses                    #    4.10% of all branches             ( +-  0.04% )
  Time (mean ± σ):      9.343 s ±  0.011 s    [User: 9.198 s, System: 0.141 s]
  Range (min … max):    9.326 s …  9.359 s    10 runs
    39,178,375,169      instructions                     #    1.53  insn per cycle            
                                                  #    0.24  stalled cycles per insn     ( +-  0.01% )
    25,665,077,467      cycles                           #    2.750 GHz                         ( +-  0.09% )
     7,657,498,371      branches                         #  820.440 M/sec                       ( +-  0.01% )
       311,128,182      branch-misses                    #    4.06% of all branches             ( +-  0.05% )
```

So at least on this machine, the number of instructions is misleading and this is actually faster. This only leaves the file-size ~= max-rss regression (and therefore also more page faults etc.). Given that we currently don't really care about this (I think we should do more here, but whatever), I don't think this is too important.

I'm mildly opposed to a DensePtrMap. This provides only slight (if any) advantages, but makes it more difficult to choose. I think, if beneficial, DenseMap should automatically use some heuristic to determine this instead of placing the burden of choice on our devs, which might not even be aware of the subtle performance differences.

https://github.com/llvm/llvm-project/pull/201281


More information about the llvm-commits mailing list