Reworked thread safe cache solution.
Also performed refactoring. And added some threaded tests.
This commit is contained in:
@@ -9,7 +9,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.datatypes.Point2;
|
||||
|
||||
/**
|
||||
* Unit test for simple App.
|
||||
|
@@ -2,9 +2,14 @@ package ca.recrown.islandsurvivalcraft.caching;
|
||||
|
||||
import static org.junit.Assert.assertSame;
|
||||
import static org.junit.jupiter.api.Assertions.assertEquals;
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.junit.jupiter.api.AfterEach;
|
||||
import org.junit.jupiter.api.BeforeAll;
|
||||
@@ -28,35 +33,35 @@ public class CacheTest {
|
||||
|
||||
@Test
|
||||
public void testBasicCaching() {
|
||||
String val = integerCache.getValue(0);
|
||||
String val = integerCache.get(0);
|
||||
assertTrue(val == null);
|
||||
val = "first";
|
||||
integerCache.setValue(0, val);
|
||||
assertTrue(integerCache.getValue(0) != null);
|
||||
assertSame(val, integerCache.getValue(0));
|
||||
integerCache.set(0, val);
|
||||
assertTrue(integerCache.get(0) != null);
|
||||
assertSame(val, integerCache.get(0));
|
||||
|
||||
val = integerCache.getValue(1);
|
||||
val = integerCache.get(1);
|
||||
assertTrue(val == null);
|
||||
val = "second";
|
||||
integerCache.setValue(1, val);
|
||||
assertTrue(integerCache.getValue(1) != null);
|
||||
assertSame(val, integerCache.getValue(1));
|
||||
integerCache.set(1, val);
|
||||
assertTrue(integerCache.get(1) != null);
|
||||
assertSame(val, integerCache.get(1));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testUsageBasedClean() {
|
||||
integerCache.setValue(0, "first");
|
||||
integerCache.setValue(1, "second");
|
||||
integerCache.setValue(2, "third");
|
||||
|
||||
assertEquals("first", integerCache.getValue(0));
|
||||
integerCache.set(0, "first");
|
||||
integerCache.set(1, "second");
|
||||
integerCache.set(2, "third");
|
||||
|
||||
integerCache.setValue(3, "fourth");
|
||||
assertEquals("first", integerCache.getValue(0));
|
||||
assertEquals("first", integerCache.get(0));
|
||||
|
||||
integerCache.setValue(4, "fifth");
|
||||
assertTrue(integerCache.getValue(3) != null);
|
||||
assertTrue(integerCache.getValue(0) != null);
|
||||
integerCache.set(3, "fourth");
|
||||
assertEquals("first", integerCache.get(0));
|
||||
|
||||
integerCache.set(4, "fifth");
|
||||
assertTrue(integerCache.get(3) != null);
|
||||
assertTrue(integerCache.get(0) != null);
|
||||
}
|
||||
|
||||
@Test
|
||||
@@ -64,20 +69,20 @@ public class CacheTest {
|
||||
int amount = 1024;
|
||||
Random random = new Random();
|
||||
|
||||
Cache<Integer, Integer> largeCache = new Cache<>(amount/2);
|
||||
Cache<Integer, Integer> largeCache = new Cache<>(amount / 2);
|
||||
|
||||
int[] values = new int[amount];
|
||||
for (int i = 0; i < amount; i++) {
|
||||
values[i] = random.nextInt();
|
||||
largeCache.setValue(i, values[i]);
|
||||
largeCache.set(i, values[i]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < amount /2; i++) {
|
||||
assertEquals(null, largeCache.getValue(i), "Current accessor: " + i);
|
||||
for (int i = 0; i < amount / 2; i++) {
|
||||
assertEquals(null, largeCache.get(i), "Current accessor: " + i);
|
||||
}
|
||||
|
||||
for (int i = amount /2; i < amount; i++) {
|
||||
assertEquals(values[i], largeCache.getValue(i), "Current accessor: " + i);
|
||||
for (int i = amount / 2; i < amount; i++) {
|
||||
assertEquals(values[i], largeCache.get(i), "Current accessor: " + i);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -86,23 +91,194 @@ public class CacheTest {
|
||||
int amount = 1024;
|
||||
Random random = new Random();
|
||||
|
||||
Cache<Integer, Integer> largeCache = new Cache<>(amount/2);
|
||||
Cache<Integer, Integer> largeCache = new Cache<>(amount / 2);
|
||||
|
||||
int[] values = new int[amount];
|
||||
for (int i = 0; i < amount; i++) {
|
||||
values[i] = random.nextInt();
|
||||
largeCache.setValue(i, values[i]);
|
||||
largeCache.getValue(0);
|
||||
largeCache.set(i, values[i]);
|
||||
largeCache.get(0);
|
||||
}
|
||||
|
||||
for (int i = 1; i < (amount/2) + 1; i++) {
|
||||
assertEquals(null, largeCache.getValue(i), "Current accessor: " + i);
|
||||
for (int i = 1; i < (amount / 2) + 1; i++) {
|
||||
assertEquals(null, largeCache.get(i), "Current accessor: " + i);
|
||||
}
|
||||
|
||||
for (int i = (amount /2) + 1; i < amount; i++) {
|
||||
assertEquals(values[i], largeCache.getValue(i), "Current accessor: " + i);
|
||||
for (int i = (amount / 2) + 1; i < amount; i++) {
|
||||
assertEquals(values[i], largeCache.get(i), "Current accessor: " + i);
|
||||
}
|
||||
|
||||
assertEquals(values[0], largeCache.getValue(0));
|
||||
assertEquals(values[0], largeCache.get(0));
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultithreadingWriteConsistency() {
|
||||
int size = 16384;
|
||||
int[] answers = new int[size];
|
||||
Random rand = new Random();
|
||||
|
||||
for (int i = 0; i < answers.length; i++) {
|
||||
answers[i] = rand.nextInt();
|
||||
}
|
||||
Cache<Integer, Integer> lCache = new Cache<>(size);
|
||||
Runnable write = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < size; i++) {
|
||||
lCache.set(i, answers[i]);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Thread firstThread = new Thread(write);
|
||||
firstThread.start();
|
||||
Thread secondThread = new Thread(write);
|
||||
secondThread.start();
|
||||
Thread thirdThread = new Thread(write);
|
||||
thirdThread.start();
|
||||
|
||||
try {
|
||||
firstThread.join();
|
||||
secondThread.join();
|
||||
thirdThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
assertFalse(false, e.getCause().getMessage());
|
||||
}
|
||||
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertEquals(answers[i], lCache.get(i), "Accessor at: " + i);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultithreadingReadConsistency() {
|
||||
int size = 16384;
|
||||
Cache<Integer, Integer> lCache = new Cache<>(size);
|
||||
int[] answers = new int[size];
|
||||
Random rand = new Random();
|
||||
|
||||
for (int i = 0; i < answers.length; i++) {
|
||||
answers[i] = rand.nextInt();
|
||||
lCache.set(i, answers[i]);
|
||||
}
|
||||
|
||||
Runnable read = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertEquals(answers[i], lCache.get(i), "Accessor at: " + i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Thread firstThread = new Thread(read);
|
||||
firstThread.start();
|
||||
Thread secondThread = new Thread(read);
|
||||
secondThread.start();
|
||||
Thread thirdThread = new Thread(read);
|
||||
thirdThread.start();
|
||||
|
||||
try {
|
||||
firstThread.join();
|
||||
secondThread.join();
|
||||
thirdThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
assertFalse(false, e.getCause().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMulthreadedReadWrite() {
|
||||
int size = 16384;
|
||||
Cache<Integer, Integer> lCache = new Cache<>(size);
|
||||
int[] answers = new int[size];
|
||||
Random rand = new Random();
|
||||
|
||||
for (int i = 0; i < answers.length; i++) {
|
||||
answers[i] = rand.nextInt();
|
||||
lCache.set(i, answers[i]);
|
||||
}
|
||||
|
||||
Runnable readWrite = new Runnable() {
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < size / 2; i++) {
|
||||
if (lCache.get(i) == null) {
|
||||
lCache.set(i, answers[i]);
|
||||
} else {
|
||||
assertEquals(answers[i], lCache.get(i), "Accessor at: " + i);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Runnable readAll = new Runnable() {
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < size; i++) {
|
||||
assertEquals(answers[i], lCache.get(i), "Accessor at: " + i);
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
Thread firstThread = new Thread(readWrite);
|
||||
firstThread.start();
|
||||
Thread secondThread = new Thread(readWrite);
|
||||
secondThread.start();
|
||||
Thread thirdThread = new Thread(readAll);
|
||||
|
||||
try {
|
||||
firstThread.join();
|
||||
secondThread.join();
|
||||
thirdThread.start();
|
||||
thirdThread.join();
|
||||
} catch (InterruptedException e) {
|
||||
assertFalse(false, e.getCause().getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testMultiThreadConsistency() {
|
||||
int size = 51200;
|
||||
Cache<Integer, Integer> lCache = new Cache<>(size / 2);
|
||||
int[] answers = new int[size];
|
||||
Random rand = new Random();
|
||||
ConcurrentHashMap<Integer, Integer> expectedStoredValues = new ConcurrentHashMap<>();
|
||||
|
||||
for (int i = 0; i < answers.length; i++) {
|
||||
answers[i] = rand.nextInt();
|
||||
lCache.set(i, answers[i]);
|
||||
}
|
||||
|
||||
Runnable readWriteCheck = new Runnable(){
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (int i = 0; i < size; i++) {
|
||||
if (lCache.get(i) != null) {
|
||||
assertEquals(answers[i], lCache.get(i), "Accessor at: " + i);
|
||||
} else {
|
||||
lCache.set(i, answers[i]);
|
||||
expectedStoredValues.put(i, answers[i]);
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
|
||||
ExecutorService executorService = Executors.newFixedThreadPool(6);
|
||||
executorService.submit(readWriteCheck);
|
||||
executorService.submit(readWriteCheck);
|
||||
executorService.submit(readWriteCheck);
|
||||
executorService.submit(readWriteCheck);
|
||||
executorService.submit(readWriteCheck);
|
||||
executorService.submit(readWriteCheck);
|
||||
|
||||
try {
|
||||
executorService.shutdown();
|
||||
assertTrue(executorService.awaitTermination(1, TimeUnit.MINUTES), "Timed out.");
|
||||
} catch (InterruptedException e) {
|
||||
assertFalse(false, e.getCause().getMessage());
|
||||
}
|
||||
}
|
||||
}
|
@@ -13,7 +13,7 @@ import org.junit.jupiter.api.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.types.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.datatypes.Point2;
|
||||
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
public class FloodFillTest {
|
||||
|
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,109 @@
|
||||
package ca.recrown.islandsurvivalcraft.world.generation;
|
||||
|
||||
import static org.junit.jupiter.api.Assertions.assertFalse;
|
||||
import static org.junit.jupiter.api.Assertions.assertTrue;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.concurrent.ExecutorService;
|
||||
import java.util.concurrent.Executors;
|
||||
import java.util.concurrent.TimeUnit;
|
||||
|
||||
import org.bukkit.block.Biome;
|
||||
import org.junit.Test;
|
||||
import org.junit.jupiter.api.TestInstance;
|
||||
import org.junit.jupiter.api.TestInstance.Lifecycle;
|
||||
|
||||
import ca.recrown.islandsurvivalcraft.Utilities;
|
||||
import ca.recrown.islandsurvivalcraft.caching.Cache;
|
||||
import ca.recrown.islandsurvivalcraft.datatypes.Point2;
|
||||
import ca.recrown.islandsurvivalcraft.world.BiomeSelector;
|
||||
import ca.recrown.islandsurvivalcraft.world.IslandWorldMapper;
|
||||
|
||||
@TestInstance(Lifecycle.PER_CLASS)
|
||||
public class UniBiomeIslandGeneratorTest {
|
||||
private final int SEED = 249398015;
|
||||
private final DummyWorld dummyWorld = new DummyWorld();
|
||||
private final Cache<Point2, Double> blockValueCache = new Cache<>(102400);
|
||||
private final Cache<Point2, Biome> biomeCache = new Cache<>(102400);
|
||||
private final Cache<Point2, Boolean> chunkExistenceCache = new Cache<>(16384);
|
||||
private final Random rand = new Random(SEED);
|
||||
|
||||
private class BiomeGenTask implements Runnable {
|
||||
private final int amount;
|
||||
private final int shift;
|
||||
|
||||
public BiomeGenTask(int amount, int shift) {
|
||||
this.shift = shift;
|
||||
this.amount = amount;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void run() {
|
||||
for (int x = 0; x < amount; x++) {
|
||||
generateBiome(x, shift);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public void generateBiome(int chunkX, int chunkZ) {
|
||||
IslandWorldMapper mapper = new IslandWorldMapper(rand, blockValueCache);
|
||||
TemperatureMapGenerator temperatureMapGenerator = new TemperatureMapGenerator(SEED);
|
||||
BiomeSelector biomeSelector = new BiomeSelector(rand);
|
||||
biomeSelector.initialize();
|
||||
BiomeGenerator biomeGenerator = new UniBiomeIslandGenerator();
|
||||
|
||||
Biome[][] biomes = new Biome[Utilities.CHUNK_SIZE][Utilities.CHUNK_SIZE];
|
||||
for (int localX = 0; localX < Utilities.CHUNK_SIZE; localX++) {
|
||||
for (int localZ = 0; localZ < Utilities.CHUNK_SIZE; localZ++) {
|
||||
if (biomes[localX][localZ] == null) {
|
||||
biomeGenerator.generateBiomeColumn(biomes, dummyWorld, chunkX, chunkZ, localX, localZ, mapper,
|
||||
biomeSelector, temperatureMapGenerator, biomeCache, chunkExistenceCache);
|
||||
}
|
||||
if (biomes[localX][localZ] == null)
|
||||
throw new IllegalStateException("Biome was null.");
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBiomeGenerationMultithread() {
|
||||
int chunksToDo = 280;
|
||||
Runnable g1 = new BiomeGenTask(chunksToDo, 0);
|
||||
Runnable g2 = new BiomeGenTask(chunksToDo, 1);
|
||||
Runnable g3 = new BiomeGenTask(chunksToDo, 2);
|
||||
Runnable g4 = new BiomeGenTask(chunksToDo, 3);
|
||||
Runnable g5 = new BiomeGenTask(chunksToDo, 4);
|
||||
Runnable g6 = new BiomeGenTask(chunksToDo, 5);
|
||||
|
||||
ExecutorService ex = Executors.newFixedThreadPool(6);
|
||||
ex.execute(g1);
|
||||
ex.execute(g2);
|
||||
ex.execute(g3);
|
||||
ex.execute(g4);
|
||||
ex.execute(g5);
|
||||
ex.execute(g6);
|
||||
|
||||
try {
|
||||
ex.shutdown();
|
||||
assertTrue(ex.awaitTermination(1, TimeUnit.MINUTES), "timed out.");
|
||||
} catch (InterruptedException e) {
|
||||
assertFalse(false);
|
||||
}
|
||||
}
|
||||
|
||||
@Test
|
||||
public void testBiomeGenerationSingleThread() {
|
||||
Runnable g1 = new BiomeGenTask(1680, 0);
|
||||
|
||||
ExecutorService ex = Executors.newFixedThreadPool(6);
|
||||
ex.execute(g1);
|
||||
|
||||
try {
|
||||
ex.shutdown();
|
||||
assertTrue(ex.awaitTermination(1, TimeUnit.MINUTES));
|
||||
} catch (InterruptedException e) {
|
||||
assertFalse(false, e.getCause().getMessage());
|
||||
}
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user