Add update propagator thread pool

This commit is contained in:
James Seibel
2024-03-04 07:10:20 -06:00
parent 0c5c4f3a74
commit c866fbfbfd
7 changed files with 101 additions and 21 deletions
@@ -907,7 +907,7 @@ public class Config
ThreadPresetConfigEventHandler.getFileHandlerDefaultThreadCount(),
Runtime.getRuntime().availableProcessors())
.comment(""
+ "How many threads should be used when reading in LOD data from disk? \n"
+ "How many threads should be used when reading/writing LOD data to/from disk? \n"
+ "\n"
+ "Increasing this number will cause LODs to load in faster, \n"
+ "but may cause lag when loading a new world or when \n"
@@ -920,6 +920,31 @@ public class Config
.comment(THREAD_RUN_TIME_RATIO_NOTE)
.build();
public static final ConfigEntry<Integer> numberOfUpdatePropagatorThreads = new ConfigEntry.Builder<Integer>()
.setMinDefaultMax(1,
ThreadPresetConfigEventHandler.getUpdatePropagatorDefaultThreadCount(),
Runtime.getRuntime().availableProcessors())
.comment(""
+ "How many threads should be used when applying LOD updates? \n"
+ "An LOD update is the operation of down-sampling a high detail LOD \n"
+ "into a lower detail one.\n"
+ "\n"
+ "This config can have a much higher number of threads \n"
+ "assigned and much lower run time ratio vs other thread pools \n"
+ "because the amount of time any particular thread may run is relatively low.\n"
+ "\n"
+ "This is because LOD updating only only partially thread safe, \n"
+ "so between 40% and 60% of the time a given thread may end up \n"
+ "waiting on another thread to finish updating the same LOD it also wants\n"
+ "to work on.\n"
+ "\n"
+ THREAD_NOTE)
.build();
public static final ConfigEntry<Double> runTimeRatioForUpdatePropagatorThreads = new ConfigEntry.Builder<Double>()
.setMinDefaultMax(0.01, ThreadPresetConfigEventHandler.getUpdatePropagatorDefaultRunTimeRatio(), 1.0)
.comment(THREAD_RUN_TIME_RATIO_NOTE)
.build();
public static final ConfigEntry<Integer> numberOfLodBuilderThreads = new ConfigEntry.Builder<Integer>()
.setMinDefaultMax(1,
ThreadPresetConfigEventHandler.getLodBuilderDefaultThreadCount(),
@@ -85,6 +85,28 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
}});
public static int getUpdatePropagatorDefaultThreadCount() { return getThreadCountByPercent(0.5); }
private final ConfigEntryWithPresetOptions<EThreadPreset, Integer> UpdatePropagatorThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfUpdatePropagatorThreads,
new HashMap<EThreadPreset, Integer>()
{{
this.put(EThreadPreset.MINIMAL_IMPACT, 1);
this.put(EThreadPreset.LOW_IMPACT, getUpdatePropagatorDefaultThreadCount());
this.put(EThreadPreset.BALANCED, getThreadCountByPercent(0.75));
this.put(EThreadPreset.AGGRESSIVE, getThreadCountByPercent(0.75));
this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, getThreadCountByPercent(1.0));
}});
public static double getUpdatePropagatorDefaultRunTimeRatio() { return 0.25; }
private final ConfigEntryWithPresetOptions<EThreadPreset, Double> UpdatePropagatorRunTime = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.runTimeRatioForUpdatePropagatorThreads,
new HashMap<EThreadPreset, Double>()
{{
this.put(EThreadPreset.MINIMAL_IMPACT, 0.25);
this.put(EThreadPreset.LOW_IMPACT, getUpdatePropagatorDefaultRunTimeRatio());
this.put(EThreadPreset.BALANCED, 0.5);
this.put(EThreadPreset.AGGRESSIVE, 1.0);
this.put(EThreadPreset.I_PAID_FOR_THE_WHOLE_CPU, 1.0);
}});
public static int getLodBuilderDefaultThreadCount() { return getThreadCountByPercent(0.1); }
private final ConfigEntryWithPresetOptions<EThreadPreset, Integer> lodBuilderThreadCount = new ConfigEntryWithPresetOptions<>(Config.Client.Advanced.MultiThreading.numberOfLodBuilderThreads,
new HashMap<EThreadPreset, Integer>()
@@ -122,6 +144,9 @@ public class ThreadPresetConfigEventHandler extends AbstractPresetConfigEventHan
this.configList.add(this.fileHandlerThreadCount);
this.configList.add(this.fileHandlerRunTime);
this.configList.add(this.UpdatePropagatorThreadCount);
this.configList.add(this.UpdatePropagatorRunTime);
this.configList.add(this.lodBuilderThreadCount);
this.configList.add(this.lodBuilderRunTime);
@@ -168,7 +168,7 @@ public abstract class AbstractNewDataSourceHandler
@Override
public CompletableFuture<Void> updateDataSourceAsync(NewFullDataSource inputDataSource)
{
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
ThreadPoolExecutor executor = ThreadPoolUtil.getUpdatePropagatorExecutor();
if (executor == null || executor.isTerminated())
{
return CompletableFuture.completedFuture(null);
@@ -214,18 +214,27 @@ public abstract class AbstractNewDataSourceHandler
if (dataModified)
{
// save the updated data to the database
TDTO dto = this.createDtoFromDataSource(dataSource);
this.repo.save(dto);
for (IDataSourceUpdateFunc<TDataSource> listener : this.dateSourceUpdateListeners)
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
if (executor == null || executor.isTerminated())
{
if (listener != null)
{
listener.OnDataSourceUpdated(dataSource);
}
return;
}
executor.execute(() ->
{
// save the updated data to the database
TDTO dto = this.createDtoFromDataSource(dataSource);
this.repo.save(dto);
for (IDataSourceUpdateFunc<TDataSource> listener : this.dateSourceUpdateListeners)
{
if (listener != null)
{
listener.OnDataSourceUpdated(dataSource);
}
}
});
}
}
catch (Exception e)
@@ -58,7 +58,7 @@ public class NewFullDataFileHandler
private static final int MAX_PARENT_UPDATE_TASK_COUNT = NUMBER_OF_PARENT_UPDATE_TASKS_PER_THREAD * Config.Client.Advanced.MultiThreading.numberOfFileHandlerThreads.get();
/** indicates how long the update queue thread should wait between queuing ticks */
private static final int UPDATE_QUEUE_THREAD_DELAY_IN_MS = 500;
private static final int UPDATE_QUEUE_THREAD_DELAY_IN_MS = 1_000;
/** the list of queued positions that need to update their parents */
Set<DhSectionPos> parentApplicationPositionSet = ConcurrentHashMap.newKeySet();
@@ -210,7 +210,7 @@ public class NewFullDataFileHandler
if (updatePosList.size() != 0)
{
// stop if the file handler has been shut down
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
ThreadPoolExecutor executor = ThreadPoolUtil.getUpdatePropagatorExecutor();
if (executor == null || executor.isTerminated())
{
this.updateQueueThreadRunningRef.set(false);
@@ -222,6 +222,9 @@ public class NewFullDataFileHandler
int queueCount = 0;
for (DhSectionPos pos : updatePosList)
{
// James thought batching together updates
// based on the parent they were going to update would reduce update locks,
// but after testing it didn't, so we're just queing each section individually
if (this.parentApplicationPositionSet.add(pos))
{
queueCount++;
@@ -230,8 +233,6 @@ public class NewFullDataFileHandler
NewFullDataSource inputData = this.get(pos);
// update the parent position with this new data
this.updateDataSourceAtPos(pos.getParentPos(), inputData, true);
// TODO add comparable interface to make this low priority
});
}
}
@@ -126,13 +126,18 @@ public class RenderSourceFileHandler extends AbstractLegacyDataSourceHandler<Col
/** Returns what should be displayed in Minecraft's F3 debug menu */
private String[] f3Log()
{
ThreadPoolExecutor executor = ThreadPoolUtil.getFileHandlerExecutor();
String queueSize = (executor != null) ? executor.getQueue().size()+"" : "-";
String completedTaskSize = (executor != null) ? executor.getCompletedTaskCount()+"" : "-";
ThreadPoolExecutor fileExecutor = ThreadPoolUtil.getFileHandlerExecutor();
String fileQueueSize = (fileExecutor != null) ? fileExecutor.getQueue().size()+"" : "-";
String fileCompletedTaskSize = (fileExecutor != null) ? fileExecutor.getCompletedTaskCount()+"" : "-";
ThreadPoolExecutor updateExecutor = ThreadPoolUtil.getUpdatePropagatorExecutor();
String updateQueueSize = (updateExecutor != null) ? updateExecutor.getQueue().size()+"" : "-";
String updateCompletedTaskSize = (updateExecutor != null) ? updateExecutor.getCompletedTaskCount()+"" : "-";
ArrayList<String> lines = new ArrayList<>();
lines.add("File Handler [" + this.level.getLevelWrapper().getDimensionType().getDimensionName() + "]");
lines.add(" Thread pool tasks: " + queueSize + " (completed: " + completedTaskSize + ")");
lines.add(" File thread pool tasks: " + fileQueueSize + " (completed: " + fileCompletedTaskSize + ")");
lines.add(" Update thread pool tasks: " + updateQueueSize + " (completed: " + updateCompletedTaskSize + ")");
lines.add(" Unsaved render sources: " + this.unsavedDataSourceBySectionPos.size());
return lines.toArray(new String[0]);
@@ -46,11 +46,17 @@ public class ThreadPoolUtil
@Nullable
public static ThreadPoolExecutor getFileHandlerExecutor() { return fileHandlerThreadPool.executor; }
public static final DhThreadFactory UPDATE_PROPAGATOR_THREAD_FACTORY = new DhThreadFactory("LOD Update Propagator", Thread.MIN_PRIORITY);
private static RateLimitedThreadPoolExecutor updatePropagatorThreadPool;
@Nullable
public static ThreadPoolExecutor getUpdatePropagatorExecutor() { return updatePropagatorThreadPool; }
public static final DhThreadFactory WORLD_GEN_THREAD_FACTORY = new DhThreadFactory("World Gen", Thread.MIN_PRIORITY);
private static ConfigThreadPool worldGenThreadPool;
@Nullable
public static ThreadPoolExecutor getWorldGenExecutor() { return worldGenThreadPool.executor; }
public static final String BUFFER_UPLOADER_THREAD_NAME = "Buffer Uploader";
private static ThreadPoolExecutor bufferUploaderThreadPool;
@Nullable
public static ThreadPoolExecutor getBufferUploaderExecutor() { return bufferUploaderThreadPool; }
@@ -99,8 +105,9 @@ public class ThreadPoolUtil
// standalone threads //
fileHandlerThreadPool = new ConfigThreadPool(FILE_HANDLER_THREAD_FACTORY, Config.Client.Advanced.MultiThreading.numberOfFileHandlerThreads, Config.Client.Advanced.MultiThreading.runTimeRatioForFileHandlerThreads, null);
updatePropagatorThreadPool = new RateLimitedThreadPoolExecutor(8, 1.0, UPDATE_PROPAGATOR_THREAD_FACTORY);
worldGenThreadPool = new ConfigThreadPool(WORLD_GEN_THREAD_FACTORY, Config.Client.Advanced.MultiThreading.numberOfWorldGenerationThreads, Config.Client.Advanced.MultiThreading.runTimeRatioForWorldGenerationThreads, null);
bufferUploaderThreadPool = ThreadUtil.makeSingleThreadPool("Buffer Uploader");
bufferUploaderThreadPool = ThreadUtil.makeSingleThreadPool(BUFFER_UPLOADER_THREAD_NAME);
@@ -138,6 +145,7 @@ public class ThreadPoolUtil
{
// standalone threads
fileHandlerThreadPool.shutdownExecutorService();
updatePropagatorThreadPool.shutdown();
worldGenThreadPool.shutdownExecutorService();
bufferUploaderThreadPool.shutdown();
@@ -395,6 +395,13 @@
"distanthorizons.config.client.advanced.multiThreading.runTimeRatioForFileHandlerThreads":
"Runtime % for file handler threads",
"distanthorizons.config.client.advanced.multiThreading.numberOfUpdatePropagatorThreads":
"NO. of update propagator threads",
"distanthorizons.config.client.advanced.multiThreading.numberOfUpdatePropagatorThreads.@tooltip":
"How many threads should be used when applying LOD updates? \nAn LOD update is the operation of down-sampling a high detail LOD \ninto a lower detail one. \n\nThis config can have a much higher number of threads \nassigned and much lower run time ratio vs other thread pools \nbecause the amount of time any particular thread may run is relatively low.",
"distanthorizons.config.client.advanced.multiThreading.runTimeRatioForUpdatePropagatorThreads":
"Runtime % for update propagator threads",
"distanthorizons.config.client.advanced.multiThreading.numberOfLodBuilderThreads":
"NO. of LOD builder threads",
"distanthorizons.config.client.advanced.multiThreading.numberOfLodBuilderThreads.@tooltip":