Good question! With a bit of careful size selection (to make sure each one gets 
the right capacity / load), and fixing load factor difference, I’ve got a fun 
table.
The data is obtained on M5 Max MBP, lower number is better (ns/op).

Key takeaways,

1. This is performance measurement, not measuring memory. HashSet is the best 
in terms of memory usage. OrderedHashSet is second, and ListHashSet is the 
worst.
2. Insert is showing very interesting result. The fastest container for 
insertion is actually OrderedHashSet! There is legit reason: OrderedHashSet is 
maintaining index-hash-table and entries are separate contiguous buffer. So 
when rehashing, we need to reconstruct the integer hashtable part, but entries 
buffers are literally just you can copy (& compact by removing tombstones). 
Still, integer key cases, UncheckedHashSet is the fastest because both are 
rehashing integer hashtable. But if the key is string for example, 
OrderedHashTable gets better since hashtable rehashing is only happening for 
index buffer. Furthermore, this is related to (4), iteration of entries are 
dramatically faster in OrderedHashSet. So rehashing becomes in general faster 
than normal HashSet since we need to iterate entries and reinsert them. But 
OrderedHashSet only needs to scan the entries in very packed manner, 
significantly better cache miss rate.
3. ContainsHit is showing the something we just expected. HashSet / 
UncheckedHashSet is faster than the others and ListHashSet is the slowest. 
OrderedHashSet / ListHashSet requires load-dependent double-dereference so this 
is strictly slower than HashSet / UnchecheckedHashSet.
4. Iterate shows interesting results too. Because of packed entries in 
OrderedHashSet, iteration in this is really faster than even normal HashSet. 
HashSet is having many empty slots, while OrderedHashSet entries are packed.

Overall, given that most of HashTable are look-up heavy typically, the good 
guidance is use HashSet if possible, consider using OrderedHashSet if insertion 
ordering is necessary. You can use ListHashSet only when you have very 
particular algorithm that you need to add / remove entries during iteration :)


=== Insert (ns/op) ===
container         key              7        63       511        3K       31K    
  255K
----------------  --------  --------  --------  --------  --------  --------  
--------
HashSet           uint32_t      4.12      3.99      4.00      4.09      8.61    
 13.24
UncheckedHashSet  uint32_t      3.80      3.36      3.77      3.78      7.77    
 12.80
OrderedHashSet    uint32_t      4.02      3.63      3.31      2.62      4.65    
  8.16
ListHashSet       uint32_t     11.19     11.49     12.52     15.08     24.41    
 31.28
HashSet           void*         4.20      3.74      4.35      4.53     10.05    
 14.04
UncheckedHashSet  void*         3.77      3.43      4.13      4.42      8.72    
 13.63
OrderedHashSet    void*         4.59      3.51      3.13      2.65      4.33    
  8.23
ListHashSet       void*        11.48     11.61     12.44     14.60     24.17    
 31.09
HashSet           String        7.11      7.37      8.95      9.03     17.58    
 23.67
UncheckedHashSet  String        6.84      7.10      8.53      9.14     17.65    
 23.56
OrderedHashSet    String        7.73      7.45      8.54      7.51     11.43    
 14.55
ListHashSet       String       11.89     11.44     13.35     16.59     27.06    
 38.42

=== ContainsHit (ns/op) ===
container         key              7        63       511        3K       31K    
  255K
----------------  --------  --------  --------  --------  --------  --------  
--------
HashSet           uint32_t      0.64      0.76      0.80      0.99      1.40    
  3.67
UncheckedHashSet  uint32_t      0.43      0.57      0.56      0.79      1.14    
  3.41
OrderedHashSet    uint32_t      0.56      0.74      0.79      0.97      1.95    
  4.87
ListHashSet       uint32_t      0.66      0.73      0.76      0.98      1.67    
  5.43
HashSet           void*         0.75      0.69      0.77      0.98      1.53    
  3.96
UncheckedHashSet  void*         0.45      0.49      0.54      0.78      0.85    
  3.21
OrderedHashSet    void*         0.63      0.65      0.75      0.92      2.21    
  5.01
ListHashSet       void*         0.68      0.64      0.73      0.96      1.80    
  5.31
HashSet           String        2.43      3.17      3.96      4.96      6.80    
 10.57
UncheckedHashSet  String        2.68      3.05      3.99      4.77      6.89    
  9.86
