performance improvements
the heap refill code was recursively implemented with two methods. I merged both methods. replace recursion in heap refill method with iterative approach use array for list of LongQueue This way there is no precondition when accessing the elements
This commit is contained in:
@@ -7,7 +7,7 @@ buildscript {
|
||||
jcenter()
|
||||
}
|
||||
dependencies {
|
||||
// run with gradle --no-daemon clean jmh
|
||||
// run with Java 11 and ./gradlew --no-daemon clean jmh
|
||||
classpath "me.champeau.gradle:jmh-gradle-plugin:0.5.2"
|
||||
}
|
||||
}
|
||||
@@ -16,9 +16,13 @@ plugins {
|
||||
// usage: gradle dependencyUpdates -Drevision=release
|
||||
id "com.github.ben-manes.versions" version "0.34.0"
|
||||
}
|
||||
|
||||
apply plugin: 'java'
|
||||
apply plugin: 'eclipse'
|
||||
|
||||
// java compatibility version
|
||||
sourceCompatibility = 11
|
||||
|
||||
/*
|
||||
* The shared configuration for all sub-projects:
|
||||
*/
|
||||
@@ -78,7 +82,7 @@ subprojects {
|
||||
jmh {
|
||||
//jvmArgsAppend = ""
|
||||
resultFormat = "JSON"
|
||||
//include = ['.*Union.*'] // include pattern (regular expression) for benchmarks to be executed
|
||||
include = ['.*BenchmarkMultiwayMerge.*'] // include pattern (regular expression) for benchmarks to be executed
|
||||
//exclude = ['some regular expression'] // exclude pattern (regular expression) for benchmarks to be executed
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,85 @@
|
||||
package org.lucares.collections;
|
||||
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
import java.util.stream.LongStream;
|
||||
|
||||
import org.lucares.collections.MultiwayLongMerger.LongQueue;
|
||||
import org.openjdk.jmh.annotations.Benchmark;
|
||||
import org.openjdk.jmh.annotations.BenchmarkMode;
|
||||
import org.openjdk.jmh.annotations.Fork;
|
||||
import org.openjdk.jmh.annotations.Measurement;
|
||||
import org.openjdk.jmh.annotations.Mode;
|
||||
import org.openjdk.jmh.annotations.Param;
|
||||
import org.openjdk.jmh.annotations.Scope;
|
||||
import org.openjdk.jmh.annotations.Setup;
|
||||
import org.openjdk.jmh.annotations.State;
|
||||
import org.openjdk.jmh.annotations.TearDown;
|
||||
import org.openjdk.jmh.annotations.Warmup;
|
||||
|
||||
@State(Scope.Benchmark)
|
||||
@BenchmarkMode(Mode.Throughput)
|
||||
@Warmup(iterations = 10, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Measurement(iterations = 5, time = 500, timeUnit = TimeUnit.MILLISECONDS)
|
||||
@Fork(3)
|
||||
public class BenchmarkMultiwayMerge {
|
||||
|
||||
@Param({ "10000"/*, "20000" */})
|
||||
private int values;
|
||||
|
||||
@Param({/*"3","5",*/"10"})
|
||||
private int numLists;
|
||||
|
||||
|
||||
private List<LongList> longSorted = null;
|
||||
|
||||
@Setup
|
||||
public void setup() throws Exception {
|
||||
longSorted = new ArrayList<>();
|
||||
for(int i = 0; i < numLists; i++) {
|
||||
LongList list = LongList.of();
|
||||
LongStream.range(0, values).forEachOrdered(list::add);
|
||||
list.sort();
|
||||
longSorted.add(list);
|
||||
}
|
||||
}
|
||||
|
||||
@TearDown
|
||||
public void tearDown() {
|
||||
longSorted = null;
|
||||
}
|
||||
|
||||
@Benchmark
|
||||
public void testUnionSortedLists_MultiwayMerge() throws Exception {
|
||||
|
||||
LongList.union(longSorted);
|
||||
}
|
||||
|
||||
//@Benchmark
|
||||
public void testUnionSortedLists_TwowayMergeImplementation() throws Exception {
|
||||
|
||||
twowayMerge(longSorted);
|
||||
}
|
||||
|
||||
private void twowayMerge(List<LongList> longLists) {
|
||||
LongList result = longLists.get(0);
|
||||
for(int i =1; i < longLists.size(); i++) {
|
||||
result = LongList.union(result, longLists.get(i));
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) throws Exception {
|
||||
System.out.println("\n\n----------------\nstart");
|
||||
// -XX:+PrintCompilation
|
||||
for (int i = 0; i < 200; i++) {
|
||||
BenchmarkMultiwayMerge benchmark = new BenchmarkMultiwayMerge();
|
||||
benchmark.setup();
|
||||
//System.out.println("\n\n----------------\n"+i);
|
||||
for (int j = 0; j < 100000; j++) {
|
||||
benchmark.testUnionSortedLists_MultiwayMerge();
|
||||
}
|
||||
}
|
||||
System.out.println("done");
|
||||
}
|
||||
}
|
||||
@@ -8,9 +8,9 @@ import java.util.List;
|
||||
class MultiwayLongMerger {
|
||||
private static final long UNSET = Long.MAX_VALUE;
|
||||
|
||||
private static class LongQueue {
|
||||
static class LongQueue {
|
||||
private static final LongQueue EMPTY = new LongQueue(LongList.of());
|
||||
|
||||
|
||||
final LongList wrapped;
|
||||
|
||||
int offset = 0;
|
||||
@@ -109,7 +109,9 @@ class MultiwayLongMerger {
|
||||
|
||||
private static class MinValuePriorityQueue {
|
||||
|
||||
private List<LongQueue> longQueues;
|
||||
private static final LongQueue[] EMPTY_QUEUE = new LongQueue[0];
|
||||
|
||||
private LongQueue[] longQueues;
|
||||
|
||||
/*
|
||||
* a classic heap where the nodes are layed out in breath first order. First the
|
||||
@@ -122,7 +124,7 @@ class MultiwayLongMerger {
|
||||
private final int firstLeafIndex;
|
||||
|
||||
public MinValuePriorityQueue(final Collection<LongQueue> longQueues) {
|
||||
this.longQueues = new ArrayList<>(longQueues);
|
||||
final List<LongQueue> tmpQueues = new ArrayList<>(longQueues);
|
||||
size = longQueues.size();
|
||||
heap = new long[2 * nextPowOfTwo(size) - 1];
|
||||
|
||||
@@ -132,11 +134,12 @@ class MultiwayLongMerger {
|
||||
|
||||
// fill the longQueues list with empty queues, so that we
|
||||
// have a queue for every leaf in the heap. This makes fillWithMinOfChildren()
|
||||
// easier, because it removes special cases.
|
||||
for (int i = size ; i < nextPowOfTwo(size); i++) {
|
||||
this.longQueues.add(LongQueue.EMPTY);
|
||||
// easier, because it removes special cases.
|
||||
for (int i = size; i < nextPowOfTwo(size); i++) {
|
||||
tmpQueues.add(LongQueue.EMPTY);
|
||||
}
|
||||
|
||||
this.longQueues = tmpQueues.toArray(EMPTY_QUEUE);
|
||||
|
||||
init();
|
||||
}
|
||||
|
||||
@@ -180,7 +183,7 @@ class MultiwayLongMerger {
|
||||
// fill leaf nodes
|
||||
int offset = firstLeafIndex;
|
||||
for (int j = 0; j < size; j++) {
|
||||
final LongQueue q = longQueues.get(j);
|
||||
final LongQueue q = longQueues[j];
|
||||
heap[offset + j] = q.isEmpty() ? UNSET : q.pop();
|
||||
}
|
||||
|
||||
@@ -211,37 +214,40 @@ class MultiwayLongMerger {
|
||||
}
|
||||
|
||||
private void fillWithMinOfChildren(int index) {
|
||||
final int leftChildIndex = index * 2 + 1; // leftChildIndex(index);
|
||||
final int rightChildIndex = leftChildIndex + 1;// rightChildIndex(index);
|
||||
|
||||
final long valueOfLeftChild = heap[leftChildIndex];
|
||||
final long valueOfRightChild = heap[rightChildIndex];
|
||||
|
||||
final int chosenValue;
|
||||
|
||||
int firstLeafOffset = firstLeafIndex;
|
||||
|
||||
if (valueOfLeftChild < valueOfRightChild) {
|
||||
// left < right
|
||||
heap[index] = valueOfLeftChild;
|
||||
chosenValue = leftChildIndex;
|
||||
} else {
|
||||
// left >= right
|
||||
heap[index] = valueOfRightChild;
|
||||
chosenValue = rightChildIndex;
|
||||
int currentIndex = index;
|
||||
while (true) {
|
||||
final int leftChildIndex = currentIndex * 2 + 1; // leftChildIndex(index);
|
||||
final int rightChildIndex = leftChildIndex + 1;// rightChildIndex(index);
|
||||
|
||||
final long valueOfLeftChild = heap[leftChildIndex];
|
||||
final long valueOfRightChild = heap[rightChildIndex];
|
||||
|
||||
// final boolean chooseLeft = valueOfLeftChild < valueOfRightChild;
|
||||
// heap[currentIndex] = chooseLeft ? valueOfLeftChild : valueOfRightChild;
|
||||
// currentIndex = chooseLeft ? leftChildIndex : rightChildIndex;
|
||||
|
||||
if (valueOfLeftChild < valueOfRightChild) {
|
||||
// left < right
|
||||
heap[currentIndex] = valueOfLeftChild;
|
||||
currentIndex = leftChildIndex;
|
||||
} else {
|
||||
// left >= right
|
||||
heap[currentIndex] = valueOfRightChild;
|
||||
currentIndex = rightChildIndex;
|
||||
}
|
||||
|
||||
if (currentIndex >= firstLeafOffset) {
|
||||
final int listIndex = currentIndex - firstLeafIndex; // leafIndexToListIndex(index);
|
||||
final LongQueue queue = longQueues[listIndex];
|
||||
heap[currentIndex] = queue.isEmpty() ? UNSET : queue.pop();
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
refillValue(chosenValue);
|
||||
}
|
||||
|
||||
private void refillValue(int index) {
|
||||
if (isLeaf(index)) {
|
||||
final int listIndex = index - firstLeafIndex; // leafIndexToListIndex(index);
|
||||
final LongQueue queue = longQueues.get(listIndex);
|
||||
heap[index] = queue.isEmpty() ? UNSET : queue.pop();
|
||||
return;
|
||||
}
|
||||
fillWithMinOfChildren(index);
|
||||
}
|
||||
}
|
||||
|
||||
public static void main(String[] args) {
|
||||
|
||||
Reference in New Issue
Block a user