Skip to content

Optimization: Specialized memory-efficient layout for tiny ImmutableSet (n=2 to 4) #8207

@salvino080-coder

Description

@salvino080-coder

1. What are you trying to do?

Summary:

This contribution introduces SmallImmutableSet, a specialized implementation for sets containing 2, 3, or 4 elements. By replacing the standard hash table with a simple linear-scan array, we significantly reduce the memory footprint and improve CPU cache locality.

​Key Improvements:

​Memory Efficiency: Eliminates the int[] hash table array, saving ~32–48 bytes per instance. ​Performance: For n \le 4, linear search outperforms hashing by avoiding hashCode() computations and hash table probing. ​Reduced GC Pressure: Reduces the object count per set creation from two arrays to one. ​L1 Cache Locality: Small arrays stay entirely within a single CPU cache line, making contains() operations faster.

​Impact:

This is particularly beneficial for high-scale applications (e.g., dependency graphs, metadata mapping) where millions of small sets are held in memory.

Code:
package com.google.common.collect;

import com.google.common.annotations.GwtCompatible;
import java.util.Spliterator;
import java.util.Spliterators;
import javax.annotation.CheckForNull;

/**

  • Optimized implementation of {@code ImmutableSet} for 2 to 4 elements.
  • Uses linear scan to avoid hash table overhead, improving memory footprint
  • and L1 cache locality for small collections.
    */
    @GwtCompatible(serializable = true, emulated = true)
    final class SmallImmutableSet extends ImmutableSet {
    private final transient Object[] elements;

SmallImmutableSet(Object[] elements) {
this.elements = elements;
}

@OverRide
public int size() {
return elements.length;
}

@OverRide
public boolean contains(@checkfornull Object target) {
if (target == null) return false;
// Linear scan is faster than hashing for n <= 4
for (Object element : elements) {
if (element.equals(target)) {
return true;
}
}
return false;
}

@OverRide
public UnmodifiableIterator iterator() {
return (UnmodifiableIterator) Iterators.forArray(elements);
}

@OverRide
public Spliterator spliterator() {
return Spliterators.spliterator(elements, SPLITERATOR_CHARACTERISTICS);
}

@OverRide
boolean isPartialView() {
return false;
}

@OverRide
int copyIntoArray(Object[] dst, int offset) {
System.arraycopy(elements, 0, dst, offset, elements.length);
return offset + elements.length;
}

@OverRide
ImmutableList createAsList() {
return ImmutableList.asImmutableList(elements);
}
}

Integration point:
private static ImmutableSet construct(int n, Object... elements) {
switch (n) {
case 0:
return of();
case 1:
@SuppressWarnings("unchecked")
E elem = (E) elements[0];
return of(elem);
case 2:
case 3:
case 4:
// NEW: Memory and CPU optimization for tiny sets
return new SmallImmutableSet(elements);
default:
return RegularImmutableSet.create(n, elements);
}
}

2. What's the best code you can write to accomplish that without the new feature?

Possible i try to optimized this idea if is a good idea for the project

3. What would that same code look like if we added your feature?

If the specialized engeneer can do best for this code implement the engeneer contribution

(Optional) What would the method signatures for your feature look like?

Concrete Use Cases

Memory, calculated ecc

Packages

No response

Checklist

Metadata

Metadata

Assignees

No one assigned

    Type

    No type

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions