From b9e94d3020b680d3747c661a47e499273f007d78 Mon Sep 17 00:00:00 2001 From: games647 Date: Tue, 30 Apr 2024 15:57:02 +0200 Subject: [PATCH] Improve threading by making use of green threads with Java 21 This is an experiment. The code was checked to keep blocking of platform threads to a minimum (See [1]). This will keep overhead to of using threads small while only I/O will allocate more threads. The project now uses Multi-Release Jars, so it will detect correct version during runtime while keeping backwards compatibility. However, your IDE might be set manually to Java 21. A multi-project [2] architecture might be necessary, but not tested if it really fixes it. [1] https://openjdk.org/jeps/444 [2] https://maven.apache.org/plugins/maven-compiler-plugin/multirelease.html --- README.md | 4 +- bukkit/pom.xml | 1 - core/pom.xml | 60 ++++++++++++++- .../fastlogin/core/AsyncScheduler.java | 8 +- .../fastlogin/core/AsyncScheduler.java | 75 +++++++++++++++++++ pom.xml | 26 +++++-- 6 files changed, 158 insertions(+), 16 deletions(-) create mode 100644 core/src/main/java21/com/github/games647/fastlogin/core/AsyncScheduler.java diff --git a/README.md b/README.md index f4409258..d0b265a2 100644 --- a/README.md +++ b/README.md @@ -60,11 +60,11 @@ Possible values: `Premium`, `Cracked`, `Unknown` ## Requirements -* Java 17+ (Recommended) +* Java 8 supported, but Java 21 recommended for improved threading * Server software in offlinemode: * Spigot (or a fork e.g. Paper) 1.8.8+ * Protocol plugin: - * [ProtocolLib 5.1+](https://www.spigotmc.org/resources/protocollib.1997/) or + * [ProtocolLib 5.2+](https://www.spigotmc.org/resources/protocollib.1997/) or * [ProtocolSupport](https://www.spigotmc.org/resources/protocolsupport.7201/) * Latest BungeeCord (or a fork e.g. Waterfall) or Velocity * An auth plugin. diff --git a/bukkit/pom.xml b/bukkit/pom.xml index aa03d832..4b88835f 100644 --- a/bukkit/pom.xml +++ b/bukkit/pom.xml @@ -98,7 +98,6 @@ *:* - META-INF/MANIFEST.MF **/module-info.class diff --git a/core/pom.xml b/core/pom.xml index 5106cd8b..7e861f92 100644 --- a/core/pom.xml +++ b/core/pom.xml @@ -77,9 +77,67 @@ + + org.apache.maven.plugins + maven-compiler-plugin + + 8 + 8 + + + + + jdk21 + + [21,) + + + + + + org.apache.maven.plugins + maven-compiler-plugin + + + jdk9 + + compile + + + 21 + + ${project.basedir}/src/main/java21 + + true + + + + + + org.apache.maven.plugins + maven-jar-plugin + + + default-jar + + + + true + + + + + + + + + + + + @@ -158,7 +216,7 @@ com.github.games647 craftapi - 0.6.2 + 0.7 diff --git a/core/src/main/java/com/github/games647/fastlogin/core/AsyncScheduler.java b/core/src/main/java/com/github/games647/fastlogin/core/AsyncScheduler.java index e1b42b68..53620e4e 100644 --- a/core/src/main/java/com/github/games647/fastlogin/core/AsyncScheduler.java +++ b/core/src/main/java/com/github/games647/fastlogin/core/AsyncScheduler.java @@ -40,20 +40,16 @@ import java.util.concurrent.atomic.AtomicInteger; */ public class AsyncScheduler { - private static final int MAX_CAPACITY = 1024; - - //todo: single thread for delaying and scheduling tasks private final Logger logger; - // 30 threads are still too many - the optimal solution is to separate into processing and blocking threads - // where processing threads could only be max number of cores while blocking threads could be minimized using - // non-blocking I/O and a single event executor private final Executor processingPool; private final AtomicInteger currentlyRunning = new AtomicInteger(); public AsyncScheduler(Logger logger, Executor processingPool) { this.logger = logger; + + logger.info("Using legacy scheduler"); this.processingPool = processingPool; } diff --git a/core/src/main/java21/com/github/games647/fastlogin/core/AsyncScheduler.java b/core/src/main/java21/com/github/games647/fastlogin/core/AsyncScheduler.java new file mode 100644 index 00000000..5e355319 --- /dev/null +++ b/core/src/main/java21/com/github/games647/fastlogin/core/AsyncScheduler.java @@ -0,0 +1,75 @@ +/* + * SPDX-License-Identifier: MIT + * + * The MIT License (MIT) + * + * Copyright (c) 2015-2023 games647 and contributors + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package com.github.games647.fastlogin.core; + +import org.slf4j.Logger; + +import java.util.concurrent.CompletableFuture; +import java.util.concurrent.Executor; +import java.util.concurrent.Executors; +import java.util.concurrent.atomic.AtomicInteger; + +/** + * This limits the number of threads that are used at maximum. Thread creation can be very heavy for the CPU and + * context switching between threads too. However, we need many threads for blocking HTTP and database calls. + * Nevertheless, this number can be further limited, because the number of actually working database threads + * is limited by the size of our database pool. The goal is to separate concerns into processing and blocking only + * threads. + */ +public class AsyncScheduler { + + private final Logger logger; + + private final Executor asyncPool; + + private final AtomicInteger currentlyRunning = new AtomicInteger(); + + public AsyncScheduler(Logger logger, Executor processingPool) { + this.logger = logger; + + logger.info("Using optimized green threads with Java 21"); + this.asyncPool = Executors.newVirtualThreadPerTaskExecutor(); + } + + public CompletableFuture runAsync(Runnable task) { + return CompletableFuture + .runAsync(() -> process(task), asyncPool) + .exceptionally(error -> { + logger.warn("Error occurred on thread pool", error); + return null; + }); + } + + private void process(Runnable task) { + currentlyRunning.incrementAndGet(); + try { + task.run(); + } finally { + currentlyRunning.getAndDecrement(); + } + } +} diff --git a/pom.xml b/pom.xml index e1fe9fcd..b1089098 100644 --- a/pom.xml +++ b/pom.xml @@ -48,12 +48,9 @@ Unknown - 8 - ${java.version} - ${java.version} - ${java.version} + + 1.18.30 development-2.2.2-SNAPSHOT 2.1.0-SNAPSHOT @@ -161,6 +158,23 @@ maven-surefire-plugin 3.2.5 + + + org.apache.maven.plugins + maven-jar-plugin + + + default-jar + + + + true + + + + + + @@ -188,7 +202,7 @@ org.projectlombok lombok - 1.18.30 + ${lombook.version} provided