True parallel registry

This commit is contained in:
Sotr
2018-05-28 21:45:08 +08:00
parent 6a33c4b4bf
commit 7d14adcb58
2 changed files with 48 additions and 47 deletions

View File

@@ -48,6 +48,6 @@ Contributing
* Feel free to open an [Issue](https://github.com/Akarin-project/akarin/issues) if you have any problem with Akarin. * Feel free to open an [Issue](https://github.com/Akarin-project/akarin/issues) if you have any problem with Akarin.
* [Pull Request](https://github.com/Akarin-project/akarin/pulls) is welcomed, Akarin use [Mixin](https://github.com/SpongePowered/Mixin) to modify the code, you can checkout `sources` folder to see them. Moreover, add your name to the [LICENSE](https://github.com/Akarin-project/Akarin/blob/master/LICENSE.md) if you want to publish your code under the [MIT License](https://github.com/Akarin-project/Akarin/blob/master/licenses/MIT.md). * [Pull Request](https://github.com/Akarin-project/akarin/pulls) is welcomed, Akarin use [Mixin](https://github.com/SpongePowered/Mixin) to modify the code, you can checkout `sources` folder to see them. Moreover, add your name to the [LICENSE](https://github.com/Akarin-project/Akarin/blob/master/LICENSE.md) if you want to publish your code under the [MIT License](https://github.com/Akarin-project/Akarin/blob/master/licenses/MIT.md).
* If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can send an email to `kira@kira.moe` with your experience and necessary information. Besides, welcome to join our [TIM Group](https://jq.qq.com/?_wv=1027&k=59q2kV4) to chat *(Chinese)*. * If you want to join the [Akarin-project](https://github.com/Akarin-project) team, you can send an email to `kira@kira.moe` with your experience and necessary information. Besides, welcome to join our [TIM Group](https://jq.qq.com/?_wv=1027&k=59q2kV4) to chat *(Chinese)*.
* Note that you need add `work/Paper/Paper-Server` to the `Build Path` of your IDE manually to organize the *NMS* import, the raw *NMS* dependency was dragged to the `Paper-Parent` project. * Note that you need add `work/Paper/Paper-Server` to the `Build Path` of your IDE manually to organize the *NMS* imports, the raw *NMS* dependency was dragged to the `Paper-Parent` project.
![Akarin project](https://i.loli.net/2018/05/13/5af7fbbfbcddf.png) ![Akarin project](https://i.loli.net/2018/05/13/5af7fbbfbcddf.png)

View File

@@ -4,7 +4,6 @@ import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors; import java.util.concurrent.Executors;
import java.util.concurrent.ThreadFactory; import java.util.concurrent.ThreadFactory;
import java.util.concurrent.TimeUnit; import java.util.concurrent.TimeUnit;
import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.Mixin;
import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject; import org.spongepowered.asm.mixin.injection.Inject;
@@ -13,7 +12,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo;
import com.google.common.util.concurrent.ThreadFactoryBuilder; import com.google.common.util.concurrent.ThreadFactoryBuilder;
import io.akarin.api.LogWrapper;
import net.minecraft.server.BiomeBase; import net.minecraft.server.BiomeBase;
import net.minecraft.server.Block; import net.minecraft.server.Block;
import net.minecraft.server.BlockFire; import net.minecraft.server.BlockFire;
@@ -31,111 +29,114 @@ public class ParallelRegistry {
private static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Parallel Registry Thread - %1$d").build(); private static final ThreadFactory STAGE_FACTORY = new ThreadFactoryBuilder().setNameFormat("Parallel Registry Thread - %1$d").build();
/** /**
* Registry order: SoundEffect -> Block -> BlockFire -> Item -> PotionBrewer -> BiomeBase * Registry order: SoundEffect -> Block
*/ */
private static final ExecutorService STAGE_A = Executors.newSingleThreadExecutor(STAGE_FACTORY); // TODO go deeper! private static final ExecutorService STAGE_BLOCK = Executors.newSingleThreadExecutor(STAGE_FACTORY);
/** /**
* Registry order: MobEffectList -> PotionRegistry * Registry order: Item -> PotionBrewer & orderless: BlockFire, BiomeBase (After STAGE_BLOCK)
*/ */
private static final ExecutorService STAGE_B = Executors.newSingleThreadExecutor(STAGE_FACTORY); private static final ExecutorService STAGE_BLOCK_BASE = Executors.newWorkStealingPool(3);
/** /**
* Registry order: Enchantment -> EntityTypes * Registry order: MobEffectList -> PotionRegistry & orderless: Enchantment, EntityTypes
*/ */
private static final ExecutorService STAGE_C = Executors.newSingleThreadExecutor(STAGE_FACTORY); private static final ExecutorService STAGE_STANDALONE = Executors.newWorkStealingPool(3);
private static final int TERMINATION_IN_SEC = 30; private static final int TERMINATION_IN_SEC = 30; // TODO configurable
// We've kept the original order in codes thought we use parallel
@Redirect(method = "c()V", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/SoundEffect.b()V" target = "net/minecraft/server/SoundEffect.b()V"
)) ))
private static void soundEffect() { private static void soundEffect() {
STAGE_A.execute(() -> SoundEffect.b()); STAGE_BLOCK.execute(() -> {
SoundEffect.b();
Block.w();
STAGE_BLOCK_BASE.execute(() -> BlockFire.e()); // This single task only cost 4ms, however, firing a task only takes 1ms
STAGE_BLOCK_BASE.execute(() -> {
Item.t();
PotionBrewer.a();
});
STAGE_BLOCK_BASE.execute(() -> BiomeBase.q());
});
} }
@Redirect(method = "c()V", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/Block.w()V" target = "net/minecraft/server/Block.w()V"
)) ))
private static void block() { private static void block() {} // STAGE_BLOCK
STAGE_A.execute(() -> Block.w());
}
@Redirect(method = "c()V", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/BlockFire.e()V" target = "net/minecraft/server/BlockFire.e()V"
)) ))
private static void blockFire() { private static void blockFire() {} // STAGE_BLOCK_BASE
STAGE_A.execute(() -> BlockFire.e());
}
@Redirect(method = "c()V", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/MobEffectList.k()V" target = "net/minecraft/server/MobEffectList.k()V"
)) ))
private static void mobEffectList() { private static void mobEffectList() {} // STAGE_STANDALONE
STAGE_B.execute(() -> MobEffectList.k());
}
@Redirect(method = "c()V", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/Enchantment.g()V" target = "net/minecraft/server/Enchantment.g()V"
)) ))
private static void enchantment() { private static void enchantment() {
STAGE_C.execute(() -> Enchantment.g()); STAGE_STANDALONE.execute(() -> Enchantment.g());
STAGE_STANDALONE.execute(() -> EntityTypes.c());
STAGE_STANDALONE.execute(() -> {
MobEffectList.k();
PotionRegistry.b();
});
} }
@Redirect(method = "c()V", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/Item.t()V" target = "net/minecraft/server/Item.t()V"
)) ))
private static void item() { private static void item() {} // STAGE_BLOCK_BASE
STAGE_A.execute(() -> Item.t());
}
@Redirect(method = "c", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/PotionRegistry.b()V" target = "net/minecraft/server/PotionRegistry.b()V"
)) ))
private static void potionRegistry() { private static void potionRegistry() {} // STAGE_STANDALONE
STAGE_B.execute(() -> PotionRegistry.b());
}
@Redirect(method = "c", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/PotionBrewer.a()V" target = "net/minecraft/server/PotionBrewer.a()V"
)) ))
private static void potionBrewer() { private static void potionBrewer() {} // STAGE_BLOCK_BASE
STAGE_A.execute(() -> PotionBrewer.a());
}
@Redirect(method = "c", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/EntityTypes.c()V" target = "net/minecraft/server/EntityTypes.c()V"
)) ))
private static void entityTypes() { private static void entityTypes() {} // STAGE_STANDALONE
STAGE_C.execute(() -> EntityTypes.c());
}
@Redirect(method = "c", at = @At( @Redirect(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/BiomeBase.q()V" target = "net/minecraft/server/BiomeBase.q()V"
)) ))
private static void biomeBase() { private static void biomeBase() {} // STAGE_BLOCK_BASE
STAGE_A.execute(() -> BiomeBase.q());
}
@Inject(method = "c", at = @At( @Inject(method = "c()V", at = @At(
value = "INVOKE", value = "INVOKE",
target = "net/minecraft/server/DispenserRegistry.b()V", target = "net/minecraft/server/DispenserRegistry.b()V",
shift = At.Shift.BEFORE shift = At.Shift.BEFORE
)) ))
private static void await(CallbackInfo info) throws InterruptedException { private static void await(CallbackInfo info) throws InterruptedException {
STAGE_A.shutdown(); STAGE_STANDALONE.shutdown();
STAGE_B.shutdown(); STAGE_BLOCK.shutdown();
STAGE_C.shutdown();
STAGE_A.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS); STAGE_STANDALONE.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS);
STAGE_B.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS); STAGE_BLOCK.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS);
STAGE_C.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS); // This must after STAGE_BLOCK terminated
STAGE_BLOCK_BASE.shutdown();
STAGE_BLOCK_BASE.awaitTermination(TERMINATION_IN_SEC, TimeUnit.SECONDS);
} }
} }