GlowstoneMC / Glowstone

A fast, customizable and compatible open source server for Minecraft: Java Edition
https://glowstone.net
Other
1.88k stars 271 forks source link

Parallelism Pulse entities in parallel using divide and conquer #1008

Open Hagvan opened 5 years ago

Hagvan commented 5 years ago

Utilizes CPU resources better. I tested this using artificial load inside GlowEntity.pulse() method - each pulse every entity generates 10000 random integers. Here are my benchmarks (CPU: Ryzen 2600X): A message is shown every 120 ticks inside each world thread.

Single threaded single thread

ForkJoinPool forkjoinpool

Possible concerns: I know in the future AI for mobs will be added and right now I don't know for sure if it will affect it. What I did is just speed up iteration inside the GlowWorld.pulse() method.

mastercoms commented 5 years ago

Would it be possible to use the forkJoinPool branch and make these as GlowTasks?

BEllis commented 5 years ago

Just re-iterating (excuse the pun) what I said in discord,

Would the Spliterator.trySplit not be a bit more concise? i.e. (taken from https://docs.oracle.com/javase/8/docs/api/java/util/Spliterator.html)

static <T> void parEach(TaggedArray<T> a, Consumer<T> action) {
   Spliterator<T> s = a.spliterator();
   long targetBatchSize = s.estimateSize() / (ForkJoinPool.getCommonPoolParallelism() * 8);
   new ParEach(null, s, action, targetBatchSize).invoke();
 }

 static class ParEach<T> extends CountedCompleter<Void> {
   final Spliterator<T> spliterator;
   final Consumer<T> action;
   final long targetBatchSize;

   ParEach(ParEach<T> parent, Spliterator<T> spliterator,
           Consumer<T> action, long targetBatchSize) {
     super(parent);
     this.spliterator = spliterator; this.action = action;
     this.targetBatchSize = targetBatchSize;
   }

   public void compute() {
     Spliterator<T> sub;
     while (spliterator.estimateSize() > targetBatchSize &&
            (sub = spliterator.trySplit()) != null) {
       addToPendingCount(1);
       new ParEach<>(this, sub, action, targetBatchSize).fork();
     }
     spliterator.forEachRemaining(action);
     propagateCompletion();
   }
 }
mastercoms commented 5 years ago

Thinking about it more, I would really like to have real world situations be tested on this, because of cache locality, world access, and more that would be important to actual entity simulation and AI.

Would you be able to make a more valid test scenario for this PR rather than just generating random numbers?

Hagvan commented 5 years ago

Sorry I was away for a while. I'll try to use actual AI if it is ready at this point.

mastercoms commented 5 years ago

It isn't ready, but we can hold this PR until then.