|
1 | 1 | package de.peeeq.wurstscript.validation; |
2 | 2 |
|
3 | 3 | import de.peeeq.wurstscript.ast.Element; |
4 | | -import de.peeeq.wurstscript.attributes.names.NameResolution; |
5 | 4 | import de.peeeq.wurstscript.intermediatelang.ILconst; |
6 | 5 | import de.peeeq.wurstscript.intermediatelang.interpreter.LocalState; |
7 | | -import de.peeeq.wurstscript.types.WurstType; |
8 | 6 | import it.unimi.dsi.fastutil.objects.Object2ObjectOpenHashMap; |
9 | | -import it.unimi.dsi.fastutil.objects.Reference2BooleanOpenHashMap; |
10 | | -import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; |
11 | 7 |
|
12 | 8 | import java.util.Arrays; |
13 | 9 | import java.util.Map; |
14 | | -import java.util.WeakHashMap; |
| 10 | +import java.util.concurrent.atomic.AtomicLong; |
15 | 11 |
|
16 | 12 | // Expose static fields only if you already have them there; otherwise, just clear via dedicated methods. |
17 | 13 | public final class GlobalCaches { |
| 14 | + |
| 15 | + // Statistics tracking |
| 16 | + public static class CacheStats { |
| 17 | + final AtomicLong hits = new AtomicLong(); |
| 18 | + final AtomicLong misses = new AtomicLong(); |
| 19 | + final AtomicLong evictions = new AtomicLong(); |
| 20 | + final String name; |
| 21 | + |
| 22 | + CacheStats(String name) { |
| 23 | + this.name = name; |
| 24 | + } |
| 25 | + |
| 26 | + void recordHit() { |
| 27 | + hits.incrementAndGet(); |
| 28 | + } |
| 29 | + |
| 30 | + void recordMiss() { |
| 31 | + misses.incrementAndGet(); |
| 32 | + } |
| 33 | + |
| 34 | + void recordEviction(int count) { |
| 35 | + evictions.addAndGet(count); |
| 36 | + } |
| 37 | + |
| 38 | + double hitRate() { |
| 39 | + long h = hits.get(); |
| 40 | + long m = misses.get(); |
| 41 | + long total = h + m; |
| 42 | + return total == 0 ? 0.0 : (double) h / total; |
| 43 | + } |
| 44 | + |
| 45 | + @Override |
| 46 | + public String toString() { |
| 47 | + return String.format("%s: hits=%d, misses=%d, hitRate=%.2f%%, evictions=%d", |
| 48 | + name, hits.get(), misses.get(), hitRate() * 100, evictions.get()); |
| 49 | + } |
| 50 | + } |
| 51 | + |
| 52 | + private static final CacheStats lookupStats = new CacheStats("LookupCache"); |
| 53 | + private static final CacheStats localStateStats = new CacheStats("LocalStateCache"); |
| 54 | + |
18 | 55 | // Optimized ArgumentKey that minimizes allocation overhead |
19 | 56 | public static final class ArgumentKey { |
20 | 57 | private final ILconst[] args; |
@@ -48,21 +85,48 @@ public boolean equals(Object o) { |
48 | 85 | } |
49 | 86 | } |
50 | 87 |
|
51 | | - public enum Mode { TEST_ISOLATED, DEV_PERSISTENT } |
| 88 | + public enum Mode {TEST_ISOLATED, DEV_PERSISTENT} |
| 89 | + |
52 | 90 | private static volatile Mode mode = Mode.DEV_PERSISTENT; |
53 | 91 |
|
54 | | - public static void setMode(Mode m) { mode = m; } |
55 | | - public static Mode mode() { return mode; } |
| 92 | + public static void setMode(Mode m) { |
| 93 | + mode = m; |
| 94 | + } |
56 | 95 |
|
57 | | - private GlobalCaches() {} |
| 96 | + public static Mode mode() { |
| 97 | + return mode; |
| 98 | + } |
58 | 99 |
|
59 | | - public static final Object2ObjectOpenHashMap<Object, Object2ObjectOpenHashMap<ArgumentKey, LocalState>> LOCAL_STATE_CACHE = new Object2ObjectOpenHashMap<>(); |
60 | | - public static final Reference2ObjectOpenHashMap<WurstType, Reference2BooleanOpenHashMap<WurstType>> SUBTYPE_MEMO = new Reference2ObjectOpenHashMap<>(); |
| 100 | + private GlobalCaches() { |
| 101 | + } |
61 | 102 |
|
62 | | - /** Call this between tests (and after each compile) */ |
| 103 | + // Wrapped caches with statistics |
| 104 | + public static final Object2ObjectOpenHashMap<Object, Object2ObjectOpenHashMap<ArgumentKey, LocalState>> LOCAL_STATE_CACHE = |
| 105 | + new Object2ObjectOpenHashMap<Object, Object2ObjectOpenHashMap<ArgumentKey, LocalState>>() { |
| 106 | + @Override |
| 107 | + public Object2ObjectOpenHashMap<ArgumentKey, LocalState> get(Object key) { |
| 108 | + Object2ObjectOpenHashMap<ArgumentKey, LocalState> result = super.get(key); |
| 109 | + if (result != null) { |
| 110 | + localStateStats.recordHit(); |
| 111 | + } else { |
| 112 | + localStateStats.recordMiss(); |
| 113 | + } |
| 114 | + return result; |
| 115 | + } |
| 116 | + |
| 117 | + @Override |
| 118 | + public void clear() { |
| 119 | + localStateStats.recordEviction(size()); |
| 120 | + super.clear(); |
| 121 | + } |
| 122 | + }; |
| 123 | + |
| 124 | + |
| 125 | + /** |
| 126 | + * Call this between tests (and after each compile) |
| 127 | + */ |
63 | 128 | public static void clearAll() { |
64 | 129 | LOCAL_STATE_CACHE.clear(); |
65 | | - SUBTYPE_MEMO.clear(); |
66 | 130 | lookupCache.clear(); |
67 | 131 | } |
68 | 132 |
|
@@ -95,5 +159,59 @@ public int hashCode() { |
95 | 159 | } |
96 | 160 | } |
97 | 161 |
|
98 | | - public static final Map<CacheKey, Object> lookupCache = new Object2ObjectOpenHashMap<>(); |
| 162 | + public static final Map<CacheKey, Object> lookupCache = new Object2ObjectOpenHashMap<CacheKey, Object>() { |
| 163 | + @Override |
| 164 | + public Object get(Object key) { |
| 165 | + Object result = super.get(key); |
| 166 | + if (result != null) { |
| 167 | + lookupStats.recordHit(); |
| 168 | + } else { |
| 169 | + lookupStats.recordMiss(); |
| 170 | + } |
| 171 | + return result; |
| 172 | + } |
| 173 | + |
| 174 | + @Override |
| 175 | + public Object put(CacheKey key, Object value) { |
| 176 | + // Note: put returns old value, null if new entry |
| 177 | + Object old = super.put(key, value); |
| 178 | + if (old == null) { |
| 179 | + // New entry, the miss was already recorded in get() |
| 180 | + } |
| 181 | + return old; |
| 182 | + } |
| 183 | + |
| 184 | + @Override |
| 185 | + public void clear() { |
| 186 | + lookupStats.recordEviction(size()); |
| 187 | + super.clear(); |
| 188 | + } |
| 189 | + }; |
| 190 | + |
| 191 | + // Statistics methods |
| 192 | + public static void printStats() { |
| 193 | + System.out.println("=== GlobalCaches Statistics ==="); |
| 194 | + System.out.println(lookupStats); |
| 195 | + System.out.println(localStateStats); |
| 196 | + System.out.println("Current sizes: lookup=" + lookupCache.size() + |
| 197 | + ", localState=" + LOCAL_STATE_CACHE.size()); |
| 198 | + System.out.println("=============================="); |
| 199 | + } |
| 200 | + |
| 201 | + public static void resetStats() { |
| 202 | + lookupStats.hits.set(0); |
| 203 | + lookupStats.misses.set(0); |
| 204 | + lookupStats.evictions.set(0); |
| 205 | + localStateStats.hits.set(0); |
| 206 | + localStateStats.misses.set(0); |
| 207 | + localStateStats.evictions.set(0); |
| 208 | + } |
| 209 | + |
| 210 | + public static CacheStats getLookupStats() { |
| 211 | + return lookupStats; |
| 212 | + } |
| 213 | + |
| 214 | + public static CacheStats getLocalStateStats() { |
| 215 | + return localStateStats; |
| 216 | + } |
99 | 217 | } |
0 commit comments