Java performance timing library

拟墨画扇 提交于 2019-11-27 00:55:57

问题


I frequent wrap code in a System.nanoTime() pair in order to timing it. Something like:

long start = System.nanoTime();    
methodToBeTimed();
long elapsedTime = System.nanoTime() - start;

There is any good timing library that helps with this problem? Also homegrown code will be accepted.

NB

A profiler is not a solution here, since I want to enforce some time constraints in my unit tests, so I want to timing methods programmatically.


回答1:


I haven't used it but I came across perf4j recently.




回答2:


Not a direct answer to your question, but I am also often using this tip to time my code and just wrote the following simple Eclipse -> Surround With template:

long startTime = System.currentTimeMillis();
${line_selection}${cursor}
long totalTime = System.currentTimeMillis() - startTime;
System.out.println("Total time = " + totalTime);
System.out.println();



回答3:


JUnit 4 got a built-in timing-contraint functionality.

@Test(timeout=X)

should do the trick. X is the maximum number of milliseconds the method is allowed to run.




回答4:


there is StopWatch from commons-lang, it also allows you to split timer.




回答5:


If you're using Spring you already have nice class called StopWatch in your classpath for this propose.




回答6:


JETM is a good library for doing this. It can also provide with mins, maxs and averages, plus can generate informative graphs.




回答7:


Tried JPerf ?




回答8:


What kind of help are you looking for with this problem? You have the basics in place. You get the elapsed time in Nanoseconds, accurate to whatever resolution the underlying OS/Hardware is capable of.

Also... and I know you said no profilers... but I have had outstanding experience with YourKit. It provides an API that you can use to control profiling from the outside. Depending on what your exact problem is, this one might be worth having a look at.




回答9:


Caliper

And in addition to reading the wikis on that site, i recommend reading:

  • "Writing a Java Micro-benchmark"
  • "Java theory and practice: Anatomy of a flawed microbenchmark"



回答10:


Something new on the market is JMH. It's made under the umbrella of the openjdk project.




回答11:


I've just started using Java Simon (formerly on Google Code), and it seems awesome! A simple set of stopwatches and counters with few dependencies.




回答12:


handmade...
gather statistic in different places, define unique name for monitoring
live performance statistic could be monitored via jvisualvm in real-time and you can persist it at the end of your flow

example

private PerfStat perfStat = new PerfStat("my-critical-method");
...
public void myCriticalMethod() {
  perfStat.start();
  ...
  perfStat.stop();
}

implementation

import static JmxUtils.registerMBean;
import static java.lang.String.format;
import static java.lang.System.currentTimeMillis;
import java.io.Serializable;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.atomic.AtomicLong;
import org.apache.commons.lang3.time.StopWatch;

public class PerfStat {

  public interface Stat extends Serializable {
    long getLeapsCount();

    double getMinTimeMs();

    double getMinTimeSampleMs();

    double getMaxTimeMs();

    double getMaxTimeSampleMs();

    double getLeapTimeMs();

    double getAvgTimeMs();

    double getAvgTimeSampleMs();

    double getTotalTimeMs();

    long getFailedLeapsCount();

    long getPeersCount();
  }

  public static final class StatImpl implements Stat {
    private static final long serialVersionUID = -5170937866147981204L;

    private String domain;
    private volatile long leapsCount;
    private volatile long leapsCountSample;
    private volatile long failedLeapsCount;
    private volatile double totalTimeNs;
    private volatile double totalTimeSampleNs;
    private volatile double avgTimeSampleMs;
    private volatile double leapTimeMs;
    private volatile double minTimeMs;
    private volatile double minTimeSampleMs;
    private volatile double minTimeThresholdMs;
    private volatile double maxTimeMs;
    private volatile double maxTimeSampleMs;
    private volatile double maxTimeThresholdMs;
    private volatile AtomicLong peersCount = new AtomicLong();

    public StatImpl() {
      // for deserialization
    }

    public StatImpl(String domain) {
      this.domain = domain;
    }

    public String getDomain() {
      return domain;
    }

    @Override
    public long getLeapsCount() {
      return leapsCount;
    }

    @Override
    public double getLeapTimeMs() {
      return leapTimeMs;
    }

    @Override
    public double getMinTimeMs() {
      return minTimeMs;
    }

    @Override
    public double getMinTimeSampleMs() {
      return minTimeSampleMs;
    }

