Skip to content

Commit c6fb0be

Browse files
committed
Tinker with performance on some base data classes
1 parent 5a30ab0 commit c6fb0be

File tree

8 files changed

+172
-119
lines changed

8 files changed

+172
-119
lines changed

DynmapCore/src/main/java/org/dynmap/Color.java

Lines changed: 36 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -38,12 +38,13 @@ public final void setTransparent() {
3838
val = TRANSPARENT;
3939
}
4040
public final void setGrayscale() {
41-
int alpha = (val >> 24) & 0xFF;
42-
int red = (val >> 16) & 0xFF;
43-
int green = (val >> 8) & 0xFF;
44-
int blue = val & 0xFF;
45-
int gray = ((red * 76) + (green * 151) + (blue * 28)) / 255;
46-
setRGBA(gray, gray, gray, alpha);
41+
int alpha = val & 0xFF000000;
42+
int num = (((val >> 16) & 0xFF) * 76)
43+
+ (((val >> 8) & 0xFF) * 151)
44+
+ (( val & 0xFF) * 28);
45+
// weights sum to 255, so num ∈ [0, 65025]; fast /255 via shift
46+
int gray = (num + (num >> 8) + 1) >> 8;
47+
val = alpha | (gray << 16) | (gray << 8) | gray;
4748
}
4849
public final void setColor(Color c) {
4950
val = c.val;
@@ -85,11 +86,10 @@ public final void blendColor(Color c) {
8586
* @param argb - ARGB to blend
8687
*/
8788
public final void blendColor(int argb) {
88-
int nval = (((((val >> 24) & 0xFF) * ((argb >> 24) & 0xFF)) / 255) << 24);
89-
nval = nval | (((((val >> 16) & 0xFF) * ((argb >> 16) & 0xFF)) / 255) << 16);
90-
nval = nval | (((((val >> 8) & 0xFF) * ((argb >> 8) & 0xFF)) / 255) << 8);
91-
nval = nval | (((val & 0xFF) * (argb & 0xFF)) / 255);
92-
val = nval;
89+
val = (mulDiv255(val >>> 24, argb >>> 24 ) << 24)
90+
| (mulDiv255((val >> 16) & 0xFF, (argb >> 16) & 0xFF) << 16)
91+
| (mulDiv255((val >> 8) & 0xFF, (argb >> 8) & 0xFF) << 8)
92+
| mulDiv255( val & 0xFF, argb & 0xFF);
9393
}
9494
/**
9595
* Scale each color component, based on the corresponding component
@@ -98,10 +98,30 @@ public final void blendColor(int argb) {
9898
* @return blended color
9999
*/
100100
public static final int blendColor(int argb0, int argb1) {
101-
int nval = (((((argb0 >> 24) & 0xFF) * ((argb1 >> 24) & 0xFF)) / 255) << 24);
102-
nval = nval | (((((argb0 >> 16) & 0xFF) * ((argb1 >> 16) & 0xFF)) / 255) << 16);
103-
nval = nval | (((((argb0 >> 8) & 0xFF) * ((argb1 >> 8) & 0xFF)) / 255) << 8);
104-
nval = nval | (((argb0 & 0xFF) * (argb1 & 0xFF)) / 255);
105-
return nval;
101+
return (mulDiv255(argb0 >>> 24, argb1 >>> 24 ) << 24)
102+
| (mulDiv255((argb0 >> 16) & 0xFF, (argb1 >> 16) & 0xFF) << 16)
103+
| (mulDiv255((argb0 >> 8) & 0xFF, (argb1 >> 8) & 0xFF) << 8)
104+
| mulDiv255( argb0 & 0xFF, argb1 & 0xFF);
105+
}
106+
/**
107+
* Scale the RGB channels by scale/256, leaving alpha unchanged.
108+
* Equivalent to setRGBA(getRed()*scale>>8, getGreen()*scale>>8, getBlue()*scale>>8, getAlpha())
109+
* but avoids redundant unpack/repack of the alpha channel.
110+
* @param scale - scale factor 0..256 (256 = full brightness)
111+
*/
112+
public final void scaleRGB(int scale) {
113+
val = (val & 0xFF000000)
114+
| ((((val >> 16) & 0xFF) * scale >> 8) << 16)
115+
| ((((val >> 8) & 0xFF) * scale >> 8) << 8)
116+
| ((val & 0xFF) * scale >> 8);
117+
}
118+
/**
119+
* Fast multiply-then-divide-by-255 for two values a, b each in [0, 255].
120+
* Returns floor(a*b/255), equivalent to the standard integer division but
121+
* computed with shifts only: (x + (x >> 8) + 1) >> 8 where x = a * b.
122+
*/
123+
private static int mulDiv255(int a, int b) {
124+
int x = a * b;
125+
return (x + (x >> 8) + 1) >> 8;
106126
}
107127
}

DynmapCore/src/main/java/org/dynmap/common/BiomeMap.java

Lines changed: 27 additions & 24 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@
22

33
import java.util.Arrays;
44
import java.util.HashMap;
5+
import java.util.HashSet;
56
import java.util.Map;
67
import java.util.Optional;
78

@@ -11,7 +12,9 @@
1112
public class BiomeMap {
1213
public static final int NO_INDEX = -2;
1314
private static BiomeMap[] biome_by_index = new BiomeMap[256];
14-
private static Map<String, BiomeMap> biome_by_rl = new HashMap<String, BiomeMap>();
15+
private static Map<String, BiomeMap> biome_by_rl = new HashMap<String, BiomeMap>(256);
16+
// Tracks registered IDs for O(1) uniqueness checks during initialization
17+
private static final HashSet<String> biome_ids = new HashSet<String>(256);
1518
public static final BiomeMap NULL = new BiomeMap(-1, "NULL", 0.5, 0.5, 0xFFFFFF, 0, 0, null);
1619

1720
public static final BiomeMap OCEAN = new BiomeMap(0, "OCEAN", "minecraft:ocean");
@@ -145,12 +148,17 @@ public static void loadWellKnownByVersion(String mcver) {
145148
}
146149

147150
private static boolean isUniqueID(String id) {
148-
for(int i = 0; i < biome_by_index.length; i++) {
149-
if(biome_by_index[i] == null) continue;
150-
if(biome_by_index[i].id.equals(id))
151-
return false;
152-
}
153-
return true;
151+
return !biome_ids.contains(id);
152+
}
153+
154+
/**
155+
* Encodes a grass/foliage color multiplier for efficient hot-path dispatch:
156+
* == 0 → 0 (passthrough: return raw value unchanged)
157+
* 0 < val ≤ 0xFFFFFF → positive (blend: average with raw)
158+
* val > 0xFFFFFF → -(val & 0xFFFFFF) (negative sentinel: fixed override color)
159+
*/
160+
private static int encodeColorMult(int val) {
161+
return (val > 0xFFFFFF) ? -(val & 0xFFFFFF) : val;
154162
}
155163

156164
private static void resizeIfNeeded(int idx) {
@@ -171,15 +179,16 @@ private BiomeMap(int idx, String id, double tmp, double rain, int waterColorMult
171179
setTemperature(tmp);
172180
setRainfall(rain);
173181
this.watercolormult = waterColorMultiplier;
174-
this.grassmult = grassmult;
175-
this.foliagemult = foliagemult;
182+
this.grassmult = encodeColorMult(grassmult);
183+
this.foliagemult = encodeColorMult(foliagemult);
176184
// Handle null biome
177185
if (id == null) { id = "biome_" + idx; }
178186
id = id.toUpperCase().replace(' ', '_');
179187
if (isUniqueID(id) == false) {
180188
id = id + "_" + idx;
181189
}
182190
this.id = id;
191+
biome_ids.add(this.id);
183192
// If index is NO_INDEX, find one after the well known ones
184193
if (idx == NO_INDEX) {
185194
idx = LAST_WELL_KNOWN;
@@ -235,21 +244,15 @@ public final int biomeLookup() {
235244
}
236245

237246
public final int getModifiedGrassMultiplier(int rawgrassmult) {
238-
if(grassmult == 0)
239-
return rawgrassmult;
240-
else if(grassmult > 0xFFFFFF)
241-
return grassmult & 0xFFFFFF;
242-
else
243-
return ((rawgrassmult & 0xfefefe) + grassmult) / 2;
247+
if (grassmult == 0) return rawgrassmult; // common case: no override
248+
if (grassmult < 0) return -grassmult; // fixed color (pre-masked at set-time)
249+
return ((rawgrassmult & 0xfefefe) + grassmult) >> 1; // blend
244250
}
245-
251+
246252
public final int getModifiedFoliageMultiplier(int rawfoliagemult) {
247-
if(foliagemult == 0)
248-
return rawfoliagemult;
249-
else if(foliagemult > 0xFFFFFF)
250-
return foliagemult & 0xFFFFFF;
251-
else
252-
return ((rawfoliagemult & 0xfefefe) + foliagemult) / 2;
253+
if (foliagemult == 0) return rawfoliagemult; // common case: no override
254+
if (foliagemult < 0) return -foliagemult; // fixed color (pre-masked at set-time)
255+
return ((rawfoliagemult & 0xfefefe) + foliagemult) >> 1; // blend
253256
}
254257
public final int getWaterColorMult() {
255258
return watercolormult;
@@ -278,10 +281,10 @@ public void setWaterColorMultiplier(int watercolormult) {
278281
this.watercolormult = watercolormult;
279282
}
280283
public void setGrassColorMultiplier(int grassmult) {
281-
this.grassmult = grassmult;
284+
this.grassmult = encodeColorMult(grassmult);
282285
}
283286
public void setFoliageColorMultiplier(int foliagemult) {
284-
this.foliagemult = foliagemult;
287+
this.foliagemult = encodeColorMult(foliagemult);
285288
}
286289
public void setTemperature(double tmp) {
287290
if(tmp < 0.0) tmp = 0.0;

DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunk.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -38,7 +38,7 @@ private GenericChunk(int cx, int cz, int cy_min, GenericChunkSection[] sections,
3838
// Get section for given block Y coord
3939
public final GenericChunkSection getSection(int y) {
4040
int idx = (y >> 4) - this.cy_min;
41-
if ((idx < 0) || (idx >= sectionCnt)) {
41+
if ((idx < 0) || (idx >= sections.length)) {
4242
return GenericChunkSection.EMPTY;
4343
}
4444
return this.sections[idx];

DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkCache.java

Lines changed: 32 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -4,13 +4,14 @@
44
import java.lang.ref.ReferenceQueue;
55
import java.lang.ref.WeakReference;
66
import java.lang.ref.SoftReference;
7+
import java.util.HashMap;
78
import java.util.IdentityHashMap;
89
import java.util.LinkedHashMap;
910
import java.util.Map;
1011

1112
import org.dynmap.utils.DynIntHashMap;
1213

13-
// Generic chunk cache
14+
// Generic chunk cache
1415
public class GenericChunkCache {
1516
public static class ChunkCacheRec {
1617
public GenericChunk ss;
@@ -23,22 +24,26 @@ public static class ChunkCacheRec {
2324
private long cache_attempts;
2425
private long cache_success;
2526
private boolean softref;
27+
// World name -> small integer ID, used to build long cache keys without String concatenation.
28+
// Accessed only while holding snapcachelock.
29+
private final HashMap<String, Integer> worldIds = new HashMap<String, Integer>();
30+
private int nextWorldId = 0;
2631

2732
private static class CacheRec {
2833
Reference<ChunkCacheRec> ref;
2934
}
30-
35+
3136
@SuppressWarnings("serial")
32-
public class CacheHashMap extends LinkedHashMap<String, CacheRec> {
37+
public class CacheHashMap extends LinkedHashMap<Long, CacheRec> {
3338
private int limit;
34-
private IdentityHashMap<Reference<ChunkCacheRec>, String> reverselookup;
39+
private IdentityHashMap<Reference<ChunkCacheRec>, Long> reverselookup;
3540

3641
public CacheHashMap(int lim) {
3742
super(16, (float)0.75, true);
3843
limit = lim;
39-
reverselookup = new IdentityHashMap<Reference<ChunkCacheRec>, String>();
44+
reverselookup = new IdentityHashMap<Reference<ChunkCacheRec>, Long>();
4045
}
41-
protected boolean removeEldestEntry(Map.Entry<String, CacheRec> last) {
46+
protected boolean removeEldestEntry(Map.Entry<Long, CacheRec> last) {
4247
boolean remove = (size() >= limit);
4348
if(remove && (last != null) && (last.getValue() != null)) {
4449
reverselookup.remove(last.getValue().ref);
@@ -56,15 +61,26 @@ public GenericChunkCache(int max_size, boolean softref) {
5661
refqueue = new ReferenceQueue<ChunkCacheRec>();
5762
this.softref = softref;
5863
}
59-
private String getKey(String w, int cx, int cz) {
60-
return w + ":" + cx + ":" + cz;
64+
/**
65+
* Encode world name + chunk coords as a single long key.
66+
* Worlds are assigned small integer IDs (10 bits) on first use.
67+
* cx and cz are each encoded in 27 bits (signed, supports ±8M chunks / ±128M blocks).
68+
* Must be called while holding snapcachelock.
69+
*/
70+
private long getKey(String w, int cx, int cz) {
71+
Integer wid = worldIds.get(w);
72+
if (wid == null) {
73+
wid = nextWorldId++;
74+
worldIds.put(w, wid);
75+
}
76+
return ((long)(wid & 0x3FF) << 54) | ((long)(cx & 0x7FFFFFF) << 27) | (long)(cz & 0x7FFFFFF);
6177
}
6278
/**
6379
* Invalidate cached snapshot, if in cache
6480
*/
6581
public void invalidateSnapshot(String w, int x, int y, int z) {
66-
String key = getKey(w, x>>4, z>>4);
6782
synchronized(snapcachelock) {
83+
long key = getKey(w, x>>4, z>>4);
6884
CacheRec rec = (snapcache != null) ? snapcache.remove(key) : null;
6985
if(rec != null) {
7086
snapcache.reverselookup.remove(rec.ref);
@@ -77,10 +93,10 @@ public void invalidateSnapshot(String w, int x, int y, int z) {
7793
* Invalidate cached snapshot, if in cache
7894
*/
7995
public void invalidateSnapshot(String w, int x0, int y0, int z0, int x1, int y1, int z1) {
80-
for(int xx = (x0>>4); xx <= (x1>>4); xx++) {
81-
for(int zz = (z0>>4); zz <= (z1>>4); zz++) {
82-
String key = getKey(w, xx, zz);
83-
synchronized(snapcachelock) {
96+
synchronized(snapcachelock) {
97+
for(int xx = (x0>>4); xx <= (x1>>4); xx++) {
98+
for(int zz = (z0>>4); zz <= (z1>>4); zz++) {
99+
long key = getKey(w, xx, zz);
84100
CacheRec rec = (snapcache != null) ? snapcache.remove(key) : null;
85101
if(rec != null) {
86102
snapcache.reverselookup.remove(rec.ref);
@@ -95,11 +111,11 @@ public void invalidateSnapshot(String w, int x0, int y0, int z0, int x1, int y1,
95111
* Look for chunk snapshot in cache
96112
*/
97113
public ChunkCacheRec getSnapshot(String w, int chunkx, int chunkz) {
98-
String key = getKey(w, chunkx, chunkz);
99114
processRefQueue();
100115
ChunkCacheRec ss = null;
101116
CacheRec rec;
102117
synchronized(snapcachelock) {
118+
long key = getKey(w, chunkx, chunkz);
103119
rec = (snapcache != null) ? snapcache.get(key) : null;
104120
if(rec != null) {
105121
ss = rec.ref.get();
@@ -118,14 +134,14 @@ public ChunkCacheRec getSnapshot(String w, int chunkx, int chunkz) {
118134
* Add chunk snapshot to cache
119135
*/
120136
public void putSnapshot(String w, int chunkx, int chunkz, ChunkCacheRec ss) {
121-
String key = getKey(w, chunkx, chunkz);
122137
processRefQueue();
123138
CacheRec rec = new CacheRec();
124139
if (softref)
125140
rec.ref = new SoftReference<ChunkCacheRec>(ss, refqueue);
126141
else
127142
rec.ref = new WeakReference<ChunkCacheRec>(ss, refqueue);
128143
synchronized(snapcachelock) {
144+
long key = getKey(w, chunkx, chunkz);
129145
CacheRec prevrec = (snapcache != null) ? snapcache.put(key, rec) : null;
130146
if(prevrec != null) {
131147
snapcache.reverselookup.remove(prevrec.ref);
@@ -140,7 +156,7 @@ private void processRefQueue() {
140156
Reference<? extends ChunkCacheRec> ref;
141157
while((ref = refqueue.poll()) != null) {
142158
synchronized(snapcachelock) {
143-
String k = (snapcache != null) ? snapcache.reverselookup.remove(ref) : null;
159+
Long k = (snapcache != null) ? snapcache.reverselookup.remove(ref) : null;
144160
if(k != null) {
145161
snapcache.remove(k);
146162
}

DynmapCore/src/main/java/org/dynmap/common/chunk/GenericChunkSection.java

Lines changed: 4 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -25,7 +25,7 @@ private static class BlockStateAccess3D implements BlockStateAccess {
2525
blocks = bs;
2626
}
2727
public final DynmapBlockState getBlock(int x, int y, int z) {
28-
return blocks[(256 * (y & 0xF)) + (16 * (z & 0xF)) + (x & 0xF)];
28+
return blocks[((y & 0xF) << 8) | ((z & 0xF) << 4) | (x & 0xF)];
2929
}
3030
public final DynmapBlockState getBlock(GenericChunkPos pos) {
3131
return blocks[pos.soffset];
@@ -40,7 +40,7 @@ private static class BlockStateAccess3DPalette implements BlockStateAccess {
4040
palette = pal;
4141
}
4242
public final DynmapBlockState getBlock(int x, int y, int z) {
43-
return palette[blocks[(256 * (y & 0xF)) + (16 * (z & 0xF)) + (x & 0xF)]];
43+
return palette[blocks[((y & 0xF) << 8) | ((z & 0xF) << 4) | (x & 0xF)]];
4444
}
4545
public final DynmapBlockState getBlock(GenericChunkPos pos) {
4646
return palette[blocks[pos.soffset]];
@@ -126,12 +126,12 @@ private static class LightingAccess3D implements LightingAccess {
126126
light = new long[256];
127127
if (lig != null) {
128128
for (int off = 0; (off < lig.length) && (off < 2048); off++) {
129-
light[off >> 3] |= (0xFFL & (long)lig[off]) << (8 * (off & 0x7));
129+
light[off >> 3] |= (0xFFL & (long)lig[off]) << ((off & 0x7) << 3);
130130
}
131131
}
132132
}
133133
public final int getLight(int x, int y, int z) {
134-
return 0xF & (int)(light[(16 * (y & 0xF)) + (z & 0xF)] >> (4 * (x & 0xF)));
134+
return 0xF & (int)(light[((y & 0xF) << 4) | (z & 0xF)] >> ((x & 0xF) << 2));
135135
}
136136
public final int getLight(GenericChunkPos pos) {
137137
return 0xF & (int)(light[pos.soffset >> 4] >> (4 * pos.sx));

DynmapCore/src/main/java/org/dynmap/hdmap/ShadowHDLighting.java

Lines changed: 3 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -154,9 +154,7 @@ else if(weight < 0) { /* If negative, interpolate down */
154154
}
155155
}
156156
if(cscale < 256) {
157-
Color c = outcolor[0];
158-
c.setRGBA((c.getRed() * cscale) >> 8, (c.getGreen() * cscale) >> 8,
159-
(c.getBlue() * cscale) >> 8, c.getAlpha());
157+
outcolor[0].scaleRGB(cscale);
160158
}
161159
if(outcolor.length > 1) {
162160
ll0 = getLightLevel(skyemit0, false);
@@ -194,9 +192,7 @@ else if(weight < 0) { /* If negative, interpolate down */
194192
}
195193
}
196194
if(cscale < 256) {
197-
Color c = outcolor[1];
198-
c.setRGBA((c.getRed() * cscale) >> 8, (c.getGreen() * cscale) >> 8,
199-
(c.getBlue() * cscale) >> 8, c.getAlpha());
195+
outcolor[1].scaleRGB(cscale);
200196
}
201197
}
202198
}
@@ -269,8 +265,7 @@ public void applyLighting(HDPerspectiveState ps, HDShaderState ss, Color inco
269265
private final void shadowColor(Color c, int lightlevel, int[] shadowscale) {
270266
int scale = shadowscale[lightlevel];
271267
if(scale < 256)
272-
c.setRGBA((c.getRed() * scale) >> 8, (c.getGreen() * scale) >> 8,
273-
(c.getBlue() * scale) >> 8, c.getAlpha());
268+
c.scaleRGB(scale);
274269
}
275270

276271

0 commit comments

Comments
 (0)