Skip to content

ImmutableBiMap.copyOf quadratic running time behavior #3015

@MrVPlusOne

Description

@MrVPlusOne

Hi,

I found an input pattern that can trigger Ω(N^2) running time behavior of ImmutableBiMap.copyOf, the input is constructed and tested using the following code:

import org.apache.commons.lang3.tuple.ImmutablePair;
import com.google.common.collect.ImmutableBiMap;

import java.util.ArrayList;
import java.util.Random;


public class GuavaImmutableBiMap {

    public static void main(String[] args) {
        ArrayList<ImmutablePair<Integer, Integer>> pattern = generateInput(600);
        ArrayList<ImmutablePair<Integer, Integer>> randomInput = randomInput(600);

        int testNum = 4000;
        long start;
        double timeUse;

        start = System.nanoTime();
        for(int i = 0; i < testNum; i++) {
            ImmutableBiMap.copyOf(randomInput);
        }
        timeUse = (System.nanoTime() - start)/1e9;
        System.out.println("Random input time use: " + timeUse + "s.");

        start = System.nanoTime();
        for(int i = 0; i < testNum; i++) {
            ImmutableBiMap.copyOf(pattern);
        }
        timeUse = (System.nanoTime() - start)/1e9;
        System.out.println("Pattern time use: " + timeUse + "s.");
    }

    public static ArrayList<ImmutablePair<Integer, Integer>> generateInput(int size){
        int s1 = 475;
        ImmutablePair<Integer, Integer> s2 = new ImmutablePair<>(271,212);
        ArrayList<ImmutablePair<Integer, Integer>> s4 = new ArrayList<>();

        s4.add(s2);

        for (int i = 0; i < size; i ++){
            s1 = ((99*302*486) | 475) + 142 + (s1-1);
            s2 = new ImmutablePair<>(s2.right, s1 << 353);
            s4.add(s2);
        }

        return s4;
    }

    public static ArrayList<ImmutablePair<Integer, Integer>> randomInput(int size){
        Random rand = new Random(1);

        ArrayList<ImmutablePair<Integer, Integer>> list = new ArrayList<>();
        for (int i = 0; i < size; i ++){
            list.add(new ImmutablePair<>(rand.nextInt(), rand.nextInt()));
        }

        return list;
    }
}

On my machine, when running with -Xint, I got the following result:

Random input time use: 4.183300757s.
Pattern time use: 243.795791197s.

It's about 60X slowdown.

When running without -Xint and increase testNum from 4000 to 40000 (to reduce noise), the result was:

Random input time use: 0.816171511s.
Pattern time use: 38.315509883s.

I also tried to fit the performance-inputSize relationship, the result is a quadratic curve:
fit-bimap

Note that this input pattern I found only have this quadratic behavior when size < 600, but this is due to a current technical limitation of our fuzzing tool used to find this input pattern, and we suspect there exist patterns that can scale to larger sizes.

Metadata

Metadata

Assignees

Type

No type

Projects

No projects

Milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions