Optimize setAll

This commit is contained in:
themode 2022-01-05 00:24:32 +01:00 committed by TheMode
parent dee18150de
commit 9c11863f3e
4 changed files with 97 additions and 15 deletions

View File

@ -6,8 +6,8 @@ import org.openjdk.jmh.infra.Blackhole;
import java.util.concurrent.TimeUnit;
@Warmup(iterations = 5, time = 1500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 1500, timeUnit = TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)

View File

@ -4,9 +4,10 @@ import net.minestom.server.instance.palette.Palette;
import org.openjdk.jmh.annotations.*;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
@Warmup(iterations = 5, time = 1500, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 1500, timeUnit = TimeUnit.MILLISECONDS)
@Warmup(iterations = 5, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Measurement(iterations = 10, time = 1000, timeUnit = TimeUnit.MILLISECONDS)
@Fork(3)
@BenchmarkMode(Mode.AverageTime)
@OutputTimeUnit(TimeUnit.NANOSECONDS)
@ -36,6 +37,16 @@ public class PaletteSetBenchmark {
}
}
@Benchmark
public void incrWriteAll() {
AtomicInteger value = new AtomicInteger(0);
palette.setAll((x, y, z) -> {
final int v = value.getPlain();
value.setPlain(v + 1);
return v;
});
}
@Benchmark
public void constantWrite() {
final int dimension = palette.dimension();

View File

@ -96,7 +96,6 @@ final class PaletteImpl implements Palette, Cloneable {
final int bitsPerEntry = this.bitsPerEntry;
final int magicMask = MAGIC_MASKS[bitsPerEntry];
final int valuesPerLong = VALUES_PER_LONG[bitsPerEntry];
final int dimensionMinus = dimension - 1;
for (int i = 0; i < values.length; i++) {
@ -107,7 +106,7 @@ final class PaletteImpl implements Palette, Cloneable {
final int z = (index >> (dimensionBitCount)) & dimensionMinus;
final int x = index & dimensionMinus;
if (y >= dimension)
return; //
return; // Out of bounds
final int bitIndex = j * bitsPerEntry;
final short paletteIndex = (short) (value >> bitIndex & magicMask);
final int result = hasPalette ? paletteToValueList.getInt(paletteIndex) : paletteIndex;
@ -191,13 +190,76 @@ final class PaletteImpl implements Palette, Cloneable {
@Override
public void setAll(@NotNull EntrySupplier supplier) {
// TODO optimize
for (int y = 0; y < dimension; y++) {
for (int z = 0; z < dimension; z++) {
for (int x = 0; x < dimension; x++) {
set(x, y, z, supplier.get(x, y, z));
long[] values = this.values;
int bitsPerEntry = this.bitsPerEntry;
int valuesPerLong = VALUES_PER_LONG[bitsPerEntry];
if (values.length == 0) {
this.values = values = new long[(size + valuesPerLong - 1) / valuesPerLong];
}
int magicMask = MAGIC_MASKS[bitsPerEntry];
final int dimensionMinus = dimension - 1;
int[] cache = new int[maxSize()];
int j = 0;
valueLoop:
for (int i = 0; i < values.length; i++) {
long block = values[i];
for (; j < valuesPerLong; j++) {
final int index = i * valuesPerLong + j;
final int y = index >> (dimensionBitCount << 1);
final int z = (index >> (dimensionBitCount)) & dimensionMinus;
final int x = index & dimensionMinus;
if (y >= dimension)
continue; // Out of bounds
int value = supplier.get(x, y, z);
cache[index] = value;
final boolean placedAir = value == 0;
if (value != 0) {
value = getPaletteIndex(value);
if (bitsPerEntry != this.bitsPerEntry) {
// Palette has been resized, must update the loop indexes
for (int k = 0; k < j + 1; k++) { // Place previous elements from cache
final int index2 = i * valuesPerLong + k;
final int y2 = index2 >> (dimensionBitCount << 1);
final int z2 = (index2 >> (dimensionBitCount)) & dimensionMinus;
final int x2 = index2 & dimensionMinus;
if (y2 >= dimension)
throw new IllegalStateException("Out of bounds");
set(x2, y2, z2, cache[index2]);
}
bitsPerEntry = this.bitsPerEntry;
values = this.values;
valuesPerLong = VALUES_PER_LONG[bitsPerEntry];
magicMask = MAGIC_MASKS[bitsPerEntry];
if (values.length == 0) {
this.values = values = new long[(size + valuesPerLong - 1) / valuesPerLong];
}
i = (index) / valuesPerLong - 1;
j = index % valuesPerLong + 1;
continue valueLoop;
}
}
final int bitIndex = j * bitsPerEntry;
{
final long clear = MAGIC_MASKS[bitsPerEntry];
final long oldBlock = block >> bitIndex & magicMask;
if (oldBlock == value)
continue; // Trying to place the same block
final boolean currentAir = oldBlock == 0;
final long indexClear = clear << bitIndex;
block &= ~indexClear;
block |= (long) value << bitIndex;
if (currentAir != placedAir) {
// Block count changed
this.count += currentAir ? 1 : -1;
}
}
}
j = 0;
values[i] = block;
}
}

View File

@ -1,9 +1,13 @@
package net.minestom.server.instance;
import net.minestom.server.coordinate.Point;
import net.minestom.server.coordinate.Vec;
import net.minestom.server.instance.palette.Palette;
import org.junit.jupiter.api.Test;
import java.util.HashSet;
import java.util.List;
import java.util.Set;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.jupiter.api.Assertions.*;
@ -140,7 +144,8 @@ public class PaletteTest {
for (Palette palette : palettes) {
// Fill all entries
palette.setAll((x, y, z) -> x + y + z + 1);
palette.getAll((x, y, z, value) -> assertEquals(x + y + z + 1, value));
palette.getAll((x, y, z, value) -> assertEquals(x + y + z + 1, value,
"x: " + x + ", y: " + y + ", z: " + z + ", dimension: " + palette.dimension()));
// Replacing
palette.replaceAll((x, y, z, value) -> {
@ -164,9 +169,13 @@ public class PaletteTest {
// Fill all entries
count.set(0);
palette.setAll((x, y, z) -> count.incrementAndGet());
Set<Point> points = new HashSet<>();
palette.setAll((x, y, z) -> {
assertTrue(points.add(new Vec(x, y, z)), "Duplicate point: " + x + ", " + y + ", " + z + ", dimension " + palette.dimension());
return count.incrementAndGet();
});
assertEquals(palette.maxSize(), palette.size());
assertEquals(count.get(), palette.size());
assertEquals(palette.size(), count.get());
count.set(0);
palette.getAll((x, y, z, value) -> assertEquals(count.incrementAndGet(), value));