    @Override
    public double getMaxTimeMs() {
      return maxTimeMs;
    }

    @Override
    public double getMaxTimeSampleMs() {
      return maxTimeSampleMs;
    }

    @Override
    public double getAvgTimeMs() {
      return round(totalTimeNs / leapsCount);
    }

    @Override
    public double getAvgTimeSampleMs() {
      return avgTimeSampleMs;
    }

    @Override
    public double getTotalTimeMs() {
      return round(totalTimeNs);
    }

    @Override
    public long getFailedLeapsCount() {
      return failedLeapsCount;
    }

    @Override
    public long getPeersCount() {
      return peersCount.get();
    }

    @Override
    public String toString() {
      return format("%,.2f %,.2f %,.2f", minTimeMs, getAvgTimeMs(), maxTimeMs);
    }
  }

  private ThreadLocal<StopWatch> stopWatch = new ThreadLocal<StopWatch>() {
    @Override
    protected StopWatch initialValue() {
      return new StopWatch();
    }
  };

  private static final long AGGREGATION_TIME_MS = 4_000;
  private static final ConcurrentHashMap<String, StatImpl> stats = new ConcurrentHashMap<>();
  private StatImpl stat;
  private long lastAggregationTimeMs;

  public PerfStat(String domain) {
    stat = stats.get(domain);
    if (stat == null)
      initStat(domain);
    stat.peersCount.incrementAndGet();
  }

  @Override
  protected void finalize() throws Throwable {
    stat.peersCount.decrementAndGet();
    super.finalize();
  }

  public static Stat getPerfStat(String domain) {
    return stats.get(domain);
  }

  public static Map<String, StatImpl> getPerfStat() {
    return new TreeMap<>(stats);
  }

  public static void merge(Map<String, StatImpl> rhsStats) {
    synchronized (stats) {
      for (Entry<String, StatImpl> rhsStat : rhsStats.entrySet()) {
        StatImpl lhs = stats.get(rhsStat.getKey());
        StatImpl rhs = rhsStat.getValue();
        if (lhs == null) {
          stats.put(rhsStat.getKey(), rhs);
          continue;
        }
        lhs.leapsCount += rhs.leapsCount;
        lhs.totalTimeNs += rhs.totalTimeNs;
        if (rhs.minTimeMs < lhs.minTimeMs)
          lhs.minTimeMs = rhs.minTimeMs;
        if (rhs.maxTimeMs > lhs.maxTimeMs)
          lhs.maxTimeMs = rhs.maxTimeMs;
      }
    }
  }

  private void initStat(String domain) {
    synchronized (stats) {
      stat = stats.get(domain);
      if (stat == null) {
        stat = new StatImpl(domain);
        stats.put(domain, stat);
        registerMBean(format("%s:type=%s", getClass().getName(), domain), stat, Stat.class);
      }
    }
  }

  public PerfStat start() {
    if (stopWatch.get().isStarted()) {
      synchronized (stat) {
        stat.failedLeapsCount += 1;
      }
    }
    long currentTimeMillis = currentTimeMillis();
    if (stat.leapsCountSample > 0 && currentTimeMillis > lastAggregationTimeMs + AGGREGATION_TIME_MS) {
      synchronized (stat) {
        if (stat.leapsCountSample > 0 && currentTimeMillis > lastAggregationTimeMs + AGGREGATION_TIME_MS) {
          lastAggregationTimeMs = currentTimeMillis;
          stat.avgTimeSampleMs = round(stat.totalTimeSampleNs / stat.leapsCountSample);
          stat.leapsCountSample = 0;
          stat.totalTimeSampleNs = 0;
          stat.maxTimeSampleMs = stat.maxTimeThresholdMs;
          stat.maxTimeThresholdMs = 0;
          stat.minTimeSampleMs = stat.minTimeThresholdMs;
          stat.minTimeThresholdMs = 0;
        }
      }
    }
    stopWatch.get().reset();
    stopWatch.get().start();
    return this;
  }

