From 98ea811d9b5b27e08d71d4a3a073c66cda213a54 Mon Sep 17 00:00:00 2001 From: Aikar Date: Tue, 5 Apr 2016 01:20:25 -0400 Subject: [PATCH] Update Timings to use Region based chunk reporting This will drastically reduce file size on large servers and help avoid the parser failing to load the report. This will also reduce memory usage of timings data. --- Spigot-API-Patches/0003-Timings-v2.patch | 196 +++++++++++++++-------- 1 file changed, 129 insertions(+), 67 deletions(-) diff --git a/Spigot-API-Patches/0003-Timings-v2.patch b/Spigot-API-Patches/0003-Timings-v2.patch index 1a2030edd..ce6f8fb46 100644 --- a/Spigot-API-Patches/0003-Timings-v2.patch +++ b/Spigot-API-Patches/0003-Timings-v2.patch @@ -1,4 +1,4 @@ -From ec3f78348b95c24b1e62101a871c43136432f5bb Mon Sep 17 00:00:00 2001 +From 0b47a95a20c68bbc0620b0fb71ae2f5663619343 Mon Sep 17 00:00:00 2001 From: Aikar Date: Mon, 29 Feb 2016 18:48:17 -0600 Subject: [PATCH] Timings v2 @@ -630,10 +630,10 @@ index 0000000..46c4e13 +} diff --git a/src/main/java/co/aikar/timings/TimingHistory.java b/src/main/java/co/aikar/timings/TimingHistory.java new file mode 100644 -index 0000000..7a04770 +index 0000000..9206569 --- /dev/null +++ b/src/main/java/co/aikar/timings/TimingHistory.java -@@ -0,0 +1,276 @@ +@@ -0,0 +1,338 @@ +/* + * This file is licensed under the MIT License (MIT). + * @@ -659,7 +659,10 @@ index 0000000..7a04770 + */ +package co.aikar.timings; + ++import co.aikar.timings.TimingHistory.RegionData.RegionId; ++import co.aikar.util.JSONUtil; +import com.google.common.base.Function; ++import com.google.common.collect.Maps; +import com.google.common.collect.Sets; +import org.bukkit.Bukkit; +import org.bukkit.Chunk; @@ -683,7 +686,7 @@ index 0000000..7a04770 +import static co.aikar.timings.TimingsManager.MINUTE_REPORTS; +import static co.aikar.util.JSONUtil.*; + -+@SuppressWarnings({"deprecation", "SuppressionAnnotation"}) ++@SuppressWarnings({"deprecation", "SuppressionAnnotation", "Convert2Lambda", "Anonymous2MethodRef"}) +public class TimingHistory { + public static long lastMinuteTime; + public static long timedTicks; @@ -691,23 +694,23 @@ index 0000000..7a04770 + public static long entityTicks; + public static long tileEntityTicks; + public static long activatedEntityTicks; -+ static int worldIdPool = 1; ++ private static int worldIdPool = 1; + static Map worldMap = LoadingMap.newHashMap(new Function() { + @Override + public Integer apply(String input) { + return worldIdPool++; + } + }); -+ final long endTime; -+ final long startTime; -+ final long totalTicks; -+ final long totalTime; // Represents all time spent running the server this history -+ final MinuteReport[] minuteReports; ++ private final long endTime; ++ private final long startTime; ++ private final long totalTicks; ++ private final long totalTime; // Represents all time spent running the server this history ++ private final MinuteReport[] minuteReports; + -+ final TimingHistoryEntry[] entries; ++ private final TimingHistoryEntry[] entries; + final Set tileEntityTypeSet = Sets.newHashSet(); + final Set entityTypeSet = Sets.newHashSet(); -+ final Map worlds; ++ private final Map worlds; + + TimingHistory() { + this.endTime = System.currentTimeMillis() / 1000; @@ -731,71 +734,129 @@ index 0000000..7a04770 + entries[i++] = new TimingHistoryEntry(handler); + } + -+ final Map entityCounts = MRUMapCache.of(LoadingMap.of( -+ new EnumMap(EntityType.class), Counter.LOADER -+ )); -+ final Map tileEntityCounts = MRUMapCache.of(LoadingMap.of( -+ new EnumMap(Material.class), Counter.LOADER -+ )); ++ + // Information about all loaded chunks/entities ++ //noinspection unchecked + this.worlds = toObjectMapper(Bukkit.getWorlds(), new Function() { -+ @Override -+ public JSONPair apply(World world) { ++ @Override ++ public JSONPair apply(World world) { ++ Map regions = LoadingMap.newHashMap(RegionData.LOADER); ++ ++ for (Chunk chunk : world.getLoadedChunks()) { ++ RegionData data = regions.get(new RegionId(chunk.getX(), chunk.getZ())); ++ ++ for (Entity entity : chunk.getEntities()) { ++ data.entityCounts.get(entity.getType()).increment(); ++ } ++ ++ for (BlockState tileEntity : chunk.getTileEntities()) { ++ data.tileEntityCounts.get(tileEntity.getBlock().getType()).increment(); ++ } ++ } + return pair( + worldMap.get(world.getName()), -+ toArrayMapper(world.getLoadedChunks(), new Function() { ++ toArrayMapper(regions.values(),new Function() { + @Override -+ public Object apply(Chunk chunk) { -+ entityCounts.clear(); -+ tileEntityCounts.clear(); -+ -+ for (Entity entity : chunk.getEntities()) { -+ entityCounts.get(entity.getType()).increment(); -+ } -+ -+ for (BlockState tileEntity : chunk.getTileEntities()) { -+ tileEntityCounts.get(tileEntity.getBlock().getType()).increment(); -+ } -+ -+ if (tileEntityCounts.isEmpty() && entityCounts.isEmpty()) { -+ return null; -+ } -+ return toArray( -+ chunk.getX(), -+ chunk.getZ(), -+ toObjectMapper(entityCounts.entrySet(), -+ new Function, JSONPair>() { -+ @Override -+ public JSONPair apply(Map.Entry entry) { -+ entityTypeSet.add(entry.getKey()); -+ return pair( -+ String.valueOf(entry.getKey().getTypeId()), -+ entry.getValue().count() -+ ); ++ public Object apply(RegionData input) { ++ return toArray( ++ input.regionId.x, ++ input.regionId.z, ++ toObjectMapper(input.entityCounts.entrySet(), ++ new Function, JSONPair>() { ++ @Override ++ public JSONPair apply(Map.Entry entry) { ++ entityTypeSet.add(entry.getKey()); ++ return pair( ++ String.valueOf(entry.getKey().getTypeId()), ++ entry.getValue().count() ++ ); ++ } + } -+ } -+ ), -+ toObjectMapper(tileEntityCounts.entrySet(), -+ new Function, JSONPair>() { -+ @Override -+ public JSONPair apply(Map.Entry entry) { -+ tileEntityTypeSet.add(entry.getKey()); -+ return pair( -+ String.valueOf(entry.getKey().getId()), -+ entry.getValue().count() -+ ); ++ ), ++ toObjectMapper(input.tileEntityCounts.entrySet(), ++ new Function, JSONPair>() { ++ @Override ++ public JSONPair apply(Map.Entry entry) { ++ tileEntityTypeSet.add(entry.getKey()); ++ return pair( ++ String.valueOf(entry.getKey().getId()), ++ entry.getValue().count() ++ ); ++ } + } -+ } -+ ) -+ ); ++ ) ++ ); + } + }) + ); + } + }); + } ++ static class RegionData { ++ private final RegionId regionId; ++ @SuppressWarnings("Guava") ++ static Function LOADER = new Function() { ++ @Override ++ public RegionData apply(RegionId id) { ++ return new RegionData(id); ++ } ++ }; ++ RegionData(RegionId id) { ++ this.regionId = id; ++ } + -+ public static void resetTicks(boolean fullReset) { ++ @Override ++ public boolean equals(Object o) { ++ if (this == o) return true; ++ if (o == null || getClass() != o.getClass()) return false; ++ ++ RegionData that = (RegionData) o; ++ ++ return regionId.equals(that.regionId); ++ ++ } ++ ++ @Override ++ public int hashCode() { ++ return regionId.hashCode(); ++ } ++ ++ @SuppressWarnings("unchecked") ++ final Map entityCounts = MRUMapCache.of(LoadingMap.of( ++ new EnumMap(EntityType.class), Counter.LOADER ++ )); ++ @SuppressWarnings("unchecked") ++ final Map tileEntityCounts = MRUMapCache.of(LoadingMap.of( ++ new EnumMap(Material.class), Counter.LOADER ++ )); ++ ++ static class RegionId { ++ final int x, z; ++ final long regionId; ++ RegionId(int x, int z) { ++ this.x = x >> 5 << 5; ++ this.z = z >> 5 << 5; ++ this.regionId = ((long) (this.x) << 32) + (this.z >> 5 << 5) - Integer.MIN_VALUE; ++ } ++ ++ @Override ++ public boolean equals(Object o) { ++ if (this == o) return true; ++ if (o == null || getClass() != o.getClass()) return false; ++ ++ RegionId regionId1 = (RegionId) o; ++ ++ return regionId == regionId1.regionId; ++ ++ } ++ ++ @Override ++ public int hashCode() { ++ return (int) (regionId ^ (regionId >>> 32)); ++ } ++ } ++ } ++ static void resetTicks(boolean fullReset) { + if (fullReset) { + // Non full is simply for 1 minute reports + timedTicks = 0; @@ -863,7 +924,7 @@ index 0000000..7a04770 + } + } + -+ static class TicksRecord { ++ private static class TicksRecord { + final long timed; + final long player; + final long entity; @@ -880,7 +941,7 @@ index 0000000..7a04770 + + } + -+ static class PingRecord { ++ private static class PingRecord { + final double avg; + + PingRecord() { @@ -893,9 +954,10 @@ index 0000000..7a04770 + } + } + -+ static class Counter { ++ @SuppressWarnings("WeakerAccess") ++ public static class Counter { + int count = 0; -+ @SuppressWarnings({"rawtypes", "SuppressionAnnotation"}) ++ @SuppressWarnings({"rawtypes", "SuppressionAnnotation", "Guava"}) + static Function LOADER = new LoadingMap.Feeder() { + @Override + public Counter apply() {