Add memory/CPU optimized BlockVectorSet

Which internally it uses a map of LocalBlockVectorSet
This commit is contained in:
Jesse Boyd 2017-02-06 13:13:18 +11:00
parent 0427771b7e
commit 8fd908232e
No known key found for this signature in database
GPG Key ID: 59F1DE6293AF6E1F
2 changed files with 226 additions and 21 deletions

View File

@ -0,0 +1,184 @@
package com.boydti.fawe.object.collection;
import com.boydti.fawe.util.MathMan;
import com.sk89q.worldedit.MutableBlockVector;
import com.sk89q.worldedit.Vector;
import it.unimi.dsi.fastutil.ints.Int2ObjectMap;
import it.unimi.dsi.fastutil.ints.Int2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ObjectIterator;
import java.util.AbstractCollection;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Iterator;
import java.util.NoSuchElementException;
import java.util.Objects;
import java.util.Set;
/**
* The BlockVectorSet is a Memory optimized Set for storing BlockVectors
* - Internally it uses a map of Index->LocalBlockVectorSet
* - All BlockVectors must be a valid world coordinate: y=[0,255],x=[-30000000,30000000],z=[-30000000,30000000]
* - This will use ~8 bytes for every 64 BlockVectors (about 800x less than a HashSet)
*/
public class BlockVectorSet extends AbstractCollection<Vector> implements Set<Vector> {
private Int2ObjectMap<LocalBlockVectorSet> localSets = new Int2ObjectOpenHashMap<>();
@Override
public int size() {
int size = 0;
for (Int2ObjectMap.Entry<LocalBlockVectorSet> entry : localSets.int2ObjectEntrySet()) {
size += entry.getValue().size();
}
return size;
}
@Override
public boolean isEmpty() {
for (Int2ObjectMap.Entry<LocalBlockVectorSet> entry : localSets.int2ObjectEntrySet()) {
if (!entry.getValue().isEmpty()) {
return false;
}
}
return true;
}
public boolean contains(int x, int y, int z) {
int pair = MathMan.pair((short) (x >> 11), (short) (z >> 11));
LocalBlockVectorSet localMap = localSets.get(pair);
return localMap.contains(x & 2047, y, z & 2047);
}
@Override
public boolean contains(Object o) {
if (o instanceof Vector) {
Vector v = (Vector) o;
return contains(v.getBlockX(), v.getBlockY(), v.getBlockZ());
}
return false;
}
@Override
public Iterator<Vector> iterator() {
final ObjectIterator<Int2ObjectMap.Entry<LocalBlockVectorSet>> entries = localSets.int2ObjectEntrySet().iterator();
if (!entries.hasNext()) {
return new ArrayList<Vector>().iterator();
}
return new Iterator<Vector>() {
Int2ObjectMap.Entry<LocalBlockVectorSet> entry = entries.next();
Iterator<Vector> entryIter = entry.getValue().iterator();
MutableBlockVector mutable = new MutableBlockVector();
@Override
public void remove() {
entryIter.remove();
}
@Override
public boolean hasNext() {
return entryIter.hasNext() || entries.hasNext();
}
@Override
public Vector next() {
while (!entryIter.hasNext()) {
if (!entries.hasNext()) {
throw new NoSuchElementException("End of iterator");
}
entry = entries.next();
entryIter = entry.getValue().iterator();
}
Vector localPos = entryIter.next();
int pair = entry.getIntKey();
int cx = MathMan.unpairX(pair);
int cz = MathMan.unpairY(pair);
return mutable.setComponents((cx << 11) + localPos.getBlockX(), localPos.getBlockY(), (cz << 11) + localPos.getBlockZ());
}
};
}
@Override
public boolean add(Vector vector) {
return add(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ());
}
public boolean add(int x, int y, int z) {
int pair = MathMan.pair((short) (x >> 11), (short) (z >> 11));
LocalBlockVectorSet localMap = localSets.get(pair);
if (localMap == null) {
localMap = new LocalBlockVectorSet();
localMap.setOffset(1024, 1024);
localSets.put(pair, localMap);
}
return localMap.add(x & 2047, y, z & 2047);
}
public boolean remove(int x, int y, int z) {
int pair = MathMan.pair((short) (x >> 11), (short) (z >> 11));
LocalBlockVectorSet localMap = localSets.get(pair);
if (localMap != null) {
if (localMap.remove(x & 2047, y, z & 2047)) {
if (localMap.isEmpty()) {
localSets.remove(pair);
}
return true;
}
}
return false;
}
@Override
public boolean remove(Object o) {
if (o instanceof Vector) {
Vector v = (Vector) o;
return remove(v.getBlockX(), v.getBlockY(), v.getBlockZ());
}
return false;
}
@Override
public boolean containsAll(Collection<?> c) {
for (Object o : c) {
if (!contains(o)) {
return false;
}
}
return true;
}
@Override
public boolean addAll(Collection<? extends Vector> c) {
boolean result = false;
for (Vector v : c) {
result |= add(v);
}
return result;
}
@Override
public boolean retainAll(Collection<?> c) {
Objects.requireNonNull(c);
boolean modified = false;
Iterator it = iterator();
while (it.hasNext()) {
if (!c.contains(it.next())) {
it.remove();
modified = true;
}
}
return modified;
}
@Override
public boolean removeAll(Collection<?> c) {
boolean result = false;
for (Object o : c) {
result |= remove(o);
}
return result;
}
@Override
public void clear() {
localSets.clear();
}
}

