Skip to content

Commit

Permalink
feat: add configurable per-thread target-size with programmatic default
Browse files Browse the repository at this point in the history
  • Loading branch information
dordsor21 committed Oct 28, 2024
1 parent 7fe7ec3 commit 5cb0b3b
Show file tree
Hide file tree
Showing 9 changed files with 85 additions and 17 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,9 @@ public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk>
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -440,7 +440,9 @@ public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk>
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -441,7 +441,9 @@ public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk>
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -442,7 +442,9 @@ public synchronized <T extends Future<T>> T call(IQueueExtent<? extends IChunk>
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(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -34,7 +33,7 @@ public class Config {

private final Map<String, Object> removedKeyVals = new HashMap<>();
@Nullable
private Map<String, Map.Entry<String, Object>> copyTo = new HashMap<>();
private Map<String, FromNode> copyTo = new HashMap<>();
private boolean performCopyTo = false;
private List<String> existingMigrateNodes = null;

Expand Down Expand Up @@ -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);
Expand Down Expand Up @@ -234,6 +233,16 @@ public void save(File file) {

}

@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.FIELD})
@interface ComputedFrom {

String node();

Class<? extends ConfigOptComputation<?>> computer();

}

@Ignore // This is not part of the config
public static class ConfigBlock<T> {

Expand Down Expand Up @@ -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<String, Object> 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);
Expand Down Expand Up @@ -513,4 +535,8 @@ private void setAccessible(Field field) throws NoSuchFieldException, IllegalAcce
}
}

private record FromNode(String node, Class<? extends ConfigOptComputation<?>> computation, Object val) {

}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.fastasyncworldedit.core.configuration;

sealed abstract class ConfigOptComputation<R> permits
ConfigOptComputation.THREAD_TARGET_SIZE_COMPUTATION {

public abstract R apply(Object val);

static final class THREAD_TARGET_SIZE_COMPUTATION extends ConfigOptComputation<Integer> {

@Override
public Integer apply(Object val) {
return 100 * 2 / (Integer) val;
}

}

}
Original file line number Diff line number Diff line change
Expand Up @@ -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",
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -149,8 +149,7 @@ public <T extends Filter> 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 {
Expand Down

0 comments on commit 5cb0b3b

Please sign in to comment.