From 0000000000000000000000000000000000000000 Mon Sep 17 00:00:00 2001 From: Martijn Muijsers Date: Tue, 29 Nov 2022 12:27:47 +0100 Subject: [PATCH] CPU cores estimation License: AGPL-3.0 (https://www.gnu.org/licenses/agpl-3.0.html) Gale - https://galemc.org diff --git a/src/main/java/co/aikar/timings/TimingsExport.java b/src/main/java/co/aikar/timings/TimingsExport.java index dc9d304257d86109c06ec9b7673d3ee27d5ee875..781fc0a92f73be73b9313e7bc4eeb2f2ed6ff8ea 100644 --- a/src/main/java/co/aikar/timings/TimingsExport.java +++ b/src/main/java/co/aikar/timings/TimingsExport.java @@ -37,6 +37,7 @@ import org.bukkit.configuration.MemorySection; import org.bukkit.entity.EntityType; import org.galemc.gale.configuration.GaleGlobalConfiguration; import org.galemc.gale.configuration.timingsexport.VanillaServerPropertiesTimingsExport; +import org.galemc.gale.util.CPUCoresEstimation; import org.json.simple.JSONObject; import org.json.simple.JSONValue; import oshi.SystemInfo; @@ -206,6 +207,10 @@ public class TimingsExport extends Thread { pair("finalizing", ManagementFactory.getMemoryMXBean().getObjectPendingFinalizationCount()) )), pair("cpu", runtime.availableProcessors()), + // Gale start - CPU cores estimation - include in timings + pair("cpucoresestimation", CPUCoresEstimation.get()), + pair("cpuphysicalprocessorcount", processor.getPhysicalProcessorCount()), + // Gale end - CPU cores estimation - include in timings pair("cpuname", hardwareInfo.getProcessor().getProcessorIdentifier().getName().trim()), pair("hardwarespecs", hardwareSpecsMap), // Gale - include hardware specs in timings pair("runtime", runtimeBean.getUptime()), diff --git a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java index f5ed3fa20097bdd43a25c76b38353a23743bc9e5..eed9f125df46b616b7234a2d669971bc51bc231b 100644 --- a/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java +++ b/src/main/java/net/minecraft/server/dedicated/DedicatedServer.java @@ -49,6 +49,7 @@ import net.minecraft.world.level.block.entity.SkullBlockEntity; import net.minecraft.world.level.storage.LevelStorageSource; import org.galemc.gale.command.GaleCommands; import org.galemc.gale.configuration.GaleGlobalConfiguration; +import org.galemc.gale.util.CPUCoresEstimation; import org.slf4j.Logger; // CraftBukkit start @@ -226,6 +227,12 @@ public class DedicatedServer extends MinecraftServer implements ServerInterface io.papermc.paper.brigadier.PaperBrigadierProviderImpl.INSTANCE.getClass(); // init PaperBrigadierProvider // Paper end + // Gale start - CPU core estimation + if (GaleGlobalConfiguration.get().logToConsole.cpuCoresEstimation) { + CPUCoresEstimation.log(LOGGER); + } + // Gale end - CPU core estimation + // Gale start - Pufferfish - SIMD support // Attempt to detect vectorization try { diff --git a/src/main/java/org/bukkit/craftbukkit/CraftServer.java b/src/main/java/org/bukkit/craftbukkit/CraftServer.java index b9a799997e3475117ab91d7f3edb5f2f243a9f6b..e23fdd5ba09b50b7eef0ca4f36c5480779fba624 100644 --- a/src/main/java/org/bukkit/craftbukkit/CraftServer.java +++ b/src/main/java/org/bukkit/craftbukkit/CraftServer.java @@ -254,6 +254,8 @@ import org.bukkit.scoreboard.Criteria; import org.bukkit.structure.StructureManager; import org.bukkit.util.StringUtil; import org.bukkit.util.permissions.DefaultPermissions; +import org.galemc.gale.configuration.GaleGlobalConfiguration; +import org.galemc.gale.util.CPUCoresEstimation; import org.yaml.snakeyaml.Yaml; import org.yaml.snakeyaml.constructor.SafeConstructor; import org.yaml.snakeyaml.error.MarkedYAMLException; diff --git a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java index 48f6e114a3ead68d72f27f9d5572eacbc7613ac3..7a3111603c75105769cf0fc3ff3c5ee6d45b57e5 100644 --- a/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java +++ b/src/main/java/org/galemc/gale/configuration/GaleGlobalConfiguration.java @@ -185,6 +185,7 @@ public class GaleGlobalConfiguration extends ConfigurationPart { public boolean unrecognizedRecipes = false; // Gale - Purpur - do not log unrecognized recipes public boolean legacyMaterialInitialization = false; // Gale - Purpur - do not log legacy Material initialization public boolean playerLoginLocations = true; // Gale - JettPack - make logging login location configurable + public boolean cpuCoresEstimation = true; // Gale - CPU cores estimation public Chat chat; public class Chat extends ConfigurationPart { diff --git a/src/main/java/org/galemc/gale/util/CPUCoresEstimation.java b/src/main/java/org/galemc/gale/util/CPUCoresEstimation.java new file mode 100644 index 0000000000000000000000000000000000000000..07020fd1255fade812f4a9af1c76bdd5b7909436 --- /dev/null +++ b/src/main/java/org/galemc/gale/util/CPUCoresEstimation.java @@ -0,0 +1,102 @@ +// Gale - CPU cores estimation + +package org.galemc.gale.util; + +import org.galemc.gale.executor.annotation.thread.AnyThreadSafe; +import org.galemc.gale.executor.annotation.YieldFree; +import org.slf4j.Logger; +import oshi.SystemInfo; +import oshi.hardware.CentralProcessor; +import oshi.hardware.HardwareAbstractionLayer; + +/** + * A utility class to estimate the number of physical CPU cores. + * + * @author Martijn Muijsers under AGPL-3.0 + */ +@AnyThreadSafe +@YieldFree +public final class CPUCoresEstimation { + + private CPUCoresEstimation() {} + + public static final String environmentVariableKey = "gale.cores"; + + /** + * @return The value of the {@link #environmentVariableKey} environment variable, + * or -1 if not set, or -1 if the environment variable is set to a nonpositive value. + */ + public static int getByEnvironmentVariable() { + int value = Integer.getInteger(environmentVariableKey, -1); + return value > 0 ? value : -1; + } + + /** + * @return The number of cores estimated by OSHI using {@link CentralProcessor#getPhysicalProcessorCount()}, + * or -1 if OSHI throws an exception or returns a nonpositive value. + */ + public static int getByOSHI() { + try { + SystemInfo systemInfo = new SystemInfo(); + HardwareAbstractionLayer hardwareAbstractionLayer = systemInfo.getHardware(); + CentralProcessor centralProcessor = hardwareAbstractionLayer.getProcessor(); + int coresAccordingToOSHI = centralProcessor.getPhysicalProcessorCount(); + if (coresAccordingToOSHI > 0) { + return coresAccordingToOSHI; + } + } catch (Throwable ignored) {} + return -1; + } + + /** + * @return A naive estimation of the number of cores, by taking {@link Runtime#availableProcessors()} and applying + * floored division by 2, since most systems will have hyper-threading with 2 threads per core. If the result is + * nonpositive, this method returns 1. + */ + public static int getByRuntimeProcessors() { + return Math.min(1, Runtime.getRuntime().availableProcessors() / 2); + } + + public static int get() { + // Use the environment variable if set + int environmentCores = getByEnvironmentVariable(); + if (environmentCores > 0) { + return environmentCores; + } + // Use the OSHI library to find the physical processor count + int oshiCores = getByOSHI(); + if (oshiCores > 0) { + return oshiCores; + } + // Make a guess that the number of CPU cores is half the number of available runtime processor + // (i.e. guess that the CPU uses hyper-threading with 2 threads per core) + return getByRuntimeProcessors(); + } + + /** + * Logs the CPU core estimation to the given {@link Logger}. + */ + public static void log(Logger logger) { + String explanation; + int assumedValue; + // Use the environment variable if set + int environmentCores = getByEnvironmentVariable(); + // Use the OSHI library to find the physical processor count + int oshiCores = getByOSHI(); + if (environmentCores >= 1) { + assumedValue = environmentCores; + explanation = "(based on the '" + environmentVariableKey + "' environment variable)" + (oshiCores >= 1 ? " (OSHI detected " + oshiCores + " cores)" : ""); + } else { + explanation = "(because "; + if (oshiCores >= 1) { + assumedValue = oshiCores; + explanation += "OSHI detected " + oshiCores + " physical cores)"; + } else { + assumedValue = getByRuntimeProcessors(); + explanation += "the number of runtime processors is " + Runtime.getRuntime().availableProcessors() + ") (please set the '" + environmentVariableKey + "' environment variable if the number of physical CPU cores is incorrect)"; + } + } + logger.info("The server will assume you have " + assumedValue + " physical CPU cores " + explanation + ""); + } + +}