Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Speeding up Grim - Adding SIMD support #1841

Draft
wants to merge 22 commits into
base: 2.0
Choose a base branch
from
Draft
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
158 changes: 157 additions & 1 deletion build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ version = "2.3.68"
description = "Libre simulation anticheat designed for 1.21 with 1.8-1.21 support, powered by PacketEvents 2.0."

java {
toolchain.languageVersion.set(JavaLanguageVersion.of(17))
toolchain.languageVersion.set(JavaLanguageVersion.of(18))
}

// Set to false for debug builds
Expand Down Expand Up @@ -59,6 +59,13 @@ repositories {
// FastUtil, Discord-Webhooks
}

// Add JMH configuration
configurations {
create("jmh")
create("jmhAnnotationProcessor")
}


dependencies {
implementation("com.github.retrooper:packetevents-spigot:2.6.1-SNAPSHOT")
implementation("co.aikar:acf-paper:0.5.1-SNAPSHOT")
Expand All @@ -76,6 +83,12 @@ dependencies {
compileOnly("com.viaversion:viaversion-api:5.0.4-SNAPSHOT")
//
compileOnly("io.netty:netty-all:4.1.85.Final")

// Replace jmhImplementation with the new configuration
"jmh"("org.openjdk.jmh:jmh-core:1.37")
"jmhAnnotationProcessor"("org.openjdk.jmh:jmh-generator-annprocess:1.37")

implementation(files("./nalim.jar"))
}

bukkit {
Expand Down Expand Up @@ -169,6 +182,71 @@ publishing.publications.create<MavenPublication>("maven") {
artifact(tasks["shadowJar"])
}

java {
sourceSets {
create("java18") {
java {
srcDirs("src/main/java18")
}
compileClasspath += main.get().output
runtimeClasspath += main.get().output
dependencies {
implementation(files("./nalim.jar")) // Add nalim.jar to java18 source set
}
}
create("jmh") {
java {
srcDir("src/jmh/java")
}
compileClasspath += sourceSets.main.get().output +
sourceSets.getByName("java18").output +
configurations["jmh"]
runtimeClasspath += sourceSets.main.get().output +
sourceSets.getByName("java18").output +
configurations["jmh"]
}
}
sourceCompatibility = JavaVersion.VERSION_17
targetCompatibility = JavaVersion.VERSION_17

toolchain {
languageVersion.set(JavaLanguageVersion.of(18))
}
}

tasks.withType<JavaCompile> {
if (name == "compileJava") {
options.compilerArgs.addAll(listOf("--add-exports","java.base/jdk.internal=ALL-UNNAMED",
"--add-exports", "java.base/jdk.internal.vm.annotation=ALL-UNNAMED"))
}
if (name == "compileJava18Java") {
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector", "-Xlint:unchecked"))
sourceCompatibility = "18"
targetCompatibility = "18"
}
if (name == "compileJava21Java") {
options.compilerArgs.addAll(listOf("--enable-preview", "--add-modules", "jdk.incubator.vector", "-Xlint:unchecked"))
sourceCompatibility = "21"
targetCompatibility = "21"
}
if (name == "compileJmhJava") {
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector"))
sourceCompatibility = "18"
targetCompatibility = "18"
}
}

tasks.withType<Jar> {
manifest {
attributes("Multi-Release" to "true")
}
duplicatesStrategy = DuplicatesStrategy.INCLUDE // Important for multi-release JARs
from(sourceSets.main.get().output)
from(sourceSets.getByName("java18").output) {
into("META-INF/versions/18")
}
}

tasks.shadowJar {
minimize()
archiveFileName.set("${project.name}-${project.version}.jar")
Expand All @@ -192,3 +270,81 @@ tasks.shadowJar {
relocate("org.jetbrains", "ac.grim.grimac.shaded.jetbrains")
}
}

tasks.register<Jar>("jmhJar") {
dependsOn("compileGeneratedJmh")

from(sourceSets["main"].output)
from(sourceSets["java18"].output)
from(sourceSets["jmh"].output)
from("${buildDir}/classes/java/jmh")
from(configurations["jmh"].map { if (it.isDirectory) it else zipTree(it) })

manifest {
attributes(
"Main-Class" to "org.openjdk.jmh.Main",
"Add-Opens" to "java.base/java.lang java.base/java.io java.base/java.util java.base/java.util.concurrent java.base/java.net",
"Add-Modules" to "jdk.incubator.vector"
)
}

duplicatesStrategy = DuplicatesStrategy.EXCLUDE
archiveClassifier.set("benchmarks")
}


tasks.register<JavaCompile>("compileGeneratedJmh") {
dependsOn("compileJmhJava")

source = fileTree("${buildDir}/generated-sources/jmh")

classpath = sourceSets["jmh"].compileClasspath +
sourceSets["java18"].output +
sourceSets["java18"].compileClasspath +
sourceSets.main.get().output +
sourceSets.main.get().compileClasspath +
files("${buildDir}/classes/java/jmh")

destinationDirectory.set(file("${buildDir}/classes/java/jmh"))

sourceCompatibility = "18"
targetCompatibility = "18"
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector"))
}

tasks.register("jmh") {
dependsOn("jmhJar")
doLast {
val includes = System.getProperty("includes", "")

javaexec {
classpath = files(tasks.named("jmhJar").get().outputs.files)
mainClass.set("org.openjdk.jmh.Main")
jvmArgs = listOf("--enable-preview", "--add-modules", "jdk.incubator.vector", "-XX:+UnlockExperimentalVMOptions", "-XX:+EnableJVMCI", "-javaagent:nalim.jar")

// Initialize the args list with default settings
args = mutableListOf<String>().apply {
// Add any default arguments here if needed
if (includes.isNotEmpty()) {
addAll(listOf(includes))
}
}
}
}
}

tasks.named<JavaCompile>("compileJmhJava") {
source = fileTree("src/jmh/java")
classpath = sourceSets["jmh"].compileClasspath +
sourceSets.main.get().output +
sourceSets["java18"].output
destinationDirectory.set(file("${buildDir}/classes/java/jmh"))

sourceCompatibility = "18"
targetCompatibility = "18"
options.compilerArgs.addAll(listOf("--add-modules", "jdk.incubator.vector"))
}

tasks.named("jmhJar") {
dependsOn("compileJmhJava")
}
148 changes: 148 additions & 0 deletions src/java18/java/ac/grim/grimac/utils/vector/SIMDVector3D.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,148 @@
package ac.grim.grimac.utils.vector;

import jdk.incubator.vector.*;

public final class SIMDVector3D implements Vector3D {

private static final VectorSpecies<Double> SPECIES = DoubleVector.SPECIES_256;
private static final VectorMask<Double> LENGTH_3_ARRAY_MASK = SPECIES.indexInRange(0, 3);
private static final VectorShuffle<Double> CROSS_PRODUCT_SHUFFLE1 = VectorShuffle.fromArray(SPECIES, new int[]{1, 2, 0, 3}, 0);
private static final VectorShuffle<Double> 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 SIMDVector3D(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) {
SIMDVector3D o = (SIMDVector3D) 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(((SIMDVector3D) o).vector);
return this;
}

@Override
public Vector3D subtract(Vector3D o) {
this.vector = this.vector.sub(((SIMDVector3D) o).vector);
return this;
}

@Override
public Vector3D multiply(Vector3D o) {
this.vector = this.vector.mul(((SIMDVector3D) 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(((SIMDVector3D) o).vector);
return diff.mul(diff).reduceLanes(VectorOperators.ADD);
}

@Override
public double dot(Vector3D o) {
return this.vector.mul(((SIMDVector3D) 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 {
SIMDVector3D cloned = (SIMDVector3D) 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);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package ac.grim.grimac.utils.vector;


public class VectorOperationsJava18 implements VectorOperations {

@Override
public Vector3D newVector(double x, double y, double z) {
return new SIMDVector3D(x, y, z);
}
}
Loading