From c51bf9d9f34f793d78cab297256b38efc6aaa38e Mon Sep 17 00:00:00 2001 From: Aikar Date: Fri, 18 Mar 2016 19:25:43 -0400 Subject: [PATCH] Optimize BlockStateList/BlockData Mojang included some sanity checks on arguments passed to the BlockData. This code results in the Hash look up occuring twice per call, one to test if it exists and another to retrieve the result. This code should ideally never be hit, unless mojang released a bad build. We can discover bugs with this as furthur code that never expects a null would then NPE, so it would not result in hidden issues. This is super hot code, so removing those checks should give decent gains. --- .../0003-mc-dev-imports.patch | 335 +++++++++++++++++- ...92-Optimize-BlockStateList-BlockData.patch | 45 +++ 2 files changed, 378 insertions(+), 2 deletions(-) create mode 100644 Spigot-Server-Patches/0092-Optimize-BlockStateList-BlockData.patch diff --git a/Spigot-Server-Patches/0003-mc-dev-imports.patch b/Spigot-Server-Patches/0003-mc-dev-imports.patch index b70dccc75..362ded4b5 100644 --- a/Spigot-Server-Patches/0003-mc-dev-imports.patch +++ b/Spigot-Server-Patches/0003-mc-dev-imports.patch @@ -1,4 +1,4 @@ -From 93c3ea89eb16b2069bf0f078276f208780ea2920 Mon Sep 17 00:00:00 2001 +From 620e9058fe63c0f5eff7f4a44e168979a856af22 Mon Sep 17 00:00:00 2001 From: Zach Brown Date: Mon, 29 Feb 2016 21:09:10 -0600 Subject: [PATCH] mc-dev imports @@ -1986,6 +1986,337 @@ index 0000000..e7a95f3 + } + } +} +diff --git a/src/main/java/net/minecraft/server/BlockStateList.java b/src/main/java/net/minecraft/server/BlockStateList.java +new file mode 100644 +index 0000000..a11c62f +--- /dev/null ++++ b/src/main/java/net/minecraft/server/BlockStateList.java +@@ -0,0 +1,325 @@ ++package net.minecraft.server; ++ ++import com.google.common.base.Function; ++import com.google.common.base.Objects; ++import com.google.common.collect.HashBasedTable; ++import com.google.common.collect.ImmutableCollection; ++import com.google.common.collect.ImmutableList; ++import com.google.common.collect.ImmutableMap; ++import com.google.common.collect.ImmutableSortedMap; ++import com.google.common.collect.ImmutableTable; ++import com.google.common.collect.Iterables; ++import com.google.common.collect.Lists; ++import com.google.common.collect.Maps; ++import java.util.ArrayList; ++import java.util.Collection; ++import java.util.Collections; ++import java.util.HashMap; ++import java.util.Iterator; ++import java.util.LinkedHashMap; ++import java.util.List; ++import java.util.Map; ++import java.util.Map.Entry; ++import java.util.regex.Pattern; ++ ++public class BlockStateList { ++ ++ private static final Pattern a = Pattern.compile("^[a-z0-9_]+$"); ++ private static final Function, String> b = new Function() { ++ public String a(IBlockState iblockstate) { ++ return iblockstate == null ? "" : iblockstate.a(); ++ } ++ ++ public Object apply(Object object) { ++ return this.a((IBlockState) object); ++ } ++ }; ++ private final Block c; ++ private final ImmutableSortedMap> d; ++ private final ImmutableList e; ++ ++ public BlockStateList(Block block, IBlockState... aiblockstate) { ++ this.c = block; ++ HashMap hashmap = Maps.newHashMap(); ++ IBlockState[] aiblockstate1 = aiblockstate; ++ int i = aiblockstate.length; ++ ++ for (int j = 0; j < i; ++j) { ++ IBlockState iblockstate = aiblockstate1[j]; ++ ++ a(block, iblockstate); ++ hashmap.put(iblockstate.a(), iblockstate); ++ } ++ ++ this.d = ImmutableSortedMap.copyOf(hashmap); ++ LinkedHashMap linkedhashmap = Maps.newLinkedHashMap(); ++ ArrayList arraylist = Lists.newArrayList(); ++ Iterable iterable = IteratorUtils.a(this.e()); ++ Iterator iterator = iterable.iterator(); ++ ++ while (iterator.hasNext()) { ++ List list = (List) iterator.next(); ++ Map map = MapGeneratorUtils.b(this.d.values(), list); ++ BlockStateList.BlockData blockstatelist_blockdata = new BlockStateList.BlockData(block, ImmutableMap.copyOf(map), null); ++ ++ linkedhashmap.put(map, blockstatelist_blockdata); ++ arraylist.add(blockstatelist_blockdata); ++ } ++ ++ iterator = arraylist.iterator(); ++ ++ while (iterator.hasNext()) { ++ BlockStateList.BlockData blockstatelist_blockdata1 = (BlockStateList.BlockData) iterator.next(); ++ ++ blockstatelist_blockdata1.a((Map) linkedhashmap); ++ } ++ ++ this.e = ImmutableList.copyOf(arraylist); ++ } ++ ++ public static > String a(Block block, IBlockState iblockstate) { ++ String s = iblockstate.a(); ++ ++ if (!BlockStateList.a.matcher(s).matches()) { ++ throw new IllegalArgumentException("Block: " + block.getClass() + " has invalidly named property: " + s); ++ } else { ++ for (T t : iblockstate.c()) { ++ String s1 = iblockstate.a(t); ++ ++ if (!a.matcher(s1).matches()) ++ { ++ throw new IllegalArgumentException("Block: " + block.getClass() + " has property: " + s + " with invalidly named value: " + s1); ++ } ++ } ++ } ++ return s; ++ } ++ ++ public ImmutableList a() { ++ return this.e; ++ } ++ ++ private List>> e() { ++ ArrayList arraylist = Lists.newArrayList(); ++ ImmutableCollection immutablecollection = this.d.values(); ++ Iterator iterator = immutablecollection.iterator(); ++ ++ while (iterator.hasNext()) { ++ IBlockState iblockstate = (IBlockState) iterator.next(); ++ ++ arraylist.add(iblockstate.c()); ++ } ++ ++ return arraylist; ++ } ++ ++ public IBlockData getBlockData() { ++ return (IBlockData) this.e.get(0); ++ } ++ ++ public Block getBlock() { ++ return this.c; ++ } ++ ++ public Collection> d() { ++ return this.d.values(); ++ } ++ ++ public String toString() { ++ return Objects.toStringHelper(this).add("block", Block.REGISTRY.b(this.c)).add("properties", Iterables.transform(this.d.values(), BlockStateList.b)).toString(); ++ } ++ ++ static class BlockData extends BlockDataAbstract { ++ ++ private final Block a; ++ private final ImmutableMap, Comparable> b; ++ private ImmutableTable, Comparable, IBlockData> c; ++ ++ private BlockData(Block block, ImmutableMap, Comparable> immutablemap) { ++ this.a = block; ++ this.b = immutablemap; ++ } ++ ++ public Collection> r() { ++ return Collections.unmodifiableCollection(this.b.keySet()); ++ } ++ ++ public > T get(IBlockState iblockstate) { ++ if (!this.b.containsKey(iblockstate)) { ++ throw new IllegalArgumentException("Cannot get property " + iblockstate + " as it does not exist in " + this.a.t()); ++ } else { ++ return iblockstate.b().cast(this.b.get(iblockstate)); ++ } ++ } ++ ++ public , V extends T> IBlockData set(IBlockState iblockstate, V v0) { ++ if (!this.b.containsKey(iblockstate)) { ++ throw new IllegalArgumentException("Cannot set property " + iblockstate + " as it does not exist in " + this.a.t()); ++ } else if (!iblockstate.c().contains(v0)) { ++ throw new IllegalArgumentException("Cannot set property " + iblockstate + " to " + v0 + " on block " + Block.REGISTRY.b(this.a) + ", it is not an allowed value"); ++ } else { ++ return (IBlockData) (this.b.get(iblockstate) == v0 ? this : (IBlockData) this.c.get(iblockstate, v0)); ++ } ++ } ++ ++ public ImmutableMap, Comparable> s() { ++ return this.b; ++ } ++ ++ public Block getBlock() { ++ return this.a; ++ } ++ ++ public boolean equals(Object object) { ++ return this == object; ++ } ++ ++ public int hashCode() { ++ return this.b.hashCode(); ++ } ++ ++ public void a(Map, Comparable>, BlockStateList.BlockData> map) { ++ if (this.c != null) { ++ throw new IllegalStateException(); ++ } else { ++ HashBasedTable hashbasedtable = HashBasedTable.create(); ++ Iterator iterator = this.b.entrySet().iterator(); ++ ++ while (iterator.hasNext()) { ++ Entry entry = (Entry) iterator.next(); ++ IBlockState iblockstate = (IBlockState) entry.getKey(); ++ Iterator iterator1 = iblockstate.c().iterator(); ++ ++ while (iterator1.hasNext()) { ++ Comparable comparable = (Comparable) iterator1.next(); ++ ++ if (comparable != entry.getValue()) { ++ hashbasedtable.put(iblockstate, comparable, map.get(this.b(iblockstate, comparable))); ++ } ++ } ++ } ++ ++ this.c = ImmutableTable.copyOf(hashbasedtable); ++ } ++ } ++ ++ private Map, Comparable> b(IBlockState iblockstate, Comparable comparable) { ++ HashMap hashmap = Maps.newHashMap(this.b); ++ ++ hashmap.put(iblockstate, comparable); ++ return hashmap; ++ } ++ ++ public Material getMaterial() { ++ return this.a.q(this); ++ } ++ ++ public boolean b() { ++ return this.a.l(this); ++ } ++ ++ public int c() { ++ return this.a.m(this); ++ } ++ ++ public int d() { ++ return this.a.o(this); ++ } ++ ++ public boolean f() { ++ return this.a.p(this); ++ } ++ ++ public MaterialMapColor g() { ++ return this.a.r(this); ++ } ++ ++ public IBlockData a(EnumBlockRotation enumblockrotation) { ++ return this.a.a((IBlockData) this, enumblockrotation); ++ } ++ ++ public IBlockData a(EnumBlockMirror enumblockmirror) { ++ return this.a.a((IBlockData) this, enumblockmirror); ++ } ++ ++ public boolean h() { ++ return this.a.c((IBlockData) this); ++ } ++ ++ public EnumRenderType i() { ++ return this.a.a((IBlockData) this); ++ } ++ ++ public boolean k() { ++ return this.a.s(this); ++ } ++ ++ public boolean l() { ++ return this.a.isOccluding(this); ++ } ++ ++ public boolean m() { ++ return this.a.isPowerSource(this); ++ } ++ ++ public int a(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { ++ return this.a.b((IBlockData) this, iblockaccess, blockposition, enumdirection); ++ } ++ ++ public boolean n() { ++ return this.a.isComplexRedstone(this); ++ } ++ ++ public int a(World world, BlockPosition blockposition) { ++ return this.a.d(this, world, blockposition); ++ } ++ ++ public float b(World world, BlockPosition blockposition) { ++ return this.a.b(this, world, blockposition); ++ } ++ ++ public float a(EntityHuman entityhuman, World world, BlockPosition blockposition) { ++ return this.a.getDamage(this, entityhuman, world, blockposition); ++ } ++ ++ public int b(IBlockAccess iblockaccess, BlockPosition blockposition, EnumDirection enumdirection) { ++ return this.a.c(this, iblockaccess, blockposition, enumdirection); ++ } ++ ++ public EnumPistonReaction o() { ++ return this.a.h(this); ++ } ++ ++ public IBlockData b(IBlockAccess iblockaccess, BlockPosition blockposition) { ++ return this.a.updateState(this, iblockaccess, blockposition); ++ } ++ ++ public boolean p() { ++ return this.a.b((IBlockData) this); ++ } ++ ++ public AxisAlignedBB d(World world, BlockPosition blockposition) { ++ return this.a.a((IBlockData) this, world, blockposition); ++ } ++ ++ public void a(World world, BlockPosition blockposition, AxisAlignedBB axisalignedbb, List list, Entity entity) { ++ this.a.a((IBlockData) this, world, blockposition, axisalignedbb, list, entity); ++ } ++ ++ public AxisAlignedBB c(IBlockAccess iblockaccess, BlockPosition blockposition) { ++ return this.a.a((IBlockData) this, iblockaccess, blockposition); ++ } ++ ++ public MovingObjectPosition a(World world, BlockPosition blockposition, Vec3D vec3d, Vec3D vec3d1) { ++ return this.a.a(this, world, blockposition, vec3d, vec3d1); ++ } ++ ++ public boolean q() { ++ return this.a.k(this); ++ } ++ ++ BlockData(Block block, ImmutableMap immutablemap, Object object) { ++ this(block, immutablemap); ++ } ++ } ++} diff --git a/src/main/java/net/minecraft/server/ChunkProviderFlat.java b/src/main/java/net/minecraft/server/ChunkProviderFlat.java new file mode 100644 index 0000000..17e0b8e @@ -5158,5 +5489,5 @@ index 0000000..2286c9e + } +} -- -2.7.3 +2.7.4 diff --git a/Spigot-Server-Patches/0092-Optimize-BlockStateList-BlockData.patch b/Spigot-Server-Patches/0092-Optimize-BlockStateList-BlockData.patch new file mode 100644 index 000000000..f06802f92 --- /dev/null +++ b/Spigot-Server-Patches/0092-Optimize-BlockStateList-BlockData.patch @@ -0,0 +1,45 @@ +From 23c5437c07a1bf673509fc5f20aed2117defe615 Mon Sep 17 00:00:00 2001 +From: Aikar +Date: Fri, 18 Mar 2016 19:15:44 -0400 +Subject: [PATCH] Optimize BlockStateList/BlockData + +Mojang included some sanity checks on arguments passed to the BlockData. +This code results in the Hash look up occuring twice per call, one to test if it exists +and another to retrieve the result. + +This code should ideally never be hit, unless mojang released a bad build. We can discover bugs with this as furthur code that never expects a null +would then NPE, so it would not result in hidden issues. + +This is super hot code, so removing those checks should give decent gains. + +diff --git a/src/main/java/net/minecraft/server/BlockStateList.java b/src/main/java/net/minecraft/server/BlockStateList.java +index a11c62f..43f198b 100644 +--- a/src/main/java/net/minecraft/server/BlockStateList.java ++++ b/src/main/java/net/minecraft/server/BlockStateList.java +@@ -145,21 +145,11 @@ public class BlockStateList { + } + + public > T get(IBlockState iblockstate) { +- if (!this.b.containsKey(iblockstate)) { +- throw new IllegalArgumentException("Cannot get property " + iblockstate + " as it does not exist in " + this.a.t()); +- } else { +- return iblockstate.b().cast(this.b.get(iblockstate)); +- } ++ return iblockstate.b().cast(this.b.get(iblockstate)); // Paper + } + + public , V extends T> IBlockData set(IBlockState iblockstate, V v0) { +- if (!this.b.containsKey(iblockstate)) { +- throw new IllegalArgumentException("Cannot set property " + iblockstate + " as it does not exist in " + this.a.t()); +- } else if (!iblockstate.c().contains(v0)) { +- throw new IllegalArgumentException("Cannot set property " + iblockstate + " to " + v0 + " on block " + Block.REGISTRY.b(this.a) + ", it is not an allowed value"); +- } else { +- return (IBlockData) (this.b.get(iblockstate) == v0 ? this : (IBlockData) this.c.get(iblockstate, v0)); +- } ++ return this.b.get(iblockstate) == v0 ? this : this.c.get(iblockstate, v0); // Paper + } + + public ImmutableMap, Comparable> s() { +-- +2.7.4 +