View File

@ -9,7 +9,7 @@ import java.util.Iterator;
import java.util.Set; import java.util.Set;
/** /**
* The LocalBlockVectorSet is a Memory and CPU optimized set for storing BlockVectors which are all in a local region * The LocalBlockVectorSet is a Memory and CPU optimized Set for storing BlockVectors which are all in a local region
* - All vectors must be in a 2048 * 2048 area centered around the first entry * - All vectors must be in a 2048 * 2048 area centered around the first entry
* - This will use 8 bytes for every 64 BlockVectors (about 800x less than a HashSet) * - This will use 8 bytes for every 64 BlockVectors (about 800x less than a HashSet)
*/ */
@ -33,18 +33,24 @@ public class LocalBlockVectorSet implements Set<Vector> {
return set.isEmpty(); return set.isEmpty();
} }
public boolean contains(int x, int y, int z) {
return set.get(MathMan.tripleSearchCoords(x - offsetX, y, z - offsetZ));
}
@Override @Override
public boolean contains(Object o) { public boolean contains(Object o) {
if (o instanceof Vector) { if (o instanceof Vector) {
Vector v = (Vector) o; Vector v = (Vector) o;
int x = v.getBlockX(); return contains(v.getBlockX(), v.getBlockY(), v.getBlockZ());
int y = v.getBlockY();
int z = v.getBlockZ();
return set.get(MathMan.tripleSearchCoords(x - offsetX, y, z - offsetZ));
} }
return false; return false;
} }
public void setOffset(int x, int z) {
this.offsetX = x;
this.offsetZ = z;
}
@Override @Override
public Iterator<Vector> iterator() { public Iterator<Vector> iterator() {
return new Iterator<Vector>() { return new Iterator<Vector>() {
@ -105,40 +111,55 @@ public class LocalBlockVectorSet implements Set<Vector> {
return array; return array;
} }
@Override public boolean add(int x, int y, int z) {
public boolean add(Vector vector) {
if (offsetX == Integer.MAX_VALUE) { if (offsetX == Integer.MAX_VALUE) {
offsetX = vector.getBlockX(); offsetX = x;
offsetZ = vector.getBlockZ(); offsetZ = z;
} }
int relX = vector.getBlockX() - offsetX; int relX = x - offsetX;
int relZ = vector.getBlockZ() - offsetZ; int relZ = z - offsetZ;
if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) { if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) {
throw new UnsupportedOperationException("LocalVectorSet can only contain vectors within 1024 blocks (cuboid) of the first entry. "); throw new UnsupportedOperationException("LocalVectorSet can only contain vectors within 1024 blocks (cuboid) of the first entry. ");
} }
int index = getIndex(vector); if (y < 0 || y > 256) {
throw new UnsupportedOperationException("LocalVectorSet can only contain vectors from y elem:[0,255]");
}
int index = getIndex(x, y, z);
boolean value = set.get(index); boolean value = set.get(index);
set.set(index); set.set(index);
return !value; return !value;
} }
@Override
public boolean add(Vector vector) {
return add(vector.getBlockX(), vector.getBlockY(), vector.getBlockZ());
}
private int getIndex(Vector vector) { private int getIndex(Vector vector) {
return MathMan.tripleSearchCoords(vector.getBlockX() - offsetX, vector.getBlockY(), vector.getBlockZ() - offsetZ); return MathMan.tripleSearchCoords(vector.getBlockX() - offsetX, vector.getBlockY(), vector.getBlockZ() - offsetZ);
} }
private int getIndex(int x, int y, int z) {
return MathMan.tripleSearchCoords(x - offsetX, y, z - offsetZ);
}
public boolean remove(int x, int y, int z) {
int relX = x - offsetX;
int relZ = z - offsetZ;
if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) {
return false;
}
int index = MathMan.tripleSearchCoords(relX, y, relZ);
boolean value = set.get(index);
set.clear(index);
return value;
}
@Override @Override
public boolean remove(Object o) { public boolean remove(Object o) {
if (o instanceof Vector) { if (o instanceof Vector) {
Vector v = (Vector) o; Vector v = (Vector) o;
int relX = v.getBlockX() - offsetX; return remove(v.getBlockX(), v.getBlockY(), v.getBlockZ());
int relZ = v.getBlockZ() - offsetZ;
if (relX > 1023 || relX < -1024 || relZ > 1023 || relZ < -1024) {
return false;
}
int index = MathMan.tripleSearchCoords(relX, v.getBlockY(), relZ);
boolean value = set.get(index);
set.clear(index);
return value;
} }
return false; return false;
} }