OrderedHashSet    String        2.48      3.08      4.04      5.04      7.84    
 11.35
ListHashSet       String        2.55      3.22      4.40      5.96      9.01    
 14.13

=== Iterate (ns/op) ===
container         key              7        63       511        3K       31K    
  255K
----------------  --------  --------  --------  --------  --------  --------  
--------
HashSet           uint32_t      0.72      0.62      0.93      1.20      2.68    
  4.66
UncheckedHashSet  uint32_t      0.74      0.61      0.91      1.19      3.15    
  4.69
OrderedHashSet    uint32_t      0.41      0.37      0.37      0.36      0.36    
  0.36
ListHashSet       uint32_t      0.25      0.41      0.73      0.85      0.85    
  0.85
HashSet           void*         0.66      0.57      0.94      1.22      3.40    
  4.61
UncheckedHashSet  void*         0.70      0.56      1.00      1.21      3.13    
  4.71
OrderedHashSet    void*         0.40      0.33      0.32      0.25      0.29    
  0.29
ListHashSet       void*         0.26      0.30      0.72      0.85      0.81    
  0.83
HashSet           String        0.94      0.81      1.17      1.45      3.56    
  5.30
UncheckedHashSet  String        1.00      0.82      1.17      1.44      3.27    
  5.01
OrderedHashSet    String        0.67      0.53      0.51      0.54      0.48    
  0.51
ListHashSet       String        0.46      0.63      0.71      0.95      0.85    
  0.91

=== Churn (ns/op) ===
container         key              7        63       511        3K       31K    
  255K
----------------  --------  --------  --------  --------  --------  --------  
--------
HashSet           uint32_t      1.48      2.47      2.77      3.24      6.73    
 10.65
UncheckedHashSet  uint32_t      1.43      1.98      1.98      2.92      5.30    
 10.60
OrderedHashSet    uint32_t      2.29      2.57      2.64      2.68      3.91    
  9.35
ListHashSet       uint32_t      4.26      5.56      5.44      7.80     12.59    
 20.49
HashSet           void*         1.52      1.77      2.49      3.86      6.62    
 11.73
UncheckedHashSet  void*         1.49      1.74      2.33      3.32      5.15    
 11.42
OrderedHashSet    void*         2.27      2.27      2.40      2.44      3.41    
  9.28
ListHashSet       void*         4.48      4.55      5.21      7.88     13.85    
 20.76
HashSet           String        4.16      4.28      6.35      7.29     12.77    
 20.31
UncheckedHashSet  String        4.01      4.08      6.23      6.95     12.79    
 19.57
OrderedHashSet    String        3.97      4.05      5.20      5.71      9.53    
 15.05
ListHashSet       String        6.35      6.26      8.43     11.43     17.53    
 29.89

Total benchmark wall time: 28.957827 s


Best regards,
-Yusuke Suzuki

> On May 19, 2026, at 12:55 AM, Jean-Yves Avenard <[email protected]> 
> wrote:
> 
> 
> 
>> On 14 May 2026, at 9:36 am, Yusuke Suzuki via webkit-dev 
>> <[email protected]> wrote:
>> 
>> Hello WebKittens!
>> 
>> Many DOM (& some JS, like, ES6 modules code) data structures require HashSet 
>> / HashMap while also requiring “insertion ordering* iteration.
>> Previously ListHashSet was the canonical solution for that, but this was 
>> really costly in both memory and performance because it is internally a 
>> doubly-linked list with HashTable.
>> 
>> I’ve introduced WTF OrderedHashSet / OrderedHashMap, which preserves 
>> insertion ordering while maintain better memory footprint and performance 
>> efficiency.
>> HashSet / HashMap are the fastest and the most memory efficient, but 
>> OrderedHashSet / OrderedHashMap is offering insertion ordering iteration 
>> (and you can also rbegin / rend) while it does not add significant cost.
>> 
>> Internally, the algorithm is pretty similar to JSC’s PropertyTable (as 
>> JSObject also needs to preserve insertion ordering and Hash table).
>> Let’s summarize,
> 
> Have we done benchmarks to compare our HashMaps to std::map ? 
> 
> Jean-Yves

_______________________________________________
webkit-dev mailing list -- [email protected]
To unsubscribe send an email to [email protected]

Reply via email to