Skip to content

Commit

Permalink
Use perfect hash for temperature lookup
Browse files Browse the repository at this point in the history
  • Loading branch information
ianopolous committed Jan 31, 2024
1 parent f1fd7b7 commit c4ad74c
Showing 1 changed file with 31 additions and 13 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@
import java.lang.foreign.MemorySegment;
import java.nio.ByteOrder;
import java.nio.channels.*;
import java.nio.charset.StandardCharsets;
import java.nio.file.Files;
import java.nio.file.Path;
import java.nio.file.StandardOpenOption;
Expand All @@ -44,7 +45,7 @@ public class CalculateAverage_ianopolousfast {

public static final int MAX_LINE_LENGTH = 107;
public static final int MAX_STATIONS = 1 << 14;
private static final OfLong LONG_LAYOUT = JAVA_LONG_UNALIGNED.withOrder(ByteOrder.BIG_ENDIAN);
private static final OfLong LONG_LAYOUT_LE = JAVA_LONG_UNALIGNED.withOrder(ByteOrder.LITTLE_ENDIAN);
private static final VectorSpecies<Byte> BYTE_SPECIES = ByteVector.SPECIES_PREFERRED.length() >= 16
? ByteVector.SPECIES_128
: ByteVector.SPECIES_64;
Expand Down Expand Up @@ -131,18 +132,31 @@ public static short getMinus(long d) {
return ((d & 0xff00000000000000L) ^ 0x2d00000000000000L) != 0 ? 0 : (short) -1;
}

private static final long PERFECT_HASH_SEED = -1982870890352534081L;
static final short[] temperatureLookup = new short[5003];
static {
// use perfect hash from hundredwatt
for (int i = -999; i < 1000; i++) {
String s = Integer.toString(i);
String withDot = s.substring(0, s.length() - 1) + (Math.abs(i) < 10 ? "0" : "") + "." + s.charAt(s.length() - 1);

byte[] bytes = withDot.getBytes(StandardCharsets.UTF_8);
MemorySegment mem = MemorySegment.ofArray(Arrays.copyOfRange(bytes, 0, 8));
long d = mem.get(LONG_LAYOUT_LE, 0);
long hash = (d * PERFECT_HASH_SEED) & ~(1L << 63);
int index = (int) (hash % temperatureLookup.length);
if (temperatureLookup[index] != 0)
throw new IllegalStateException("Perfect hash is not perfect!");
temperatureLookup[index] = (short) i;
}
}

public static void processTemperature(long lineSplit, int size, MemorySegment buffer, Stat station) {
long d = buffer.get(LONG_LAYOUT, lineSplit);
// negative is either 0 or -1
short negative = getMinus(d);
d = d << (negative * -8);
int dotIndex = size - 2 + negative;
d = (d >> 8) | 0x30000000_00000000L; // add a leading 0 digit
d = d >> 8 * (5 - dotIndex);
short temperature = (short) ((byte) d - '0' +
10 * (((byte) (d >> 16)) - '0') +
100 * (((byte) (d >> 24)) - '0'));
temperature = (short) ((temperature ^ negative) - negative); // negative treatment inspired by merkitty
long d = buffer.get(LONG_LAYOUT_LE, lineSplit);
d = d & (-1L >>> 8 * (8 - size));
long hash = (d * PERFECT_HASH_SEED) & ~(1L << 63);
int temperatureIndex = (int) (hash % temperatureLookup.length);
short temperature = temperatureLookup[temperatureIndex];
station.add(temperature);
}

Expand Down Expand Up @@ -204,6 +218,11 @@ public static Stat[] parseStats(long start1, long end2, MemorySegment buffer) {
}
}

innerLoop(start1, end2, buffer, stations);
return stations;
}

private static void innerLoop(long start1, long end2, MemorySegment buffer, Stat[] stations) {
while (start1 < end2) {
int lineSize1 = lineSize(start1, buffer);
long start2 = start1 + lineSize1 + 1;
Expand All @@ -220,7 +239,6 @@ public static Stat[] parseStats(long start1, long end2, MemorySegment buffer) {
else
start1 += lineSize1 + 1;
}
return stations;
}

public static class Stat {
Expand Down

0 comments on commit c4ad74c

Please sign in to comment.