3 ms·
Your version written in Java private static final int[] VALUES = range(0, 16).map(FizzBuzz::fizzbuzz).toArray(); private static final int[] DELTAS = stream
by _old_dude_ 6y ago
Your version written in Java
private static final int[] VALUES = range(0, 16).map(FizzBuzz::fizzbuzz).toArray();
private static final int[] DELTAS = stream(VALUES).map(v -> v < 0? 0: 15).toArray();
private static final VectorSpecies<Integer> SPECIES = IntVector.SPECIES_256;
private static int fizzbuzz(int i) {
return i % 15 == 0? FIZZ_BUZZ: i % 5 == 0? BUZZ: i % 3 == 0? FIZZ: i;
}
public static int[] simdFizzBuzz(int length) {
var result = new int[length];
var v1 = IntVector.fromArray(SPECIES, VALUES, 0);
var v2 = IntVector.fromArray(SPECIES, VALUES, 8);
var d1 = IntVector.fromArray(SPECIES, DELTAS, 0);
var d2 = IntVector.fromArray(SPECIES, DELTAS, 8);
var i = 0;
var upperBound = length - length % 15;
for(; i < upperBound; i += 15) {
v1.intoArray(result, i);
v2.intoArray(result, i + 8);
v1 = v1.add(d1);
v2 = v2.add(d2);
}
for(; i < length; i++) {
result[i] = fizzbuzz(i);
}
return result;
}
quite cool indeed
- gunnarmorling 6y agoYeah, that looks really neat, thanks for sharing that idea and even putting it together in Java here! I'll add it to the repo (https://github.com/gunnarmorling/simd-fizzbuzz https://github.com/gunnarmorling/simd-fizzbuzz) in a bit, and run the JMH benchmark for it, for the fun of it.
- psykotic 6y agoThere's a small bug in your code. The loop kernel writes out 16 entries despite only advancing by 15 entries, so your upper bound calculation needs adjustment. I'm also worried the JVM's compiler won't be able to eliminate the array bounds checks because of how you calculate the upper bound with the modulus. Writing it like this should fix both issues: // We need i + 15 < length to keep the last written entry in bounds. for (; i < length - 15; i += 15)
- _old_dude_ 6y agoyes, right, if length is a multiple of 15, there is an issue. And there is no primitive for masking on AVX2, those are only available on AVX-256. if we want to minimize the iterations of the post loop, it should be var upperBound = length % 15 == 0? length - 15: length - length % 15; but as you said, i'm not sure the VM will eliminate the bound checks in that case so your solution seems to be the best var upperBound = length - 15
- vardump 6y ago> And there is no primitive for masking on AVX2, those are only available on AVX-256. Sure there is a masking MOV in the original AVX (thus including AVX2), VMASKMOV. Works for both masked loads and stores. https://www.felixcloutier.com/x86/vmaskmov https://www.felixcloutier.com/x86/vmaskmov Notably: "Faults occur only due to mask-bit required memory accesses that caused the faults. Faults will not occur due to referencing any memory location if the corresponding mask bit for that memory location is 0. For example, no faults will be detected if the mask bits are all zero." A nitpick: there's no such thing as AVX-256, you probably meant AVX-512.
- _old_dude_ 6y agoThanks, i've overlooked that var mask = VectorMask.fromLong(SPECIES, 0b0111_1111); var i = 0; var upperBound = length - length % 15; for(; i < upperBound; i += 15) { v1.intoArray(result, i); v2.intoArray(result, i + 8, mask); ...
- jepcommenter 6y agoI guess you can also prefill result[i]=i and then apply fizzbuzz mask to avoid addition altogether
- jepcommenter 6y agoOh, wait, the original code receives input array while above loops from zero to n which is something else.