Lock-free Terrain3D caches

- Replace synchronized move-to-last LRU stripes with chunk-block CLOCK cache
- Copy-on-write compiled profile map, lock only on miss
- Raise column cache to 2048 chunks
This commit is contained in:
Brian Neumann-Fopiano committed 2026-09-28 11:06:34 -04:00
1 parent 241260a762
commit 631c486a5a
5 files changed
+285 -86

No files matched your search

@@ -0,0 +1,84 @@
package art.arcane.iris.generation.cache;
import org.junit.Test;
import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutorService;
import java.util.concurrent.Executors;
import java.util.concurrent.Future;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicInteger;
import static org.junit.Assert.assertEquals;
import static org.junit.Assert.assertNull;
import static org.junit.Assert.assertSame;
import static org.junit.Assert.assertTrue;
public class ConcurrentClockCacheTest {
@Test
public void firstPublishedValueWins() {
ConcurrentClockCache<Object> cache = new ConcurrentClockCache<>(16);
Object first = new Object();
assertNull(cache.get(7L));
assertSame(first, cache.putIfAbsent(7L, first));
assertSame(first, cache.putIfAbsent(7L, new Object()));
assertSame(first, cache.get(7L));
cache.clear();
assertNull(cache.get(7L));
}
@Test
public void capacityBoundsRetainedEntries() {
ConcurrentClockCache<Long> cache = new ConcurrentClockCache<>(64);
for (long key = 0; key < 10_000; key++) {
assertEquals(Long.valueOf(key), cache.putIfAbsent(key, key));
}
AtomicInteger retained = new AtomicInteger();
cache.forEach(value -> {
retained.incrementAndGet();
assertEquals(value, cache.get(value));
});
assertEquals(cache.capacity(), retained.get());
assertTrue(cache.capacity() >= 64);
}
@Test
public void referencedEntriesSurviveAStreamOfOneTimeKeys() {
ConcurrentClockCache<Long> cache = new ConcurrentClockCache<>(4);
cache.putIfAbsent(-1L, -1L);
for (long key = 0; key < 1_000; key++) {
assertEquals(Long.valueOf(-1L), cache.get(-1L));
cache.putIfAbsent(key, key);
}
assertEquals(Long.valueOf(-1L), cache.get(-1L));
}
@Test
public void concurrentWritersAgreeOnOneValuePerKey() throws Exception {
ConcurrentClockCache<Object> cache = new ConcurrentClockCache<>(1 << 12);
List<Future<Object[]>> results = new ArrayList<>();
try (ExecutorService executor = Executors.newFixedThreadPool(8)) {
for (int thread = 0; thread < 8; thread++) {
results.add(executor.submit(() -> {
Object[] seen = new Object[256];
for (int key = 0; key < seen.length; key++) {
seen[key] = cache.putIfAbsent(key, new Object());
}
return seen;
}));
}
Object[] reference = results.getFirst().get(10, TimeUnit.SECONDS);
for (Future<Object[]> result : results) {
Object[] seen = result.get(10, TimeUnit.SECONDS);
for (int key = 0; key < seen.length; key++) {
Object cached = cache.get(key);
if (cached != null) {
assertSame(cached, seen[key]);
assertSame(reference[key], seen[key]);
}
}
}
}
}
}
@@ -302,7 +302,7 @@ public class Terrain3DRuntimeTest {
assertEquals(0, capacity % 16);
assertTrue(capacity >= 16);
assertTrue(capacity <= 32_768);
assertTrue(capacity * fullHeightStorage <= 64L * 1024 * 1024);
assertTrue(capacity * fullHeightStorage <= 128L * 1024 * 1024);
}
}
}
@@ -354,9 +354,10 @@ public class Terrain3DRuntimeTest {
for (int x = -65_536; x < 65_536; x++) {
assertEquals(96, runtime.column(x, -1).topY());
}
assertEquals(65_536, runtime.cachedColumnCount());
assertTrue(runtime.cachedColumnCount() > 0);
assertTrue(runtime.cachedColumnCount() <= 65_536);
assertEquals(96, runtime.column(-65_536, -1).topY());
assertEquals(65_536, runtime.cachedColumnCount());
assertTrue(runtime.cachedColumnCount() <= 65_536);
runtime.clear();
assertEquals(0, runtime.cachedColumnCount());
}