/*
* Copyright 2012 ZerothAngel <zerothangel@tyrannyofheaven.org>
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
package org.tyrannyofheaven.bukkit.util;
import java.util.Map;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentMap;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.bukkit.Bukkit;
import org.bukkit.plugin.Plugin;
/**
* Simple & naive profiling, for development. Don't use in production code!
*
* @author zerothangel
*/
public class ToHProfileUtils {
private static final Logger logger = Logger.getLogger(ToHProfileUtils.class.getName());
private static ConcurrentMap<String, ProfileData> profileData = new ConcurrentHashMap<>();
private static ProfileData getProfileData(String name) {
// Avoid cost of initial creation
ProfileData pd = profileData.get(name);
if (pd == null) {
pd = new ProfileData();
ProfileData old = profileData.putIfAbsent(name, pd);
if (old != null)
pd = old; // use existing
}
return pd;
}
public static void profileStart(String name) {
ProfileData pd = getProfileData(name);
pd.startTime.set(System.nanoTime());
}
public static void profileStop(String name) {
long end = System.nanoTime();
ProfileData pd = getProfileData(name);
long current = end - pd.startTime.get();
pd.accumulatedTime.addAndGet(current);
pd.hits.incrementAndGet();
pd.lastTime.set(current);
long minTime = pd.minTime.get();
while (current < minTime) {
if (pd.minTime.compareAndSet(minTime, current)) break;
minTime = pd.minTime.get();
}
long maxTime = pd.maxTime.get();
while (current > maxTime) {
if (pd.maxTime.compareAndSet(maxTime, current)) break;
maxTime = pd.maxTime.get();
}
}
public static void profileReport() {
for (Map.Entry<String, ProfileData> me : profileData.entrySet()) {
logger.log(Level.INFO, String.format("%s - %s", me.getKey(), me.getValue()));
}
}
public static void profileReset() {
profileData.clear();
}
public static void profileReset(String name) {
profileData.remove(name);
}
public static void schedulePeriodicReport(Plugin plugin, long interval) {
Bukkit.getScheduler().runTaskTimerAsynchronously(plugin, new Runnable() {
@Override
public void run() {
profileReport();
}
}, interval, interval);
}
private static class ProfileData {
private final AtomicLong startTime = new AtomicLong();
private final AtomicLong accumulatedTime = new AtomicLong();
private final AtomicInteger hits = new AtomicInteger();
private final AtomicLong lastTime = new AtomicLong();
private final AtomicLong minTime = new AtomicLong(Long.MAX_VALUE);
private final AtomicLong maxTime = new AtomicLong();
@Override
public String toString() {
long total = accumulatedTime.get();
int hits = this.hits.get();
return String.format("total: %d, hits: %d, average: %d (min: %d, max: %d, last: %d)",
total, hits, total / hits, minTime.get(), maxTime.get(), lastTime.get());
}
}
}