  public long stop() {
    stopWatch.get().stop();
    long timeNs = stopWatch.get().getNanoTime();
    double timeMs = round(timeNs);
    synchronized (stat) {
      stat.leapTimeMs = timeMs;
      stat.totalTimeNs += timeNs;
      stat.totalTimeSampleNs += timeNs;
      if (timeMs > stat.maxTimeMs)
        stat.maxTimeMs = timeMs;
      if (timeMs > stat.maxTimeThresholdMs)
        stat.maxTimeThresholdMs = timeMs;
      if (timeMs < stat.minTimeMs || stat.minTimeMs == 0)
        stat.minTimeMs = timeMs;
      if (timeMs < stat.minTimeThresholdMs || stat.minTimeThresholdMs == 0)
        stat.minTimeThresholdMs = timeMs;
      stat.leapsCount += 1;
      stat.leapsCountSample += 1;
    }
    return timeNs;
  }

  public static void reset() {
    for (StatImpl stat : stats.values()) {
      synchronized (stat) {
        stat.leapsCount = 0;
        stat.leapsCountSample = 0;
        stat.totalTimeNs = 0;
        stat.totalTimeSampleNs = 0;
        stat.maxTimeMs = 0;
        stat.maxTimeSampleMs = 0;
        stat.maxTimeThresholdMs = 0;
        stat.minTimeMs = 0;
        stat.minTimeSampleMs = 0;
        stat.minTimeThresholdMs = 0;
      }
    }
  }

  public StopWatch getStopWatch() {
    return stopWatch.get();
  }

  public Stat getStat() {
    return stat;
  }

  private static double round(double nanos) {
    double scale = 1000.0;
    double nsInMs = 1000_000.0;
    return Math.round(nanos * scale / nsInMs) / scale;
  }
}

import com.google.common.collect.ImmutableMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.io.IOException;
import java.lang.management.ManagementFactory;
import java.util.Map;

import javax.management.InstanceAlreadyExistsException;
import javax.management.JMX;
import javax.management.MBeanServer;
import javax.management.MBeanServerConnection;
import javax.management.MalformedObjectNameException;
import javax.management.ObjectInstance;
import javax.management.ObjectName;
import javax.management.StandardMBean;
import javax.management.remote.JMXConnector;
import javax.management.remote.JMXConnectorFactory;
import javax.management.remote.JMXServiceURL;

public final class JmxUtils {

    private static final Logger LOG = LoggerFactory.getLogger(JmxUtils.class);

    private JmxUtils() {
    }

    public static <T> T newJmxClient(Class<T> clazz, String objectName, String serviceUrl) {
        return newJmxClient(clazz, objectName, serviceUrl, null, null);
    }

    public static <T> T newJmxClient(Class<T> clazz, String objectName, String serviceUrl, final String user, final String pass) {
        try {
            JMXServiceURL jmxServiceUrl = new JMXServiceURL(serviceUrl);
            Map<String, ?> env = user == null ? null : ImmutableMap.of(JMXConnector.CREDENTIALS, new String[] {user, pass});
            JMXConnector jmxc = JMXConnectorFactory.connect(jmxServiceUrl, env);
            MBeanServerConnection mbsc = jmxc.getMBeanServerConnection();
            ObjectName mbeanName = new ObjectName(objectName);
            return JMX.newMBeanProxy(mbsc, mbeanName, clazz, true);
        } catch (IOException | MalformedObjectNameException e) {
            throw new RuntimeException("Can not create client for remote JMX " + serviceUrl, e);
        }
    }

    /**
     * @param objName
     *            domain:type=value[,name=value]
     * @param implementation
     * @param mbeanInterface
     * @see ObjectName
     * @see StandardMBean
     */
    public static <I> ObjectInstance registerMBean(String objName, I implementation, Class<I> mbeanInterface) {
        int counter = 0;
        String uniqueSuffix = "";
        MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
        while (true) {
            try {
                ObjectName name = new ObjectName(objName + uniqueSuffix);
                StandardMBean mbean = new StandardMBean(implementation, mbeanInterface);
                return mbs.registerMBean(mbean, name);
            } catch (InstanceAlreadyExistsException e) {
                LOG.trace("JMX instance exists, trying next available", e);
                uniqueSuffix = "" + ++counter;
            } catch (Exception e) {
                throw new RuntimeException(e);
            }
        }
    }

    /**
     * @param objName
     * @see ObjectName
     */
    public static void unregisterMBean(String objName) {
        try {
            final MBeanServer mbs = ManagementFactory.getPlatformMBeanServer();
            final ObjectName name = new ObjectName(objName);
            mbs.unregisterMBean(name);
        } catch (Exception e) {
            throw new RuntimeException(e);
        }
    }
}


来源:https://stackoverflow.com/questions/1237181/java-performance-timing-library

易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!