Further Equihash optimisation: avoid lambda+stream in tight loop

Manually iterate over colliding table rows using a while- loop and a
custom 'PrimitiveIterator.OfInt' implementation, instead of a foreach
lambda called on an IntStream, in 'Equihash::findCollisions'. Profiling
shows that this results in a slight speedup.
This commit is contained in:
Steven Barclay 2021-11-23 07:57:07 +00:00 committed by Christoph Atteneder
parent 3130d9329c
commit 0a603167f1
No known key found for this signature in database
GPG key ID: CD5DC1C529CDFD3B

View file

@ -38,8 +38,10 @@ import java.math.BigInteger;
import java.util.ArrayDeque;
import java.util.Arrays;
import java.util.Deque;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Optional;
import java.util.stream.IntStream;
import java.util.PrimitiveIterator;
import lombok.ToString;
@ -313,14 +315,31 @@ public class Equihash {
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)
);
PrimitiveIterator.OfInt get(int key) {
return new PrimitiveIterator.OfInt() {
int i;
Iterator<Integer> overspillIterator;
private Iterator<Integer> overspillIterator() {
if (overspillIterator == null) {
overspillIterator = overspillMultimap.get(i).iterator();
}
return overspillIterator;
}
@Override
public int nextInt() {
if (!hasNext()) {
throw new NoSuchElementException();
}
return i < 4 ? ~shortLists[key * 4 + i++] : overspillIterator().next();
}
@Override
public boolean hasNext() {
return i < 4 && shortLists[key * 4 + i] < 0 || i == 4 && overspillIterator().hasNext();
}
};
}
// assumes non-negative values only:
@ -347,18 +366,18 @@ public class Equihash {
for (int i = 0; i < table.numRows; i++) {
var row = table.getRow(i);
var collisionIndices = indexMultimap.get(row.get(0));
collisionIndices.forEach(ii -> {
var collidingRow = table.getRow(ii);
while (collisionIndices.hasNext()) {
var collidingRow = table.getRow(collisionIndices.nextInt());
if (isPartial) {
for (int j = 1; j < table.hashWidth; j++) {
newTableValues.add(collidingRow.get(j) ^ row.get(j));
}
} else if (!collidingRow.subArray(1, table.hashWidth).equals(row.subArray(1, table.hashWidth))) {
return;
continue;
}
newTableValues.addAll(collidingRow.subArray(table.hashWidth, collidingRow.length()));
newTableValues.addAll(row.subArray(table.hashWidth, row.length()));
});
}
indexMultimap.put(row.get(0), i);
}
return new XorTable(newHashWidth, newIndexTupleWidth, newTableValues.build());