Last active
August 11, 2022 14:10
-
-
Save svanoort/4256d1801d97a2b7eef3 to your computer and use it in GitHub Desktop.
Java Microbenchmarks
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
package hudson.cli; | |
import org.junit.Test; | |
import java.util.*; | |
// Micro benchmark different array ops in Java | |
// Originally started from http://stackoverflow.com/questions/322715/when-to-use-linkedlist-over-arraylist/7507740#7507740 | |
// Now completely rewritten to correctly warm up the JIT compilation and take an average over many runs | |
// Tweaked/corrected version from http://stackoverflow.com/questions/322715/when-to-use-linkedlist-over-arraylist/7507740#7507740 | |
public class CollectionTest { | |
private static final int MAX_ELEMENTS = 5; | |
private static final int WARMUP_RUNS = 10000; | |
private static final int BENCHMARK_RUNS = 100000; | |
String[] strings = maxArray(); | |
// Does a JIT warmup run and then a benchmark averaged over many runs | |
abstract class Benchmarkable { | |
List<String> stringList = Arrays.asList(strings); | |
List<String> testList; | |
abstract String getName(); | |
abstract void setup(); | |
abstract void runMethod(); | |
public void doBenchMark() { | |
int warmupRuns = WARMUP_RUNS; | |
int benchmarkRuns = BENCHMARK_RUNS; | |
for(int i=0; i<warmupRuns; i++){ | |
setup(); | |
runMethod(); | |
} | |
// Timing loop | |
long totalTime = 0; | |
for(int i=0; i<benchmarkRuns; i++) { | |
setup(); | |
long startTime = System.nanoTime(); | |
runMethod(); | |
long endTime = System.nanoTime(); | |
totalTime += (endTime-startTime); | |
} | |
System.out.println("Benchmark \""+getName()+"\" took "+totalTime/benchmarkRuns+" ns/run"); | |
} | |
} | |
////////////// ADD ALL //////////////////////////////////////// | |
@Test | |
public void listAddAll() { | |
Benchmarkable arrayListAddBenchmark = new Benchmarkable() { | |
@Override | |
public String getName() { return "Array List addAll()"; } | |
@Override | |
void setup() { testList = new ArrayList<>(); } | |
@Override | |
void runMethod() { testList.addAll(stringList); } | |
}; | |
arrayListAddBenchmark.doBenchMark(); | |
Benchmarkable linkedListAddBenchmark = new Benchmarkable() { | |
@Override | |
public String getName() { return "Linked List addAll()"; } | |
@Override | |
void setup() { testList = new LinkedList<>(); } | |
@Override | |
void runMethod() { testList.addAll(stringList); } | |
}; | |
linkedListAddBenchmark.doBenchMark(); | |
} | |
@Test | |
public void listAppend() { | |
Benchmarkable arrayListAddBenchmark = new Benchmarkable() { | |
@Override | |
public String getName() { return "Array List add() from scratch"; } | |
@Override | |
void setup() { testList = new ArrayList<>(); } | |
@Override | |
void runMethod() { | |
List<String> myList = testList; | |
for (String string : strings) | |
testList.add(string); | |
} | |
}; | |
arrayListAddBenchmark.doBenchMark(); | |
Benchmarkable linkedListAddBenchmark = new Benchmarkable() { | |
@Override | |
public String getName() { return "Linked List add() from scratch"; } | |
@Override | |
void setup() { testList = new LinkedList<>(); } | |
@Override | |
void runMethod() { | |
List<String> myList = testList; | |
for (String string : strings) | |
testList.add(string); | |
} | |
}; | |
linkedListAddBenchmark.doBenchMark(); | |
} | |
@Test | |
public void testAppendLarge() { | |
Benchmarkable arrayListBenchmark = new Benchmarkable() { | |
String insertString0 = getString(true, MAX_ELEMENTS / 2 + 10); | |
String insertString1 = getString(true, MAX_ELEMENTS / 2 + 20); | |
String insertString2 = getString(true, MAX_ELEMENTS / 2 + 30); | |
String insertString3 = getString(true, MAX_ELEMENTS / 2 + 40); | |
@Override | |
public String getName() { return "Array List add() on end"; } | |
@Override | |
void setup() { | |
testList = new ArrayList<String>(); | |
// This is the most common way to add elements | |
// Important because if an addAll operation increases | |
// The backing array by more than 50% over existing to hold new elements | |
// Then the backing array is sized *exactly* to hold all the elements. | |
// Which means the next append operation would force a resize | |
// This causes a large performance hit, since it would normally be amortized | |
for (String s: stringList) { | |
testList.add(s); | |
} | |
} | |
@Override | |
void runMethod() { | |
testList.add(insertString0); | |
testList.add(insertString1); | |
testList.add(insertString2); | |
testList.add(insertString3); | |
} | |
}; | |
arrayListBenchmark.doBenchMark(); | |
Benchmarkable linkedListBenchmark = new Benchmarkable() { | |
String insertString0 = getString(true, MAX_ELEMENTS / 2 + 10); | |
String insertString1 = getString(true, MAX_ELEMENTS / 2 + 20); | |
String insertString2 = getString(true, MAX_ELEMENTS / 2 + 30); | |
String insertString3 = getString(true, MAX_ELEMENTS / 2 + 40); | |
@Override | |
public String getName() { return "Linked List add() on end"; } | |
@Override | |
void setup() { | |
testList = new LinkedList<String>(); | |
// To replicate the arraylist setup | |
for (String s: stringList) { | |
testList.add(s); | |
} | |
} | |
@Override | |
void runMethod() { | |
testList.add(insertString0); | |
testList.add(insertString1); | |
testList.add(insertString2); | |
testList.add(insertString3); | |
} | |
}; | |
linkedListBenchmark.doBenchMark(); | |
} | |
@Test | |
public void searchAndRemove() throws Exception { | |
Benchmarkable arrayListBenchmark = new Benchmarkable() { | |
String searchString0 = getString(true, MAX_ELEMENTS / 2 + 10); | |
String searchString1 = getString(true, MAX_ELEMENTS / 2 + 20); | |
@Override | |
public String getName() { return "Array List search and remove"; } | |
@Override | |
void setup() { | |
testList = new ArrayList<String>(MAX_ELEMENTS); | |
testList.addAll(stringList); | |
} | |
@Override | |
void runMethod() { | |
List<String> myList = testList; | |
myList.remove(searchString0); | |
myList.remove(searchString1); | |
} | |
}; | |
arrayListBenchmark.doBenchMark(); | |
Benchmarkable linkedListBenchmark = new Benchmarkable() { | |
String searchString0 = getString(true, MAX_ELEMENTS / 2 + 10); | |
String searchString1 = getString(true, MAX_ELEMENTS / 2 + 20); | |
@Override | |
public String getName() { return "Linked List search and remove"; } | |
@Override | |
void setup() { | |
testList = new LinkedList<String>(); | |
testList.addAll(stringList); | |
} | |
@Override | |
void runMethod() { | |
List<String> myList = testList; | |
myList.remove(searchString0); | |
myList.remove(searchString1); | |
} | |
}; | |
linkedListBenchmark.doBenchMark(); | |
} | |
@Test | |
public void search() throws Exception { | |
Benchmarkable arrayListBenchmark = new Benchmarkable() { | |
String searchString0 = getString(true, MAX_ELEMENTS / 2 + 10); | |
String searchString1 = getString(true, MAX_ELEMENTS / 2 + 20); | |
@Override | |
public String getName() { return "Array List search"; } | |
@Override | |
void setup() { | |
testList = new ArrayList<String>(MAX_ELEMENTS); | |
testList.addAll(stringList); | |
} | |
@Override | |
void runMethod() { | |
List<String> myList = testList; | |
myList.contains(searchString0); | |
myList.contains(searchString1); | |
} | |
}; | |
arrayListBenchmark.doBenchMark(); | |
Benchmarkable linkedListBenchmark = new Benchmarkable() { | |
String searchString0 = getString(true, MAX_ELEMENTS / 2 + 10); | |
String searchString1 = getString(true, MAX_ELEMENTS / 2 + 20); | |
@Override | |
public String getName() { return "Linked List search"; } | |
@Override | |
void setup() { | |
testList = new LinkedList<String>(); | |
testList.addAll(stringList); | |
} | |
@Override | |
void runMethod() { | |
List<String> myList = testList; | |
myList.contains(searchString0); | |
myList.contains(searchString1); | |
} | |
}; | |
linkedListBenchmark.doBenchMark(); | |
} | |
private String[] maxArray() { | |
String[] strings = new String[MAX_ELEMENTS]; | |
Boolean result = Boolean.TRUE; | |
for (int i = 0; i < MAX_ELEMENTS; i++) { | |
strings[i] = getString(result, i); | |
result = !result; | |
} | |
return strings; | |
} | |
private String getString(Boolean result, int i) { | |
return String.valueOf(result) + i + String.valueOf(!result); | |
} | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
I think you meant 10,000, correct @svanoort?
If so, here is the graph