From 61a9bae878ce27941c99102e1da9e293216d0a84 Mon Sep 17 00:00:00 2001 From: Axionize <154778082+Axionize@users.noreply.github.com> Date: Thu, 5 Dec 2024 07:49:06 -0500 Subject: [PATCH] Working native code --- build.gradle.kts | 19 +- .../grimac/utils/vector/SIMDVector3D.java | 2 +- .../utils/vector/Java21SIMDVector3D.java | 148 ++++++++++ .../grimac/utils/vector/NativeVector3D.java | 261 ++++++++++++++++++ .../utils/vector/VectorOperationsJava21.java | 10 + .../vector/Java21SIMDVector3DBenchmark.java | 114 ++++++++ .../vector/NativeVector3DBenchmark.java | 114 ++++++++ .../vector/SIMDVector3DBenchmark.java | 114 ++++++++ .../vector/ScalarVector3DBenchmark.java | 114 ++++++++ 9 files changed, 887 insertions(+), 9 deletions(-) create mode 100644 src/java21/java/ac/grim/grimac/utils/vector/Java21SIMDVector3D.java create mode 100644 src/java21/java/ac/grim/grimac/utils/vector/NativeVector3D.java create mode 100644 src/java21/java/ac/grim/grimac/utils/vector/VectorOperationsJava21.java create mode 100644 src/jmh/java/ac/grim/grimac/benchmark/vector/Java21SIMDVector3DBenchmark.java create mode 100644 src/jmh/java/ac/grim/grimac/benchmark/vector/NativeVector3DBenchmark.java create mode 100644 src/jmh/java/ac/grim/grimac/benchmark/vector/SIMDVector3DBenchmark.java create mode 100644 src/jmh/java/ac/grim/grimac/benchmark/vector/ScalarVector3DBenchmark.java diff --git a/build.gradle.kts b/build.gradle.kts index b686a409d6..dec69dfaa0 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -202,16 +202,14 @@ java { } compileClasspath += sourceSets.main.get().output + sourceSets.getByName("java18").output + - sourceSets.getByName("java21").output + configurations["jmh"] runtimeClasspath += sourceSets.main.get().output + sourceSets.getByName("java18").output + - sourceSets.getByName("java21").output + configurations["jmh"] } } - sourceCompatibility = JavaVersion.VERSION_21 - targetCompatibility = JavaVersion.VERSION_21 + sourceCompatibility = JavaVersion.VERSION_17 + targetCompatibility = JavaVersion.VERSION_17 toolchain { languageVersion.set(JavaLanguageVersion.of(21)) @@ -225,7 +223,12 @@ tasks.withType { targetCompatibility = "18" } if (name == "compileJava21Java") { - options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector", "-Xlint:unchecked")) + options.compilerArgs.addAll(listOf("--enable-preview", "--add-modules", "jdk.incubator.vector", "-Xlint:unchecked")) + sourceCompatibility = "21" + targetCompatibility = "21" + } + if (name == "compileJmhJava") { + options.compilerArgs.addAll(listOf("--enable-preview", "--add-modules", "jdk.incubator.vector")) sourceCompatibility = "21" targetCompatibility = "21" } @@ -298,10 +301,10 @@ tasks.register("compileGeneratedJmh") { source = fileTree("${buildDir}/generated-sources/jmh") classpath = sourceSets["jmh"].compileClasspath + - sourceSets["java21"].output + - sourceSets["java21"].compileClasspath sourceSets["java18"].output + sourceSets["java18"].compileClasspath + + sourceSets["java21"].output + + sourceSets["java21"].compileClasspath + sourceSets.main.get().output + sourceSets.main.get().compileClasspath + files("${buildDir}/classes/java/jmh") @@ -321,7 +324,7 @@ tasks.register("jmh") { javaexec { classpath = files(tasks.named("jmhJar").get().outputs.files) mainClass.set("org.openjdk.jmh.Main") - jvmArgs = listOf("--add-modules", "jdk.incubator.vector") + jvmArgs = listOf("--enable-preview", "--add-modules", "jdk.incubator.vector") // Initialize the args list with default settings args = mutableListOf().apply { diff --git a/src/java18/java/ac/grim/grimac/utils/vector/SIMDVector3D.java b/src/java18/java/ac/grim/grimac/utils/vector/SIMDVector3D.java index 46e5a9f0c0..d5157fccbd 100644 --- a/src/java18/java/ac/grim/grimac/utils/vector/SIMDVector3D.java +++ b/src/java18/java/ac/grim/grimac/utils/vector/SIMDVector3D.java @@ -13,7 +13,7 @@ public final class SIMDVector3D implements Vector3D { private DoubleVector vector; public SIMDVector3D(double x, double y, double z) { - this.vector = DoubleVector.fromArray(SPECIES, new double[]{x, y, z, 0}, 0); + this.vector = DoubleVector.fromArray(SPECIES, new double[]{x, y, z, 0}, 0, LENGTH_3_ARRAY_MASK); } @Override diff --git a/src/java21/java/ac/grim/grimac/utils/vector/Java21SIMDVector3D.java b/src/java21/java/ac/grim/grimac/utils/vector/Java21SIMDVector3D.java new file mode 100644 index 0000000000..d6506f9f15 --- /dev/null +++ b/src/java21/java/ac/grim/grimac/utils/vector/Java21SIMDVector3D.java @@ -0,0 +1,148 @@ +package ac.grim.grimac.utils.vector; + +import jdk.incubator.vector.*; + +public final class Java21SIMDVector3D implements Vector3D { + + private static final VectorSpecies SPECIES = DoubleVector.SPECIES_256; + private static final VectorMask LENGTH_3_ARRAY_MASK = SPECIES.indexInRange(0, 3); + private static final VectorShuffle CROSS_PRODUCT_SHUFFLE1 = VectorShuffle.fromArray(SPECIES, new int[]{1, 2, 0, 3}, 0); + private static final VectorShuffle CROSS_PRODUCT_SHUFFLE2 = VectorShuffle.fromArray(SPECIES, new int[]{2, 0, 1, 3}, 0); + private static final DoubleVector FOURTH_COMPONENT_ZERO = DoubleVector.zero(SPECIES).withLane(3, 1.0); + + private DoubleVector vector; + + public Java21SIMDVector3D(double x, double y, double z) { + this.vector = DoubleVector.fromArray(SPECIES, new double[]{x, y, z, 0}, 0, LENGTH_3_ARRAY_MASK); + } + + @Override + public double getX() { + return this.vector.lane(0); + } + + @Override + public double getY() { + return this.vector.lane(1); + } + + @Override + public double getZ() { + return this.vector.lane(2); + } + + @Override + public Vector3D setX(double x) { + this.vector = this.vector.withLane(0, x); + return this; + } + + @Override + public Vector3D setY(double y) { + this.vector = this.vector.withLane(1, y); + return this; + } + + @Override + public Vector3D setZ(double z) { + this.vector = this.vector.withLane(2, z); + return this; + } + + @Override + public double length() { + return Math.sqrt(lengthSquared()); + } + + @Override + public double lengthSquared() { + return this.vector.mul(this.vector).reduceLanes(VectorOperators.ADD); + } + + @Override + public Vector3D multiply(double m) { + this.vector = this.vector.mul(m); + return this; + } + + @Override + public Vector3D normalize() { +// DoubleVector squared = this.vector.mul(this.vector); +// double sum = squared.reduceLanes(VectorOperators.ADD); +// double length = Math.sqrt(sum); +// this.vector = this.vector.div(length); +// return this; + this.vector = this.vector.div(length()); + return this; + } + + @Override + public Vector3D crossProduct(Vector3D other) { + Java21SIMDVector3D o = (Java21SIMDVector3D) other; + + DoubleVector tmp0 = this.vector.rearrange(CROSS_PRODUCT_SHUFFLE1); + DoubleVector tmp1 = o.vector.rearrange(CROSS_PRODUCT_SHUFFLE2); + + DoubleVector tmp2 = tmp0.mul(o.vector); + DoubleVector tmp3 = tmp0.mul(tmp1); + + DoubleVector tmp4 = tmp2.rearrange(CROSS_PRODUCT_SHUFFLE1); + + this.vector = tmp3.sub(tmp4).blend(FOURTH_COMPONENT_ZERO, LENGTH_3_ARRAY_MASK.not()); + return this; + } + + @Override + public Vector3D add(Vector3D o) { + this.vector = this.vector.add(((Java21SIMDVector3D) o).vector); + return this; + } + + @Override + public Vector3D subtract(Vector3D o) { + this.vector = this.vector.sub(((Java21SIMDVector3D) o).vector); + return this; + } + + @Override + public Vector3D multiply(Vector3D o) { + this.vector = this.vector.mul(((Java21SIMDVector3D) o).vector); + return this; + } + + @Override + public double distance(Vector3D o) { + return Math.sqrt(distanceSquared(o)); + } + + @Override + public double distanceSquared(Vector3D o) { + DoubleVector diff = this.vector.sub(((Java21SIMDVector3D) o).vector); + return diff.mul(diff).reduceLanes(VectorOperators.ADD); + } + + @Override + public double dot(Vector3D o) { + return this.vector.mul(((Java21SIMDVector3D) o).vector).reduceLanes(VectorOperators.ADD); + } + + + @Override + public Vector3D clone() { + // I think this makes a shallow clone if I do it like this? +// try { +// return (Vector3D) super.clone(); +// } catch (CloneNotSupportedException e) { +// throw new Error(e); +// } + try { + Java21SIMDVector3D cloned = (Java21SIMDVector3D) super.clone(); + // Create a new DoubleVector with the same values + cloned.vector = DoubleVector.fromArray(SPECIES, + new double[]{this.getX(), this.getY(), this.getZ(), 0}, 0); + return cloned; + } catch (CloneNotSupportedException e) { + throw new Error(e); + } + } +} diff --git a/src/java21/java/ac/grim/grimac/utils/vector/NativeVector3D.java b/src/java21/java/ac/grim/grimac/utils/vector/NativeVector3D.java new file mode 100644 index 0000000000..33268d253d --- /dev/null +++ b/src/java21/java/ac/grim/grimac/utils/vector/NativeVector3D.java @@ -0,0 +1,261 @@ +package ac.grim.grimac.utils.vector; + +import java.lang.foreign.*; +import java.lang.invoke.MethodHandle; +import java.lang.invoke.MethodHandles; +import java.lang.invoke.MethodType; + +public class NativeVector3D implements Vector3D { + + private static final Arena GLOBAL_ARENA = Arena.global(); + private static final MemoryLayout VECTOR_LAYOUT = MemoryLayout.sequenceLayout(3, ValueLayout.JAVA_DOUBLE); + private static final MethodHandle SIN; + private static final MethodHandle COS; + private static final MethodHandle SQRT; + private static final FunctionDescriptor LENGTH_SQUARED_DESCRIPTOR = FunctionDescriptor.of(ValueLayout.JAVA_DOUBLE, + ValueLayout.ADDRESS + ); + private static final MethodHandle LENGTH_SQUARED_MH; + private static final FunctionDescriptor NORMALIZE_DESCRIPTOR = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS + ); + private static final MethodHandle NORMALIZE_MH; + private static final FunctionDescriptor MULTIPLY_SCALAR_DESCRIPTOR = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS, + ValueLayout.JAVA_DOUBLE + ); + private static final MethodHandle MULTIPLY_SCALAR_MH; + private static final FunctionDescriptor CROSS_PRODUCT_DESCRIPTOR = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS, + ValueLayout.ADDRESS + ); + private static final MethodHandle CROSS_PRODUCT_MH; + private static final FunctionDescriptor ADD_DESCRIPTOR = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS, + ValueLayout.ADDRESS + ); + private static final MethodHandle ADD_MH; + private static final FunctionDescriptor SUBTRACT_DESCRIPTOR = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS, + ValueLayout.ADDRESS + ); + private static final MethodHandle SUBTRACT_MH; + private static final FunctionDescriptor MULTIPLY_VECTOR_DESCRIPTOR = FunctionDescriptor.ofVoid( + ValueLayout.ADDRESS, + ValueLayout.ADDRESS + ); + private static final MethodHandle MULTIPLY_VECTOR_MH; + private static final FunctionDescriptor DISTANCE_SQUARED_DESCRIPTOR = FunctionDescriptor.of(ValueLayout.JAVA_DOUBLE, + ValueLayout.ADDRESS, + ValueLayout.ADDRESS + ); + private static final MethodHandle DISTANCE_SQUARED_MH; + private static final FunctionDescriptor DOT_DESCRIPTOR = FunctionDescriptor.of(ValueLayout.JAVA_DOUBLE, + ValueLayout.ADDRESS, + ValueLayout.ADDRESS + ); + private static final MethodHandle DOT_MH; + + + static { + Linker linker = Linker.nativeLinker(); + SymbolLookup symbolLookup = SymbolLookup.loaderLookup(); // Or libraryLookup if needed + try { + SIN = MethodHandles.lookup().findStatic(Math.class, "sin", MethodType.methodType(double.class, double.class)); + COS = MethodHandles.lookup().findStatic(Math.class, "cos", MethodType.methodType(double.class, double.class)); + SQRT = MethodHandles.lookup().findStatic(Math.class, "sqrt", MethodType.methodType(double.class, double.class)); + // Load native library containing optimized vector operations + System.loadLibrary("vector_ops"); // Or use linker.downcall to load from a specific path + LENGTH_SQUARED_MH = linker.downcallHandle(symbolLookup.find("length_squared").orElseThrow(), LENGTH_SQUARED_DESCRIPTOR); + NORMALIZE_MH = linker.downcallHandle(symbolLookup.find("normalize").orElseThrow(), NORMALIZE_DESCRIPTOR); + MULTIPLY_SCALAR_MH = linker.downcallHandle(symbolLookup.find("multiply_scalar").orElseThrow(), MULTIPLY_SCALAR_DESCRIPTOR); + CROSS_PRODUCT_MH = linker.downcallHandle(symbolLookup.find("cross_product").orElseThrow(), CROSS_PRODUCT_DESCRIPTOR); + ADD_MH = linker.downcallHandle(symbolLookup.find("add").orElseThrow(), ADD_DESCRIPTOR); + SUBTRACT_MH = linker.downcallHandle(symbolLookup.find("subtract").orElseThrow(), SUBTRACT_DESCRIPTOR); + MULTIPLY_VECTOR_MH = linker.downcallHandle(symbolLookup.find("multiply_vector").orElseThrow(), MULTIPLY_VECTOR_DESCRIPTOR); + DISTANCE_SQUARED_MH = linker.downcallHandle(symbolLookup.find("distance_squared").orElseThrow(), DISTANCE_SQUARED_DESCRIPTOR); + DOT_MH = linker.downcallHandle(symbolLookup.find("dot").orElseThrow(), DOT_DESCRIPTOR); + } catch (NoSuchMethodException | IllegalAccessException e) { + throw new ExceptionInInitializerError(e); + } + } + + private final MemorySegment memorySegment; + + public NativeVector3D(double x, double y, double z) { + this.memorySegment = GLOBAL_ARENA.allocate(VECTOR_LAYOUT); + setX(x); + setY(y); + setZ(z); + } + + public NativeVector3D(MemorySegment memorySegment) { + this.memorySegment = memorySegment; + } + + @Override + public double getX() { + return memorySegment.getAtIndex(ValueLayout.JAVA_DOUBLE, 0); + } + + @Override + public double getY() { + return memorySegment.getAtIndex(ValueLayout.JAVA_DOUBLE, 1); + } + + @Override + public double getZ() { + return memorySegment.getAtIndex(ValueLayout.JAVA_DOUBLE, 2); + } + + @Override + public NativeVector3D setX(double x) { + memorySegment.setAtIndex(ValueLayout.JAVA_DOUBLE, 0, x); + return this; + } + + @Override + public NativeVector3D setY(double y) { + memorySegment.setAtIndex(ValueLayout.JAVA_DOUBLE, 1, y); + return this; + } + + @Override + public NativeVector3D setZ(double z) { + memorySegment.setAtIndex(ValueLayout.JAVA_DOUBLE, 2, z); + return this; + } + + @Override + public double length() { + try { + return (double) SQRT.invokeExact(lengthSquared()); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + @Override + public double lengthSquared() { + try { + return (double) LENGTH_SQUARED_MH.invokeExact(memorySegment); + } catch (Throwable e) { + throw new RuntimeException(e); + } + } + + @Override + public NativeVector3D multiply(double m) { + try { + MULTIPLY_SCALAR_MH.invokeExact(memorySegment, m); + } catch (Throwable e) { + throw new RuntimeException(e); + } + return this; + } + + @Override + public NativeVector3D normalize() { + try { + NORMALIZE_MH.invokeExact(memorySegment); + } catch (Throwable e) { + throw new RuntimeException(e); + } + return this; + } + + @Override + public NativeVector3D crossProduct(Vector3D o) { + NativeVector3D other = (NativeVector3D) o; + CROSS_PRODUCT_MH.invokeExact(memorySegment, other.memorySegment); // No try-catch + return this; + } + + @Override + public NativeVector3D add(Vector3D o) { + if (!(o instanceof NativeVector3D other)) { + throw new IllegalArgumentException("Provided vector must be a NativeVector3D"); + } + try { + ADD_MH.invokeExact(memorySegment, other.memorySegment); + } catch (Throwable e) { + throw new RuntimeException(e); + } + return this; + } + + @Override + public NativeVector3D subtract(Vector3D o) { + if (!(o instanceof NativeVector3D other)) { + throw new IllegalArgumentException("Provided vector must be a NativeVector3D"); + } + try { + SUBTRACT_MH.invokeExact(memorySegment, other.memorySegment); + } catch (Throwable e) { + throw new RuntimeException(e); + } + return this; + } + + @Override + public NativeVector3D multiply(Vector3D o) { + if (!(o instanceof NativeVector3D other)) { + throw new IllegalArgumentException("Provided vector must be a NativeVector3D"); + } + try { + int result = (int) MULTIPLY_VECTOR_MH.invokeExact(this.memorySegment, other.memorySegment); + if (result != 0) { + throw new RuntimeException("Error in native multiply_vector function. Error code: " + result); + } + } catch (Throwable e) { + throw new RuntimeException(e); + } + return this; + } + + @Override + public double distance(Vector3D o) { + if (!(o instanceof NativeVector3D)) { + throw new IllegalArgumentException("Incompatible Vector3D type"); + } + NativeVector3D other = (NativeVector3D) o; + return Math.sqrt(distanceSquared(other)); + } + + @Override + public double distanceSquared(Vector3D o) { + if (!(o instanceof NativeVector3D)) { + throw new IllegalArgumentException("Incompatible Vector3D type"); + } + NativeVector3D other = (NativeVector3D) o; + try { + return (double) DISTANCE_SQUARED_MH.invokeExact(this.memorySegment, other.memorySegment); + } catch (Throwable e) { + throw new RuntimeException("Error invoking native distanceSquared", e); + } + } + + @Override + public double dot(Vector3D o) { + if (!(o instanceof NativeVector3D)) { + throw new IllegalArgumentException("Incompatible Vector3D type"); + } + NativeVector3D other = (NativeVector3D) o; + try { + return (double) DOT_MH.invokeExact(this.memorySegment, other.memorySegment); + } catch (Throwable e) { + throw new RuntimeException("Error invoking native dot product", e); + } + } + + @Override + public Vector3D clone() { + try { + MemorySegment clonedSegment = GLOBAL_ARENA.allocate(VECTOR_LAYOUT); + MemorySegment.copy(this.memorySegment, 0, clonedSegment, 0, VECTOR_LAYOUT.byteSize()); + return new NativeVector3D(clonedSegment); + } catch (Exception e) { + throw new RuntimeException("Error cloning NativeVector3D", e); + } + } +} \ No newline at end of file diff --git a/src/java21/java/ac/grim/grimac/utils/vector/VectorOperationsJava21.java b/src/java21/java/ac/grim/grimac/utils/vector/VectorOperationsJava21.java new file mode 100644 index 0000000000..6f478f6319 --- /dev/null +++ b/src/java21/java/ac/grim/grimac/utils/vector/VectorOperationsJava21.java @@ -0,0 +1,10 @@ +package ac.grim.grimac.utils.vector; + + +public class VectorOperationsJava21 implements VectorOperations { + + @Override + public Vector3D newVector(double x, double y, double z) { + return new Java21SIMDVector3D(x, y, z); + } +} diff --git a/src/jmh/java/ac/grim/grimac/benchmark/vector/Java21SIMDVector3DBenchmark.java b/src/jmh/java/ac/grim/grimac/benchmark/vector/Java21SIMDVector3DBenchmark.java new file mode 100644 index 0000000000..6ffa935b56 --- /dev/null +++ b/src/jmh/java/ac/grim/grimac/benchmark/vector/Java21SIMDVector3DBenchmark.java @@ -0,0 +1,114 @@ +package ac.grim.grimac.benchmark.vector; + +import ac.grim.grimac.utils.vector.Java21SIMDVector3D; +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 2, time = 1) +@Measurement(iterations = 3, time = 1) +@Fork(1) +public class Java21SIMDVector3DBenchmark { + + private Java21SIMDVector3D vector1; + private Java21SIMDVector3D vector2; + + @Setup + public void setup() { + vector1 = new Java21SIMDVector3D(1.0, 2.0, 3.0); + vector2 = new Java21SIMDVector3D(4.0, 5.0, 6.0); + } + + @Benchmark + public double benchmarkGetX() { + return vector1.getX(); + } + + @Benchmark + public double benchmarkGetY() { + return vector1.getY(); + } + + @Benchmark + public double benchmarkGetZ() { + return vector1.getZ(); + } + + @Benchmark + public Java21SIMDVector3D benchmarkSetX() { + return (Java21SIMDVector3D) vector1.setX(10.0); + } + + @Benchmark + public Java21SIMDVector3D benchmarkSetY() { + return (Java21SIMDVector3D) vector1.setY(20.0); + } + + @Benchmark + public Java21SIMDVector3D benchmarkSetZ() { + return (Java21SIMDVector3D) vector1.setZ(30.0); + } + + @Benchmark + public double benchmarkLength() { + return vector1.length(); + } + + @Benchmark + public double benchmarkLengthSquared() { + return vector1.lengthSquared(); + } + + @Benchmark + public Java21SIMDVector3D benchmarkMultiplyScalar() { + return (Java21SIMDVector3D) vector1.multiply(2.0); + } + + @Benchmark + public Java21SIMDVector3D benchmarkNormalize() { + return (Java21SIMDVector3D) vector1.normalize(); + } + + @Benchmark + public Java21SIMDVector3D benchmarkCrossProduct() { + return (Java21SIMDVector3D) vector1.crossProduct(vector2); + } + + @Benchmark + public Java21SIMDVector3D benchmarkAdd() { + return (Java21SIMDVector3D) vector1.add(vector2); + } + + @Benchmark + public Java21SIMDVector3D benchmarkSubtract() { + return (Java21SIMDVector3D) vector1.subtract(vector2); + } + + @Benchmark + public Java21SIMDVector3D benchmarkMultiplyVector() { + return (Java21SIMDVector3D) vector1.multiply(vector2); + } + + @Benchmark + public double benchmarkDistance() { + return vector1.distance(vector2); + } + + @Benchmark + public double benchmarkDistanceSquared() { + return vector1.distanceSquared(vector2); + } + + @Benchmark + public double benchmarkDotProduct() { + return vector1.dot(vector2); + } + + @Benchmark + public Java21SIMDVector3D benchmarkClone() { + return (Java21SIMDVector3D) vector1.clone(); + } +} diff --git a/src/jmh/java/ac/grim/grimac/benchmark/vector/NativeVector3DBenchmark.java b/src/jmh/java/ac/grim/grimac/benchmark/vector/NativeVector3DBenchmark.java new file mode 100644 index 0000000000..cb1a698127 --- /dev/null +++ b/src/jmh/java/ac/grim/grimac/benchmark/vector/NativeVector3DBenchmark.java @@ -0,0 +1,114 @@ +package ac.grim.grimac.benchmark.vector; + +import ac.grim.grimac.utils.vector.NativeVector3D; +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 2, time = 1) +@Measurement(iterations = 3, time = 1) +@Fork(1) +public class NativeVector3DBenchmark { + + private NativeVector3D vector1; + private NativeVector3D vector2; + + @Setup + public void setup() { + vector1 = new NativeVector3D(1.0, 2.0, 3.0); + vector2 = new NativeVector3D(4.0, 5.0, 6.0); + } + + @Benchmark + public double benchmarkGetX() { + return vector1.getX(); + } + + @Benchmark + public double benchmarkGetY() { + return vector1.getY(); + } + + @Benchmark + public double benchmarkGetZ() { + return vector1.getZ(); + } + + @Benchmark + public NativeVector3D benchmarkSetX() { + return (NativeVector3D) vector1.setX(10.0); + } + + @Benchmark + public NativeVector3D benchmarkSetY() { + return (NativeVector3D) vector1.setY(20.0); + } + + @Benchmark + public NativeVector3D benchmarkSetZ() { + return (NativeVector3D) vector1.setZ(30.0); + } + + @Benchmark + public double benchmarkLength() { + return vector1.length(); + } + + @Benchmark + public double benchmarkLengthSquared() { + return vector1.lengthSquared(); + } + + @Benchmark + public NativeVector3D benchmarkMultiplyScalar() { + return (NativeVector3D) vector1.multiply(2.0); + } + + @Benchmark + public NativeVector3D benchmarkNormalize() { + return (NativeVector3D) vector1.normalize(); + } + + @Benchmark + public NativeVector3D benchmarkCrossProduct() { + return (NativeVector3D) vector1.crossProduct(vector2); + } + + @Benchmark + public NativeVector3D benchmarkAdd() { + return (NativeVector3D) vector1.add(vector2); + } + + @Benchmark + public NativeVector3D benchmarkSubtract() { + return (NativeVector3D) vector1.subtract(vector2); + } + + @Benchmark + public NativeVector3D benchmarkMultiplyVector() { + return (NativeVector3D) vector1.multiply(vector2); + } + + @Benchmark + public double benchmarkDistance() { + return vector1.distance(vector2); + } + + @Benchmark + public double benchmarkDistanceSquared() { + return vector1.distanceSquared(vector2); + } + + @Benchmark + public double benchmarkDotProduct() { + return vector1.dot(vector2); + } + + @Benchmark + public NativeVector3D benchmarkClone() { + return (NativeVector3D) vector1.clone(); + } +} diff --git a/src/jmh/java/ac/grim/grimac/benchmark/vector/SIMDVector3DBenchmark.java b/src/jmh/java/ac/grim/grimac/benchmark/vector/SIMDVector3DBenchmark.java new file mode 100644 index 0000000000..022cf2f0b4 --- /dev/null +++ b/src/jmh/java/ac/grim/grimac/benchmark/vector/SIMDVector3DBenchmark.java @@ -0,0 +1,114 @@ +package ac.grim.grimac.benchmark.vector; + +import ac.grim.grimac.utils.vector.SIMDVector3D; +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 2, time = 1) +@Measurement(iterations = 3, time = 1) +@Fork(1) +public class SIMDVector3DBenchmark { + + private SIMDVector3D vector1; + private SIMDVector3D vector2; + + @Setup + public void setup() { + vector1 = new SIMDVector3D(1.0, 2.0, 3.0); + vector2 = new SIMDVector3D(4.0, 5.0, 6.0); + } + + @Benchmark + public double benchmarkGetX() { + return vector1.getX(); + } + + @Benchmark + public double benchmarkGetY() { + return vector1.getY(); + } + + @Benchmark + public double benchmarkGetZ() { + return vector1.getZ(); + } + + @Benchmark + public SIMDVector3D benchmarkSetX() { + return (SIMDVector3D) vector1.setX(10.0); + } + + @Benchmark + public SIMDVector3D benchmarkSetY() { + return (SIMDVector3D) vector1.setY(20.0); + } + + @Benchmark + public SIMDVector3D benchmarkSetZ() { + return (SIMDVector3D) vector1.setZ(30.0); + } + + @Benchmark + public double benchmarkLength() { + return vector1.length(); + } + + @Benchmark + public double benchmarkLengthSquared() { + return vector1.lengthSquared(); + } + + @Benchmark + public SIMDVector3D benchmarkMultiplyScalar() { + return (SIMDVector3D) vector1.multiply(2.0); + } + + @Benchmark + public SIMDVector3D benchmarkNormalize() { + return (SIMDVector3D) vector1.normalize(); + } + + @Benchmark + public SIMDVector3D benchmarkCrossProduct() { + return (SIMDVector3D) vector1.crossProduct(vector2); + } + + @Benchmark + public SIMDVector3D benchmarkAdd() { + return (SIMDVector3D) vector1.add(vector2); + } + + @Benchmark + public SIMDVector3D benchmarkSubtract() { + return (SIMDVector3D) vector1.subtract(vector2); + } + + @Benchmark + public SIMDVector3D benchmarkMultiplyVector() { + return (SIMDVector3D) vector1.multiply(vector2); + } + + @Benchmark + public double benchmarkDistance() { + return vector1.distance(vector2); + } + + @Benchmark + public double benchmarkDistanceSquared() { + return vector1.distanceSquared(vector2); + } + + @Benchmark + public double benchmarkDotProduct() { + return vector1.dot(vector2); + } + + @Benchmark + public SIMDVector3D benchmarkClone() { + return (SIMDVector3D) vector1.clone(); + } +} diff --git a/src/jmh/java/ac/grim/grimac/benchmark/vector/ScalarVector3DBenchmark.java b/src/jmh/java/ac/grim/grimac/benchmark/vector/ScalarVector3DBenchmark.java new file mode 100644 index 0000000000..700b2fd704 --- /dev/null +++ b/src/jmh/java/ac/grim/grimac/benchmark/vector/ScalarVector3DBenchmark.java @@ -0,0 +1,114 @@ +package ac.grim.grimac.benchmark.vector; + +import ac.grim.grimac.utils.vector.ScalarVector3D; +import org.openjdk.jmh.annotations.*; + +import java.util.concurrent.TimeUnit; + +@BenchmarkMode(Mode.AverageTime) +@OutputTimeUnit(TimeUnit.NANOSECONDS) +@State(Scope.Thread) +@Warmup(iterations = 2, time = 1) +@Measurement(iterations = 3, time = 1) +@Fork(1) +public class ScalarVector3DBenchmark { + + private ScalarVector3D vector1; + private ScalarVector3D vector2; + + @Setup + public void setup() { + vector1 = new ScalarVector3D(1.0, 2.0, 3.0); + vector2 = new ScalarVector3D(4.0, 5.0, 6.0); + } + + @Benchmark + public double benchmarkGetX() { + return vector1.getX(); + } + + @Benchmark + public double benchmarkGetY() { + return vector1.getY(); + } + + @Benchmark + public double benchmarkGetZ() { + return vector1.getZ(); + } + + @Benchmark + public ScalarVector3D benchmarkSetX() { + return (ScalarVector3D) vector1.setX(10.0); + } + + @Benchmark + public ScalarVector3D benchmarkSetY() { + return (ScalarVector3D) vector1.setY(20.0); + } + + @Benchmark + public ScalarVector3D benchmarkSetZ() { + return (ScalarVector3D) vector1.setZ(30.0); + } + + @Benchmark + public double benchmarkLength() { + return vector1.length(); + } + + @Benchmark + public double benchmarkLengthSquared() { + return vector1.lengthSquared(); + } + + @Benchmark + public ScalarVector3D benchmarkMultiplyScalar() { + return (ScalarVector3D) vector1.multiply(2.0); + } + + @Benchmark + public ScalarVector3D benchmarkNormalize() { + return (ScalarVector3D) vector1.normalize(); + } + + @Benchmark + public ScalarVector3D benchmarkCrossProduct() { + return (ScalarVector3D) vector1.crossProduct(vector2); + } + + @Benchmark + public ScalarVector3D benchmarkAdd() { + return (ScalarVector3D) vector1.add(vector2); + } + + @Benchmark + public ScalarVector3D benchmarkSubtract() { + return (ScalarVector3D) vector1.subtract(vector2); + } + + @Benchmark + public ScalarVector3D benchmarkMultiplyVector() { + return (ScalarVector3D) vector1.multiply(vector2); + } + + @Benchmark + public double benchmarkDistance() { + return vector1.distance(vector2); + } + + @Benchmark + public double benchmarkDistanceSquared() { + return vector1.distanceSquared(vector2); + } + + @Benchmark + public double benchmarkDotProduct() { + return vector1.dot(vector2); + } + + @Benchmark + public ScalarVector3D benchmarkClone() { + return (ScalarVector3D) vector1.clone(); + } +}