Trim performance (#451)

* Increase performance slightly when trimming.
If the chunk section is all one blocks (common in plotworlds) it'll be a nice little boost.

* Cache whether blocks are ticking or not. Greatly reduces the time required to create a palette

* collapse 5 lines to 2.

* Also apply to 14 and 15 for the numpties

* Cleanup
Actually ignore the exception - remove my debug print.
Remove double semi-colon

* Apparently 1.14/15 matter too still.
This commit is contained in:
dordsor21 2020-05-07 23:00:13 +01:00 committed by GitHub
parent a2b0a5e622
commit 56972ee40b
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
16 changed files with 171 additions and 10 deletions

View File

@ -4,6 +4,7 @@ import com.sk89q.worldedit.bukkit.WorldEditPlugin;
import com.sk89q.worldedit.math.BlockVector3;
import com.sk89q.worldedit.world.block.BlockID;
import com.sk89q.worldedit.world.block.BlockState;
import com.sk89q.worldedit.world.block.BlockTypesCache;
import java.util.Map;
import java.util.function.Function;
@ -49,6 +50,8 @@ public class NMSAdapter {
int air = 0;
int num_palette = 0;
char[] getArr = null;
char lastOrdinal = BlockID.__RESERVED__;
boolean lastticking = false;
for (int i = 0; i < 4096; i++) {
char ordinal = set[i];
switch (ordinal) {
@ -75,8 +78,16 @@ public class NMSAdapter {
air++;
break;
}
BlockState state = BlockState.getFromOrdinal(ordinal);
if (state.getMaterial().isTicksRandomly()) {
boolean ticking;
if (ordinal != lastOrdinal) {
ticking = BlockTypesCache.ticking[ordinal];
lastOrdinal = ordinal;
lastticking = ticking;
} else {
ticking = lastticking;
}
if (ticking) {
BlockState state = BlockState.getFromOrdinal(ordinal);
ticking_blocks.put(BlockVector3.at(i & 15, (i >> 8) & 15, (i >> 4) & 15),
WorldEditPlugin.getInstance().getBukkitImplAdapter()
.getInternalBlockStateId(state).orElse(0));

View File

@ -5,6 +5,7 @@ import static org.slf4j.LoggerFactory.getLogger;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.blocks.CharBlocks;
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.DelegateLock;
@ -620,7 +621,37 @@ public class BukkitGetBlocks_1_14 extends CharGetBlocks {
if (aggressive) {
sections = null;
nmsChunk = null;
return super.trim(true);
} else {
for (int i = 0; i < 16; i++) {
if (!hasSection(i) || super.sections[i] == CharBlocks.EMPTY) {
continue;
}
ChunkSection existing = getSections()[i];
try {
final DataPaletteBlock<IBlockData> blocksExisting = existing.getBlocks();
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitAdapter_1_14.fieldPalette.get(blocksExisting);
int paletteSize;
if (palette instanceof DataPaletteLinear) {
paletteSize = ((DataPaletteLinear<IBlockData>) palette).b();
} else if (palette instanceof DataPaletteHash) {
paletteSize = ((DataPaletteHash<IBlockData>) palette).b();
} else {
super.trim(false, i);
continue;
}
if (paletteSize == 1) {
//If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks.
continue;
}
super.trim(false, i);
} catch (IllegalAccessException ignored) {
super.trim(false, i);
}
}
return true;
}
return super.trim(aggressive);
}
}

View File

@ -5,6 +5,7 @@ import static org.slf4j.LoggerFactory.getLogger;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.blocks.CharBlocks;
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.DelegateLock;
@ -640,7 +641,37 @@ public class BukkitGetBlocks_1_15 extends CharGetBlocks {
if (aggressive) {
sections = null;
nmsChunk = null;
return super.trim(true);
} else {
for (int i = 0; i < 16; i++) {
if (!hasSection(i) || super.sections[i] == CharBlocks.EMPTY) {
continue;
}
ChunkSection existing = getSections()[i];
try {
final DataPaletteBlock<IBlockData> blocksExisting = existing.getBlocks();
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitAdapter_1_15.fieldPalette.get(blocksExisting);
int paletteSize;
if (palette instanceof DataPaletteLinear) {
paletteSize = ((DataPaletteLinear<IBlockData>) palette).b();
} else if (palette instanceof DataPaletteHash) {
paletteSize = ((DataPaletteHash<IBlockData>) palette).b();
} else {
super.trim(false, i);
continue;
}
if (paletteSize == 1) {
//If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks.
continue;
}
super.trim(false, i);
} catch (IllegalAccessException ignored) {
super.trim(false, i);
}
}
return true;
}
return super.trim(aggressive);
}
}

View File

@ -3,6 +3,7 @@ package com.boydti.fawe.bukkit.adapter.mc1_15_2;
import com.boydti.fawe.Fawe;
import com.boydti.fawe.FaweCache;
import com.boydti.fawe.beta.IChunkSet;
import com.boydti.fawe.beta.implementation.blocks.CharBlocks;
import com.boydti.fawe.beta.implementation.blocks.CharGetBlocks;
import com.boydti.fawe.beta.implementation.queue.QueueHandler;
import com.boydti.fawe.bukkit.adapter.DelegateLock;
@ -644,7 +645,37 @@ public class BukkitGetBlocks_1_15_2 extends CharGetBlocks {
if (aggressive) {
sections = null;
nmsChunk = null;
return super.trim(true);
} else {
for (int i = 0; i < 16; i++) {
if (!hasSection(i) || super.sections[i] == CharBlocks.EMPTY) {
continue;
}
ChunkSection existing = getSections()[i];
try {
final DataPaletteBlock<IBlockData> blocksExisting = existing.getBlocks();
final DataPalette<IBlockData> palette = (DataPalette<IBlockData>) BukkitAdapter_1_15_2.fieldPalette.get(blocksExisting);
int paletteSize;
if (palette instanceof DataPaletteLinear) {
paletteSize = ((DataPaletteLinear<IBlockData>) palette).b();
} else if (palette instanceof DataPaletteHash) {
paletteSize = ((DataPaletteHash<IBlockData>) palette).b();
} else {
super.trim(false, i);
continue;
}
if (paletteSize == 1) {
//If the cached palette size is 1 then no blocks can have been changed i.e. do not need to update these chunks.
continue;
}
super.trim(false, i);
} catch (IllegalAccessException ignored) {
super.trim(false, i);
}
}
return true;
}
return super.trim(aggressive);
}
}

View File

@ -145,4 +145,9 @@ public class CombinedBlocks implements IBlocks {
public boolean trim(boolean aggressive) {
return false;
}
@Override
public boolean trim(boolean aggressive, int layer) {
return false;
}
}

View File

@ -41,6 +41,8 @@ public interface IBlocks extends Trimable {
.map(layer -> (1 << layer)).sum();
}
boolean trim(boolean aggressive, int layer);
IBlocks reset();
default byte[] toByteArray(boolean full) {

View File

@ -153,4 +153,9 @@ public class BitSetBlocks implements IChunkSet {
public boolean trim(boolean aggressive) {
return false;
}
@Override
public boolean trim(boolean aggressive, int layer) {
return false;
}
}

View File

@ -60,6 +60,17 @@ public abstract class CharBlocks implements IBlocks {
return result;
}
@Override
public boolean trim(boolean aggressive, int layer) {
boolean result = true;
if (sections[layer] == EMPTY && blocks[layer] != null) {
blocks[layer] = null;
} else {
result = false;
}
return result;
}
@Override
public IChunkSet reset() {
for (int i = 0; i < 16; i++) {

View File

@ -25,6 +25,13 @@ public abstract class CharGetBlocks extends CharBlocks implements IChunkGet {
return true;
}
@Override
public boolean trim(boolean aggressive, int layer) {
sections[layer] = EMPTY;
blocks[layer] = null;
return true;
}
@Override
public IChunkSet reset() {
super.reset();

View File

@ -85,6 +85,11 @@ public class FallbackChunkGet implements IChunkGet {
return true;
}
@Override
public boolean trim(boolean aggressive, int layer) {
return true;
}
@Override
public <T extends Future<T>> T call(IChunkSet set, Runnable finalize) {
for (int layer = 0; layer < 16; layer++) {

View File

@ -50,6 +50,10 @@ object NullChunkGet : IChunkGet {
return true
}
override fun trim(aggressive: Boolean, layer: Int): Boolean {
return true
}
override fun <T : Future<T>> call(set: IChunkSet, finalize: Runnable): T? {
return null
}

View File

@ -344,6 +344,11 @@ public class ChunkHolder<T extends Future<T>> implements IQueueChunk<T> {
return false;
}
@Override
public boolean trim(boolean aggressive, int layer) {
return this.trim(aggressive);
}
@Override
public boolean isEmpty() {
return chunkSet == null || chunkSet.isEmpty();

View File

@ -117,5 +117,9 @@ object NullChunk : IQueueChunk<Nothing> {
override fun trim(aggressive: Boolean): Boolean {
return true
}
override fun trim(aggressive: Boolean, layer: Int): Boolean {
return true
}
}

View File

@ -4,7 +4,6 @@ import com.boydti.fawe.beta.IBatchProcessor;
import com.boydti.fawe.beta.IChunk;
import com.boydti.fawe.beta.IChunkGet;
import com.boydti.fawe.beta.IChunkSet;
import com.sk89q.worldedit.extent.Extent;
public class BatchProcessorHolder implements IBatchProcessorHolder {
private IBatchProcessor processor = EmptyBatchProcessor.INSTANCE;

View File

@ -549,6 +549,11 @@ public class MCAChunk implements IChunk {
return isEmpty();
}
@Override
public boolean trim(boolean aggressive, int layer) {
return hasSection(layer);
}
@Override
public CompoundTag getEntity(UUID uuid) {
return this.entities.get(uuid);

View File

@ -1,6 +1,7 @@
package com.sk89q.worldedit.world.block;
import com.boydti.fawe.util.MathMan;
import com.google.common.primitives.Booleans;
import com.sk89q.worldedit.WorldEdit;
import com.sk89q.worldedit.extension.platform.Capability;
import com.sk89q.worldedit.extension.platform.Platform;
@ -166,12 +167,14 @@ public class BlockTypesCache {
public static final BlockType[] values;
public static final BlockState[] states;
public static final boolean[] ticking;
protected static final Set<String> $NAMESPACES = new LinkedHashSet<>();
static {
try {
ArrayList<BlockState> stateList = new ArrayList<>();
ArrayList<Boolean> tickList = new ArrayList<>();
Platform platform = WorldEdit.getInstance().getPlatformManager().queryCapability(Capability.GAME_HOOKS);
Registries registries = platform.getRegistries();
@ -202,7 +205,7 @@ public class BlockTypesCache {
if (values[internalId] != null) {
throw new IllegalStateException("Invalid duplicate id for " + field.getName());
}
BlockType type = register(defaultState, internalId, stateList);
BlockType type = register(defaultState, internalId, stateList, tickList);
// Note: Throws IndexOutOfBoundsError if nothing is registered and blocksMap is empty
values[internalId] = type;
}
@ -214,7 +217,7 @@ public class BlockTypesCache {
String defaultState = entry.getValue();
// Skip already registered ids
for (; values[internalId] != null; internalId++);
BlockType type = register(defaultState, internalId, stateList);
BlockType type = register(defaultState, internalId, stateList, tickList);
values[internalId] = type;
}
}
@ -223,7 +226,7 @@ public class BlockTypesCache {
}
states = stateList.toArray(new BlockState[stateList.size()]);
ticking = Booleans.toArray(tickList);
} catch (Throwable e) {
e.printStackTrace();
@ -231,12 +234,14 @@ public class BlockTypesCache {
}
}
private static BlockType register(final String id, int internalId, List<BlockState> states) {
private static BlockType register(final String id, int internalId, List<BlockState> states, List<Boolean> tickList) {
// Get the enum name (remove namespace if minecraft:)
int propStart = id.indexOf('[');
String typeName = id.substring(0, propStart == -1 ? id.length() : propStart);
String enumName = (typeName.startsWith("minecraft:") ? typeName.substring(10) : typeName).toUpperCase(Locale.ROOT);
int oldsize = states.size();
BlockType existing = new BlockType(id, internalId, states);
tickList.addAll(Collections.nCopies(states.size() - oldsize, existing.getMaterial().isTicksRandomly()));
// register states
BlockType.REGISTRY.register(typeName, existing);
String nameSpace = typeName.substring(0, typeName.indexOf(':'));