mirror of
https://github.com/VolmitSoftware/Iris.git
synced 2026-10-11 19:14:11 +00:00
Drop unwired 2D noise kernels
- Delete NoiseKernels2D, ScalarNoiseKernels2D and VectorNoiseKernels2D after measuring the vector kernel at 0.07x scalar on 2-lane NEON with no production call site - Delete the 2D noise parity test and the SimdSupport plumbing that selected between the two kernel sets - State in the simd-bench README that its 2D noise classes are a candidate Iris does not ship
This commit is contained in:
@@ -1,8 +0,0 @@
|
||||
package art.arcane.iris.util.simd;
|
||||
|
||||
public interface NoiseKernels2D {
|
||||
String describe();
|
||||
|
||||
void simplexFractalFBM(long seed, int octaves, double frequency, double lacunarity, double gain,
|
||||
double fractalBounding, double[] xs, double[] zs, double[] out, int length);
|
||||
}
|
||||
@@ -1,99 +0,0 @@
|
||||
package art.arcane.iris.util.simd;
|
||||
|
||||
public final class ScalarNoiseKernels2D implements NoiseKernels2D {
|
||||
static final double F2 = 0.5D * (Math.sqrt(3.0D) - 1.0D);
|
||||
static final double G2 = (3.0D - Math.sqrt(3.0D)) / 6.0D;
|
||||
static final long X_PRIME = 1619L;
|
||||
static final long Y_PRIME = 31337L;
|
||||
static final double[] GRAD_2D = {-1D, -1D, 1D, -1D, -1D, 1D, 1D, 1D, 0D, -1D, -1D, 0D, 0D, 1D, 1D, 0D};
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return "scalar";
|
||||
}
|
||||
|
||||
static long fastFloor(double f) {
|
||||
return f >= 0D ? (long) f : (long) f - 1L;
|
||||
}
|
||||
|
||||
static double gradCoord2D(long seed, long x, long y, double xd, double yd) {
|
||||
long hash = seed;
|
||||
hash ^= X_PRIME * x;
|
||||
hash ^= Y_PRIME * y;
|
||||
hash = hash * hash * hash * 60493L;
|
||||
hash = (hash >> 13) ^ hash;
|
||||
int gradientIndex = ((int) hash & 7) << 1;
|
||||
return (xd * GRAD_2D[gradientIndex]) + (yd * GRAD_2D[gradientIndex + 1]);
|
||||
}
|
||||
|
||||
static double singleSimplex(long seed, double x, double y) {
|
||||
double t = (x + y) * F2;
|
||||
long i = fastFloor(x + t);
|
||||
long j = fastFloor(y + t);
|
||||
t = (i + j) * G2;
|
||||
double x0 = x - (i - t);
|
||||
double y0 = y - (j - t);
|
||||
long i1;
|
||||
long j1;
|
||||
if (x0 > y0) {
|
||||
i1 = 1L;
|
||||
j1 = 0L;
|
||||
} else {
|
||||
i1 = 0L;
|
||||
j1 = 1L;
|
||||
}
|
||||
double x1 = x0 - i1 + G2;
|
||||
double y1 = y0 - j1 + G2;
|
||||
double x2 = x0 - 1D + (2D * G2);
|
||||
double y2 = y0 - 1D + (2D * G2);
|
||||
double n0;
|
||||
double n1;
|
||||
double n2;
|
||||
double a = 0.5D - x0 * x0 - y0 * y0;
|
||||
if (a < 0D) {
|
||||
n0 = 0D;
|
||||
} else {
|
||||
a *= a;
|
||||
n0 = a * a * gradCoord2D(seed, i, j, x0, y0);
|
||||
}
|
||||
double b = 0.5D - x1 * x1 - y1 * y1;
|
||||
if (b < 0D) {
|
||||
n1 = 0D;
|
||||
} else {
|
||||
b *= b;
|
||||
n1 = b * b * gradCoord2D(seed, i + i1, j + j1, x1, y1);
|
||||
}
|
||||
double c = 0.5D - x2 * x2 - y2 * y2;
|
||||
if (c < 0D) {
|
||||
n2 = 0D;
|
||||
} else {
|
||||
c *= c;
|
||||
n2 = c * c * gradCoord2D(seed, i + 1L, j + 1L, x2, y2);
|
||||
}
|
||||
return 50D * (n0 + n1 + n2);
|
||||
}
|
||||
|
||||
static double simplexFractalFBMScalar(long seed, int octaves, double frequency, double lacunarity, double gain,
|
||||
double fractalBounding, double xIn, double yIn) {
|
||||
double x = xIn * frequency;
|
||||
double y = yIn * frequency;
|
||||
long s = seed;
|
||||
double sum = singleSimplex(s, x, y);
|
||||
double amp = 1D;
|
||||
for (int o = 1; o < octaves; o++) {
|
||||
x *= lacunarity;
|
||||
y *= lacunarity;
|
||||
amp *= gain;
|
||||
sum += singleSimplex(++s, x, y) * amp;
|
||||
}
|
||||
return sum * fractalBounding;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void simplexFractalFBM(long seed, int octaves, double frequency, double lacunarity, double gain,
|
||||
double fractalBounding, double[] xs, double[] zs, double[] out, int length) {
|
||||
for (int k = 0; k < length; k++) {
|
||||
out[k] = simplexFractalFBMScalar(seed, octaves, frequency, lacunarity, gain, fractalBounding, xs[k], zs[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -9,9 +9,7 @@ public final class SimdSupport {
|
||||
private static final String VECTOR_MODULE = "jdk.incubator.vector";
|
||||
private static final boolean MODULE_PRESENT = ModuleLayer.boot().findModule(VECTOR_MODULE).isPresent();
|
||||
private static final AtomicReference<Throwable> KERNEL_FAILURE = new AtomicReference<>();
|
||||
private static final AtomicReference<Throwable> NOISE_KERNEL_FAILURE = new AtomicReference<>();
|
||||
private static final SimdKernels KERNELS = selectKernels();
|
||||
private static final NoiseKernels2D NOISE_KERNELS_2D = selectNoiseKernels2D();
|
||||
|
||||
private SimdSupport() {
|
||||
}
|
||||
@@ -20,10 +18,6 @@ public final class SimdSupport {
|
||||
return KERNELS;
|
||||
}
|
||||
|
||||
public static NoiseKernels2D noiseKernels2D() {
|
||||
return NOISE_KERNELS_2D;
|
||||
}
|
||||
|
||||
public static boolean isVectorModulePresent() {
|
||||
return MODULE_PRESENT;
|
||||
}
|
||||
@@ -36,19 +30,10 @@ public final class SimdSupport {
|
||||
return KERNEL_FAILURE.get();
|
||||
}
|
||||
|
||||
public static Throwable noiseKernelInitializationFailure() {
|
||||
return NOISE_KERNEL_FAILURE.get();
|
||||
}
|
||||
|
||||
public static String kernelStatus() {
|
||||
return kernelStatus(isVectorized(), KERNELS.describe(), MODULE_PRESENT, KERNEL_FAILURE.get(), simdEnabledInSettings());
|
||||
}
|
||||
|
||||
public static String noiseKernelStatus() {
|
||||
return noiseKernelStatus(!(NOISE_KERNELS_2D instanceof ScalarNoiseKernels2D), NOISE_KERNELS_2D.describe(),
|
||||
MODULE_PRESENT, NOISE_KERNEL_FAILURE.get(), simdEnabledInSettings());
|
||||
}
|
||||
|
||||
static String kernelStatus(boolean vectorized, String description, boolean modulePresent, Throwable failure, boolean settingEnabled) {
|
||||
if (vectorized) {
|
||||
return "vector kernels enabled (" + description + ")";
|
||||
@@ -69,39 +54,13 @@ public final class SimdSupport {
|
||||
return "scalar kernels active; the Vector API reported no usable vector shape on this CPU";
|
||||
}
|
||||
|
||||
static String noiseKernelStatus(boolean vectorized, String description, boolean modulePresent, Throwable failure, boolean settingEnabled) {
|
||||
if (vectorized) {
|
||||
return "2D noise vector kernels enabled (" + description + ")";
|
||||
}
|
||||
|
||||
if (!modulePresent) {
|
||||
return "2D noise scalar kernels active; the " + VECTOR_MODULE + " module is not on this JVM";
|
||||
}
|
||||
|
||||
if (failure != null) {
|
||||
return "2D noise scalar kernels active; vector kernel initialization failed: " + describe(failure);
|
||||
}
|
||||
|
||||
if (!settingEnabled) {
|
||||
return "2D noise vector kernels disabled (performance.simdKernels=false)";
|
||||
}
|
||||
|
||||
return "2D noise scalar kernels active; this CPU has fewer than the 4 double lanes the vector kernel needs to pay for itself";
|
||||
}
|
||||
|
||||
public static void install() {
|
||||
IrisLogging.info("SIMD: " + kernelStatus());
|
||||
IrisLogging.info("SIMD: " + noiseKernelStatus());
|
||||
|
||||
Throwable failure = KERNEL_FAILURE.get();
|
||||
if (failure != null) {
|
||||
IrisLogging.reportError("Iris could not initialize its Vector API kernels; generation runs on the scalar kernels instead.", failure);
|
||||
}
|
||||
|
||||
Throwable noiseFailure = NOISE_KERNEL_FAILURE.get();
|
||||
if (noiseFailure != null) {
|
||||
IrisLogging.reportError("Iris could not initialize its Vector API 2D noise kernels; noise runs on the scalar kernels instead.", noiseFailure);
|
||||
}
|
||||
}
|
||||
|
||||
public static SimdKernels createVectorKernels() {
|
||||
@@ -117,24 +76,6 @@ public final class SimdSupport {
|
||||
}
|
||||
}
|
||||
|
||||
public static NoiseKernels2D createVectorNoiseKernels2D() {
|
||||
if (!MODULE_PRESENT) {
|
||||
return null;
|
||||
}
|
||||
|
||||
try {
|
||||
Class<?> cls = Class.forName("art.arcane.iris.util.simd.VectorNoiseKernels2D");
|
||||
boolean profitable = (boolean) cls.getMethod("profitable").invoke(null);
|
||||
if (!profitable) {
|
||||
return null;
|
||||
}
|
||||
return (NoiseKernels2D) cls.getDeclaredConstructor().newInstance();
|
||||
} catch (Throwable e) {
|
||||
NOISE_KERNEL_FAILURE.set(e);
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
||||
private static SimdKernels selectKernels() {
|
||||
if (!simdEnabledInSettings()) {
|
||||
return new ScalarSimdKernels();
|
||||
@@ -144,15 +85,6 @@ public final class SimdSupport {
|
||||
return vector == null ? new ScalarSimdKernels() : vector;
|
||||
}
|
||||
|
||||
private static NoiseKernels2D selectNoiseKernels2D() {
|
||||
if (!simdEnabledInSettings()) {
|
||||
return new ScalarNoiseKernels2D();
|
||||
}
|
||||
|
||||
NoiseKernels2D vector = createVectorNoiseKernels2D();
|
||||
return vector == null ? new ScalarNoiseKernels2D() : vector;
|
||||
}
|
||||
|
||||
private static String describe(Throwable failure) {
|
||||
Throwable root = failure;
|
||||
while (root.getCause() != null && root.getCause() != root) {
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
package art.arcane.iris.util.simd;
|
||||
|
||||
import jdk.incubator.vector.DoubleVector;
|
||||
import jdk.incubator.vector.LongVector;
|
||||
import jdk.incubator.vector.VectorMask;
|
||||
import jdk.incubator.vector.VectorOperators;
|
||||
import jdk.incubator.vector.VectorSpecies;
|
||||
|
||||
public final class VectorNoiseKernels2D implements NoiseKernels2D {
|
||||
private static final VectorSpecies<Double> DS = DoubleVector.SPECIES_PREFERRED;
|
||||
private static final VectorSpecies<Long> LS = LongVector.SPECIES_PREFERRED;
|
||||
private static final boolean ALIGNED = DS.length() == LS.length();
|
||||
private static final int MIN_PROFITABLE_LANES = 4;
|
||||
private static final boolean PROFITABLE = ALIGNED && DS.length() >= MIN_PROFITABLE_LANES;
|
||||
private static final double[] GRAD_X = {-1D, 1D, -1D, 1D, 0D, -1D, 0D, 1D};
|
||||
private static final double[] GRAD_Y = {-1D, -1D, 1D, 1D, -1D, 0D, 1D, 0D};
|
||||
private static final double F2 = ScalarNoiseKernels2D.F2;
|
||||
private static final double G2 = ScalarNoiseKernels2D.G2;
|
||||
private static final long X_PRIME = ScalarNoiseKernels2D.X_PRIME;
|
||||
private static final long Y_PRIME = ScalarNoiseKernels2D.Y_PRIME;
|
||||
private static final ThreadLocal<long[]> LONG_SCRATCH = ThreadLocal.withInitial(() -> new long[LS.length()]);
|
||||
|
||||
public static boolean lanesAligned() {
|
||||
return ALIGNED;
|
||||
}
|
||||
|
||||
public static boolean profitable() {
|
||||
return PROFITABLE;
|
||||
}
|
||||
|
||||
@Override
|
||||
public String describe() {
|
||||
return DS.length() + "x64 lanes, " + DS.vectorShape();
|
||||
}
|
||||
|
||||
private static LongVector floorToLong(DoubleVector f) {
|
||||
LongVector truncated = (LongVector) f.convertShape(VectorOperators.D2L, LS, 0);
|
||||
VectorMask<Long> negative = f.compare(VectorOperators.LT, 0D).cast(LS);
|
||||
return truncated.sub(1L, negative);
|
||||
}
|
||||
|
||||
private static DoubleVector toDouble(LongVector v) {
|
||||
return (DoubleVector) v.convertShape(VectorOperators.L2D, DS, 0);
|
||||
}
|
||||
|
||||
private static DoubleVector gradCoord(long seed, LongVector i, LongVector j, DoubleVector xd, DoubleVector yd,
|
||||
int[] idxScratch) {
|
||||
LongVector hash = LongVector.broadcast(LS, seed)
|
||||
.lanewise(VectorOperators.XOR, i.mul(X_PRIME))
|
||||
.lanewise(VectorOperators.XOR, j.mul(Y_PRIME));
|
||||
hash = hash.mul(hash).mul(hash).mul(60493L);
|
||||
LongVector shifted = hash.lanewise(VectorOperators.ASHR, 13);
|
||||
hash = shifted.lanewise(VectorOperators.XOR, hash);
|
||||
LongVector idx = hash.lanewise(VectorOperators.AND, 7L);
|
||||
long[] tmp = LONG_SCRATCH.get();
|
||||
idx.intoArray(tmp, 0);
|
||||
for (int l = 0; l < idxScratch.length; l++) {
|
||||
idxScratch[l] = (int) tmp[l];
|
||||
}
|
||||
DoubleVector gx = DoubleVector.fromArray(DS, GRAD_X, 0, idxScratch, 0);
|
||||
DoubleVector gy = DoubleVector.fromArray(DS, GRAD_Y, 0, idxScratch, 0);
|
||||
return xd.mul(gx).add(yd.mul(gy));
|
||||
}
|
||||
|
||||
private static DoubleVector corner(DoubleVector xk, DoubleVector yk, DoubleVector grad) {
|
||||
DoubleVector t = DoubleVector.broadcast(DS, 0.5D).sub(xk.mul(xk)).sub(yk.mul(yk));
|
||||
VectorMask<Double> negative = t.compare(VectorOperators.LT, 0D);
|
||||
DoubleVector t2 = t.mul(t);
|
||||
DoubleVector t4 = t2.mul(t2);
|
||||
return t4.mul(grad).blend(0D, negative);
|
||||
}
|
||||
|
||||
private static DoubleVector singleSimplexVector(long seed, DoubleVector x, DoubleVector y, int[] idxScratch) {
|
||||
DoubleVector t = x.add(y).mul(F2);
|
||||
LongVector i = floorToLong(x.add(t));
|
||||
LongVector j = floorToLong(y.add(t));
|
||||
DoubleVector skew = toDouble(i.add(j)).mul(G2);
|
||||
DoubleVector x0 = x.sub(toDouble(i).sub(skew));
|
||||
DoubleVector y0 = y.sub(toDouble(j).sub(skew));
|
||||
VectorMask<Double> xGreater = x0.compare(VectorOperators.GT, y0);
|
||||
VectorMask<Long> xGreaterL = xGreater.cast(LS);
|
||||
LongVector i1 = LongVector.zero(LS).blend(1L, xGreaterL);
|
||||
LongVector j1 = LongVector.broadcast(LS, 1L).blend(0L, xGreaterL);
|
||||
DoubleVector x1 = x0.sub(toDouble(i1)).add(G2);
|
||||
DoubleVector y1 = y0.sub(toDouble(j1)).add(G2);
|
||||
DoubleVector x2 = x0.sub(1D).add(2D * G2);
|
||||
DoubleVector y2 = y0.sub(1D).add(2D * G2);
|
||||
DoubleVector n0 = corner(x0, y0, gradCoord(seed, i, j, x0, y0, idxScratch));
|
||||
DoubleVector n1 = corner(x1, y1, gradCoord(seed, i.add(i1), j.add(j1), x1, y1, idxScratch));
|
||||
DoubleVector n2 = corner(x2, y2, gradCoord(seed, i.add(1L), j.add(1L), x2, y2, idxScratch));
|
||||
return n0.add(n1).add(n2).mul(50D);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void simplexFractalFBM(long seed, int octaves, double frequency, double lacunarity, double gain,
|
||||
double fractalBounding, double[] xs, double[] zs, double[] out, int length) {
|
||||
if (!ALIGNED) {
|
||||
tailScalar(seed, octaves, frequency, lacunarity, gain, fractalBounding, xs, zs, out, 0, length);
|
||||
return;
|
||||
}
|
||||
int lanes = DS.length();
|
||||
int bound = DS.loopBound(length);
|
||||
int[] idxScratch = new int[lanes];
|
||||
int k = 0;
|
||||
for (; k < bound; k += lanes) {
|
||||
DoubleVector x = DoubleVector.fromArray(DS, xs, k).mul(frequency);
|
||||
DoubleVector y = DoubleVector.fromArray(DS, zs, k).mul(frequency);
|
||||
long s = seed;
|
||||
DoubleVector sum = singleSimplexVector(s, x, y, idxScratch);
|
||||
double amp = 1D;
|
||||
for (int o = 1; o < octaves; o++) {
|
||||
x = x.mul(lacunarity);
|
||||
y = y.mul(lacunarity);
|
||||
amp *= gain;
|
||||
sum = sum.add(singleSimplexVector(++s, x, y, idxScratch).mul(amp));
|
||||
}
|
||||
sum.mul(fractalBounding).intoArray(out, k);
|
||||
}
|
||||
tailScalar(seed, octaves, frequency, lacunarity, gain, fractalBounding, xs, zs, out, k, length);
|
||||
}
|
||||
|
||||
private static void tailScalar(long seed, int octaves, double frequency, double lacunarity, double gain,
|
||||
double fractalBounding, double[] xs, double[] zs, double[] out, int from, int length) {
|
||||
for (int k = from; k < length; k++) {
|
||||
out[k] = ScalarNoiseKernels2D.simplexFractalFBMScalar(seed, octaves, frequency, lacunarity, gain,
|
||||
fractalBounding, xs[k], zs[k]);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,120 +0,0 @@
|
||||
package art.arcane.iris.util.simd;
|
||||
|
||||
import art.arcane.iris.util.project.noise.FastNoiseDouble;
|
||||
import art.arcane.iris.util.project.noise.FastNoiseDouble.FractalType;
|
||||
import art.arcane.iris.util.project.noise.FastNoiseDouble.NoiseType;
|
||||
import art.arcane.volmlib.util.math.RNG;
|
||||
import org.junit.Test;
|
||||
|
||||
import static org.junit.Assert.assertEquals;
|
||||
|
||||
public class NoiseKernels2DParityTest {
|
||||
private static FastNoiseDouble oracle(long seed, int octaves) {
|
||||
FastNoiseDouble n = new FastNoiseDouble(seed);
|
||||
n.setNoiseType(NoiseType.SimplexFractal);
|
||||
n.setFractalType(FractalType.FBM);
|
||||
n.setFractalOctaves(octaves);
|
||||
return n;
|
||||
}
|
||||
|
||||
private static void assertKernelMatchesOracle(NoiseKernels2D kernel) {
|
||||
RNG rng = new RNG(99L);
|
||||
for (int octaves = 1; octaves <= 8; octaves++) {
|
||||
FastNoiseDouble n = oracle(1337L + octaves, octaves);
|
||||
int length = 256;
|
||||
double[] xs = new double[length];
|
||||
double[] zs = new double[length];
|
||||
double[] expected = new double[length];
|
||||
double[] out = new double[length];
|
||||
for (int k = 0; k < length; k++) {
|
||||
double x = (rng.nextDouble() - 0.5D) * 1_000_000D;
|
||||
double z = (rng.nextDouble() - 0.5D) * 1_000_000D;
|
||||
xs[k] = x;
|
||||
zs[k] = z;
|
||||
expected[k] = n.GetSimplexFractal(x, z);
|
||||
}
|
||||
kernel.simplexFractalFBM(1337L + octaves, octaves, n.getFrequency(), 2.0D, 0.5D, n.getFractalBounding(), xs, zs, out, length);
|
||||
for (int k = 0; k < length; k++) {
|
||||
assertEquals("octaves=" + octaves + " idx=" + k, expected[k], out[k], 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void scalarKernelIsBitExactToOracle() {
|
||||
assertKernelMatchesOracle(new ScalarNoiseKernels2D());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorKernelSingleOctaveIsBitExactToOracle() {
|
||||
org.junit.Assume.assumeTrue(VectorNoiseKernels2D.lanesAligned());
|
||||
FastNoiseDouble n = oracle(202020L, 1);
|
||||
RNG rng = new RNG(7L);
|
||||
int length = 333;
|
||||
double[] xs = new double[length];
|
||||
double[] zs = new double[length];
|
||||
double[] expected = new double[length];
|
||||
double[] out = new double[length];
|
||||
for (int k = 0; k < length; k++) {
|
||||
double x = (rng.nextDouble() - 0.5D) * 800_000D;
|
||||
double z = (rng.nextDouble() - 0.5D) * 800_000D;
|
||||
xs[k] = x;
|
||||
zs[k] = z;
|
||||
expected[k] = n.GetSimplexFractal(x, z);
|
||||
}
|
||||
new VectorNoiseKernels2D().simplexFractalFBM(202020L, 1, n.getFrequency(), 2.0D, 0.5D, n.getFractalBounding(), xs, zs, out, length);
|
||||
for (int k = 0; k < length; k++) {
|
||||
assertEquals("idx=" + k, expected[k], out[k], 0D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorKernelAllOctavesAreBitExactToOracle() {
|
||||
org.junit.Assume.assumeTrue(VectorNoiseKernels2D.lanesAligned());
|
||||
assertKernelMatchesOracle(new VectorNoiseKernels2D());
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simdSupportReturnsBitExactNoiseKernel() {
|
||||
NoiseKernels2D kernel = SimdSupport.noiseKernels2D();
|
||||
org.junit.Assert.assertNotNull(kernel);
|
||||
assertKernelMatchesOracle(kernel);
|
||||
}
|
||||
|
||||
@Test
|
||||
public void simdSupportNeverSelectsVectorBelowProfitableLanes() {
|
||||
if (VectorNoiseKernels2D.profitable()) {
|
||||
org.junit.Assert.assertTrue(SimdSupport.createVectorNoiseKernels2D() instanceof VectorNoiseKernels2D);
|
||||
} else {
|
||||
org.junit.Assert.assertTrue(SimdSupport.noiseKernels2D() instanceof ScalarNoiseKernels2D);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void vectorMatchesScalarAcrossEdgeCases() {
|
||||
org.junit.Assume.assumeTrue(VectorNoiseKernels2D.lanesAligned());
|
||||
NoiseKernels2D scalar = new ScalarNoiseKernels2D();
|
||||
NoiseKernels2D vector = new VectorNoiseKernels2D();
|
||||
RNG rng = new RNG(31337L);
|
||||
int[] lengths = {1, 3, 7, 8, 9, 255, 256, 257};
|
||||
double[] edgeCoords = {-4.0D, -3.0D, -1.0D, -0.0D, 0.0D, 1.0D, 4.0D, 1_000_000.0D, -1_000_000.0D};
|
||||
for (int octaves = 1; octaves <= 8; octaves++) {
|
||||
for (int length : lengths) {
|
||||
double[] xs = new double[length];
|
||||
double[] zs = new double[length];
|
||||
double[] a = new double[length];
|
||||
double[] b = new double[length];
|
||||
for (int k = 0; k < length; k++) {
|
||||
boolean edge = (k % 3) == 0;
|
||||
xs[k] = edge ? edgeCoords[k % edgeCoords.length] : (rng.nextDouble() - 0.5D) * 2_000_000D;
|
||||
zs[k] = edge ? edgeCoords[(k + 1) % edgeCoords.length] : (rng.nextDouble() - 0.5D) * 2_000_000D;
|
||||
}
|
||||
scalar.simplexFractalFBM(77L, octaves, 0.01D, 2.0D, 0.5D, 0.6667D, xs, zs, a, length);
|
||||
vector.simplexFractalFBM(77L, octaves, 0.01D, 2.0D, 0.5D, 0.6667D, xs, zs, b, length);
|
||||
for (int k = 0; k < length; k++) {
|
||||
assertEquals("oct=" + octaves + " len=" + length + " idx=" + k, a[k], b[k], 0D);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -88,20 +88,19 @@ public class SimdSupportStatusTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
public void aNarrowCpuIsReportedAsUnprofitableRatherThanDisabled() {
|
||||
String status = SimdSupport.noiseKernelStatus(false, "scalar", true, null, true);
|
||||
public void aCpuWithoutAUsableVectorShapeIsReportedAsSuchRatherThanDisabled() {
|
||||
String status = SimdSupport.kernelStatus(false, "scalar", true, null, true);
|
||||
|
||||
assertTrue(status, status.contains("4 double lanes"));
|
||||
assertTrue(status, status.contains("no usable vector shape"));
|
||||
assertFalse(status, status.contains("performance.simdKernels=false"));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void installStatesTheLiveKernelSelectionForBothKernelSets() {
|
||||
public void installStatesTheLiveKernelSelection() {
|
||||
SimdSupport.install();
|
||||
|
||||
assertTrue(emitted.toString(), emitted.size() >= 2);
|
||||
assertTrue(emitted.toString(), emitted.size() >= 1);
|
||||
assertTrue(emitted.toString(), emitted.contains("SIMD: " + SimdSupport.kernelStatus()));
|
||||
assertTrue(emitted.toString(), emitted.contains("SIMD: " + SimdSupport.noiseKernelStatus()));
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -111,6 +110,5 @@ public class SimdSupportStatusTest {
|
||||
}
|
||||
|
||||
assertFalse(SimdSupport.kernelStatus(), SimdSupport.kernelStatus().contains("performance.simdKernels=false"));
|
||||
assertFalse(SimdSupport.noiseKernelStatus(), SimdSupport.noiseKernelStatus().contains("performance.simdKernels=false"));
|
||||
}
|
||||
}
|
||||
|
||||
@@ -6,9 +6,13 @@ on**. The Volmit dev/test machines are Apple Silicon, where the wide-SIMD path
|
||||
(4+ double lanes) does not exist. Copy the built artifact to a Windows/x86 box
|
||||
(AVX2 = 4 lanes, AVX-512 = 8 lanes) and run it to get real numbers for that CPU.
|
||||
|
||||
The six kernel classes are copied verbatim (logic byte-for-byte) from
|
||||
`Iris/core/.../util/simd/`. This tool is intentionally a duplicate so it builds
|
||||
and runs on its own with no Gradle, no VolmLib, and no Iris on the classpath.
|
||||
The three array kernel classes are copied verbatim (logic byte-for-byte) from
|
||||
`Iris/core/.../util/simd/`. The three 2D noise kernel classes are a candidate
|
||||
implementation that Iris does not ship: it measured 0.07x against scalar on
|
||||
2-lane NEON and was removed, and this harness keeps it so the same question can be
|
||||
answered on a wider CPU before anyone rebuilds it. This tool is intentionally a
|
||||
duplicate so it builds and runs on its own with no Gradle, no VolmLib, and no Iris
|
||||
on the classpath.
|
||||
|
||||
## Requirements
|
||||
|
||||
@@ -98,7 +102,8 @@ run.bat --mode both (default)
|
||||
throughput will be (cache behavior, allocation, and surrounding code differ in
|
||||
the real engine).
|
||||
- **Effort 1 array kernels** (`roundToInt` / `sum` / `max`) run unconditionally
|
||||
in real Iris. **Effort 2 noise** (`simplexFractalFBM`) is currently unwired in
|
||||
Iris and gated to 4+ double lanes; this tool force-measures it regardless.
|
||||
in real Iris. **Effort 2 noise** (`simplexFractalFBM`) is not part of
|
||||
Iris; this tool force-measures the candidate kernel regardless of the 4+ double
|
||||
lane gate.
|
||||
- Vector-API auto-vectorization and cost depend heavily on the JDK version and
|
||||
CPU. Run on the actual target hardware; do not extrapolate across machines.
|
||||
|
||||
Reference in New Issue
Block a user