From 5cb0b3beedd91f3d3de9d0ce8037130da263fd6a Mon Sep 17 00:00:00 2001 From: dordsor21 Date: Mon, 28 Oct 2024 21:15:17 +0000 Subject: [PATCH] feat: add configurable per-thread target-size with programmatic default --- .../fawe/v1_20_R2/PaperweightGetBlocks.java | 4 +- .../fawe/v1_20_R3/PaperweightGetBlocks.java | 4 +- .../fawe/v1_20_R4/PaperweightGetBlocks.java | 4 +- .../fawe/v1_21_R1/PaperweightGetBlocks.java | 4 +- .../core/configuration/Config.java | 44 +++++++++++++++---- .../configuration/ConfigOptComputation.java | 17 +++++++ .../core/configuration/Settings.java | 13 +++++- .../core/queue/IQueueExtent.java | 9 +++- .../implementation/ParallelQueueExtent.java | 3 +- 9 files changed, 85 insertions(+), 17 deletions(-) create mode 100644 worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/ConfigOptComputation.java diff --git a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java index 54c06a9f88..6955a4fe2b 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_2/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R2/PaperweightGetBlocks.java @@ -440,7 +440,9 @@ public synchronized > T call(IQueueExtent LevelChunk chunk = nmsChunkFuture.getNow(null); if ((chunk == null && MemUtil.shouldBeginSlow()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) { try { - chunk = nmsChunkFuture.get(); // "Artificially" slow FAWE down if memory low as performing the + // "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of + // memory usage + chunk = nmsChunkFuture.get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e); throw new FaweException( diff --git a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java index 23821a16ed..d9eaf157e4 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_4/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R3/PaperweightGetBlocks.java @@ -440,7 +440,9 @@ public synchronized > T call(IQueueExtent LevelChunk chunk = nmsChunkFuture.getNow(null); if ((chunk == null && MemUtil.shouldBeginSlow()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) { try { - chunk = nmsChunkFuture.get(); // "Artificially" slow FAWE down if memory low as performing the + // "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of + // memory usage + chunk = nmsChunkFuture.get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e); throw new FaweException( diff --git a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java index 981e8a7b84..9eda21b586 100644 --- a/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_20_5/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_20_R4/PaperweightGetBlocks.java @@ -441,7 +441,9 @@ public synchronized > T call(IQueueExtent LevelChunk chunk = nmsChunkFuture.getNow(null); if ((chunk == null && MemUtil.shouldBeginSlow()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) { try { - chunk = nmsChunkFuture.get(); // "Artificially" slow FAWE down if memory low as performing the + // "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of + // memory usage + chunk = nmsChunkFuture.get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e); throw new FaweException( diff --git a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java index f5f921c8bd..ebcf138243 100644 --- a/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java +++ b/worldedit-bukkit/adapters/adapter-1_21/src/main/java/com/sk89q/worldedit/bukkit/adapter/impl/fawe/v1_21_R1/PaperweightGetBlocks.java @@ -442,7 +442,9 @@ public synchronized > T call(IQueueExtent LevelChunk chunk = nmsChunkFuture.getNow(null); if ((chunk == null && MemUtil.shouldBeginSlow()) || Settings.settings().QUEUE.ASYNC_CHUNK_LOAD_WRITE) { try { - chunk = nmsChunkFuture.get(); // "Artificially" slow FAWE down if memory low as performing the + // "Artificially" slow FAWE down if memory low as performing the operation async can cause large amounts of + // memory usage + chunk = nmsChunkFuture.get(); } catch (InterruptedException | ExecutionException e) { LOGGER.error("Could not get chunk at {},{} whilst low memory", chunkX, chunkZ, e); throw new FaweException( diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Config.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Config.java index 880b4cdf79..e872f6fdeb 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Config.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Config.java @@ -19,7 +19,6 @@ import java.lang.reflect.InvocationTargetException; import java.lang.reflect.Modifier; import java.lang.reflect.ParameterizedType; -import java.util.AbstractMap; import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; @@ -34,7 +33,7 @@ public class Config { private final Map removedKeyVals = new HashMap<>(); @Nullable - private Map> copyTo = new HashMap<>(); + private Map copyTo = new HashMap<>(); private boolean performCopyTo = false; private List existingMigrateNodes = null; @@ -85,11 +84,11 @@ private void setLoadedNode(String key, Object value, Class root) { if (copyTo != null) { copyTo.remove(key); // Remove if the config field is already written final Object finalValue = value; - copyTo.replaceAll((copyToNode, entry) -> { - if (!key.equals(entry.getKey())) { - return entry; + copyTo.replaceAll((copyToNode, fromNode) -> { + if (!key.equals(fromNode.node())) { + return fromNode; } - return new AbstractMap.SimpleEntry<>(key, finalValue); + return new FromNode(key, fromNode.computation, finalValue); }); } Migrate migrate = field.getAnnotation(Migrate.class); @@ -234,6 +233,16 @@ public void save(File file) { } + @Retention(RetentionPolicy.RUNTIME) + @Target({ElementType.FIELD}) + @interface ComputedFrom { + + String node(); + + Class> computer(); + + } + @Ignore // This is not part of the config public static class ConfigBlock { @@ -319,14 +328,27 @@ private void save(PrintWriter writer, Class clazz, final Object instance, int if (copyTo != null && (copiedFrom = field.getAnnotation(CopiedFrom.class)) != null) { String node = toNodeName(field.getName()); node = parentNode == null ? node : parentNode + "." + node; - Map.Entry entry = copyTo.remove(node); + FromNode entry = copyTo.remove(node); Object copiedVal; if (entry == null) { - copyTo.put(node, new AbstractMap.SimpleEntry<>(copiedFrom.value(), null)); - } else if ((copiedVal = entry.getValue()) != null) { + copyTo.put(node, new FromNode(copiedFrom.value(), null, null)); + } else if ((copiedVal = entry.val()) != null) { field.set(instance, copiedVal); } } + ComputedFrom computedFrom; + if (copyTo != null && (computedFrom = field.getAnnotation(ComputedFrom.class)) != null) { + String node = toNodeName(field.getName()); + node = parentNode == null ? node : parentNode + "." + node; + FromNode entry = copyTo.remove(node); + Object copiedVal; + if (entry == null) { + copyTo.put(node, new FromNode(computedFrom.node(), computedFrom.computer(), null)); + } else if ((copiedVal = entry.val()) != null) { + ConfigOptComputation computer = computedFrom.computer().getDeclaredConstructor().newInstance(); + field.set(instance, computer.apply(copiedVal)); + } + } Create create = field.getAnnotation(Create.class); if (create != null) { Object value = field.get(instance); @@ -513,4 +535,8 @@ private void setAccessible(Field field) throws NoSuchFieldException, IllegalAcce } } + private record FromNode(String node, Class> computation, Object val) { + + } + } diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/ConfigOptComputation.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/ConfigOptComputation.java new file mode 100644 index 0000000000..31d4d9133e --- /dev/null +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/ConfigOptComputation.java @@ -0,0 +1,17 @@ +package com.fastasyncworldedit.core.configuration; + +sealed abstract class ConfigOptComputation permits + ConfigOptComputation.THREAD_TARGET_SIZE_COMPUTATION { + + public abstract R apply(Object val); + + static final class THREAD_TARGET_SIZE_COMPUTATION extends ConfigOptComputation { + + @Override + public Integer apply(Object val) { + return 100 * 2 / (Integer) val; + } + + } + +} diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java index d9a9d24876..7c456f5df5 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/configuration/Settings.java @@ -606,12 +606,23 @@ public static class QUEUE { public boolean POOL = true; @Comment({ - "If chunk loading for writing edits to the world should be performed asynchronously to to FAWE", + "If chunk loading for writing edits to the world should be performed asynchronously to FAWE", " - Enable to improve performance at the expense of memory", " - If experience out of memory crashed, disable this or reduce slower-memory-percent" }) public boolean ASYNC_CHUNK_LOAD_WRITE = true; + @Comment({ + "Percentage of queue.target-size to use per thread in multi-threaded operations", + " - Minimum of 100 / queue.parallel-threads (queue.target-size split across threads)", + " - Maximum of 100 (queue.target-size per thread)", + " - Higher performance at the expense of memory", + " - I.e. target-size=400, parallel-threads=8 and threads-target-size=25 means target-size of 100 per thread", + " - Defaults to 100 * 2 / parallel-threads" + }) + @ComputedFrom(node = "queue.parallel-threads", computer = ConfigOptComputation.THREAD_TARGET_SIZE_COMPUTATION.class) + public int THREAD_TARGET_SIZE_PERCENT = 100 * 2 / Runtime.getRuntime().availableProcessors(); + public static class PROGRESS { @Comment({"Display constant titles about the progress of a user's edit", diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java index 052d590bbc..fce6537e55 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/IQueueExtent.java @@ -2,6 +2,9 @@ import com.fastasyncworldedit.core.extent.filter.block.ChunkFilterBlock; import com.fastasyncworldedit.core.extent.processor.IBatchProcessorHolder; +import com.fastasyncworldedit.core.internal.simd.SimdSupport; +import com.fastasyncworldedit.core.internal.simd.VectorizedCharFilterBlock; +import com.fastasyncworldedit.core.internal.simd.VectorizedFilter; import com.sk89q.worldedit.extent.Extent; import com.sk89q.worldedit.function.operation.Operation; import com.sk89q.worldedit.math.BlockVector2; @@ -154,7 +157,11 @@ default ChunkFilterBlock apply( if (newChunk != null) { chunk = newChunk; if (block == null) { - block = this.createFilterBlock(); + if (SimdSupport.useVectorApi() && filter instanceof VectorizedFilter) { + block = new VectorizedCharFilterBlock(this); + } else { + block = this.createFilterBlock(); + } } block.initChunk(chunkX, chunkZ); chunk.filterBlocks(filter, block, region, full); diff --git a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java index 995861514d..a9dd08cb78 100644 --- a/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java +++ b/worldedit-core/src/main/java/com/fastasyncworldedit/core/queue/implementation/ParallelQueueExtent.java @@ -149,8 +149,7 @@ public T apply(Region region, T filter, boolean full) { final SingleThreadQueueExtent queue = (SingleThreadQueueExtent) getNewQueue(); queue.setFastMode(fastmode); queue.setFaweExceptionArray(faweExceptionReasonsUsed); - int div = ((size + 1) * 3) >> 1; // Allow each thread to use 1.5x TARGET_SIZE / PARALLEL_THREADS - queue.setTargetSize(Settings.settings().QUEUE.TARGET_SIZE / div); + queue.setTargetSize(Settings.settings().QUEUE.TARGET_SIZE * Settings.settings().QUEUE.THREAD_TARGET_SIZE_PERCENT / 100); enter(queue); synchronized (queue) { try {