Add Equihash.IntListMultimap (private) class for speedup

Provide a (vastly cut down) drop-in replacement for the Guava multimap
instance 'indexMultimap', of type 'ListMultimap<Integer, Integer>', used
to map table row indices to block values, to detect collisions at a
given block position (that is, in a given table column).

The replacement stores (multi-)mappings from ints to ints in a flat int-
array, only spilling over to a ListMultimap if there are more than 4
values added for a given key. This vastly reduces the amount of boxing
and memory usage when running 'Equihash::findCollisions' to build up the
next table as part of Wagner's algorithm.
This commit is contained in:
Steven Barclay 2021-11-23 07:34:52 +00:00 committed by Christoph Atteneder
parent 95637e0fa0
commit 3130d9329c
No known key found for this signature in database
GPG Key ID: CD5DC1C529CDFD3B

View File

@ -39,6 +39,7 @@ import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.Optional;
import java.util.stream.IntStream;
import lombok.ToString;
@ -303,6 +304,37 @@ public class Equihash {
}
}
private static class IntListMultimap {
final int[] shortLists;
final ListMultimap<Integer, Integer> overspillMultimap;
IntListMultimap(int keyUpperBound) {
shortLists = new int[keyUpperBound * 4];
overspillMultimap = MultimapBuilder.hashKeys().arrayListValues().build();
}
IntStream get(int key) {
if (shortLists[key * 4 + 3] == 0) {
return IntStream.range(0, 4).map(i -> ~shortLists[key * 4 + i]).takeWhile(i -> i >= 0);
}
return IntStream.concat(
IntStream.range(0, 4).map(i -> ~shortLists[key * 4 + i]),
overspillMultimap.get(key).stream().mapToInt(i -> i)
);
}
// assumes non-negative values only:
void put(int key, int value) {
for (int i = 0; i < 4; i++) {
if (shortLists[key * 4 + i] == 0) {
shortLists[key * 4 + i] = ~value;
return;
}
}
overspillMultimap.put(key, value);
}
}
// Apply a single iteration of Wagner's Algorithm.
private XorTable findCollisions(XorTable table, boolean isPartial) {
int newHashWidth = isPartial ? table.hashWidth - 1 : 0;
@ -311,7 +343,7 @@ public class Equihash {
var newTableValues = ImmutableIntArray.builder(
newRowWidth * (isPartial ? tableCapacity : 10));
ListMultimap<Integer, Integer> indexMultimap = MultimapBuilder.hashKeys().arrayListValues().build();
var indexMultimap = new IntListMultimap(N / 2);
for (int i = 0; i < table.numRows; i++) {
var row = table.getRow(i);
var collisionIndices = indexMultimap.get(row.get(0));