【问题标题】:Stopwatch class for JavaJava 的秒表类
【发布时间】:2009-08-06 12:51:31
【问题描述】:

您应该使用哪个 Java 类来测量时间性能?

(可以使用任何日期/时间类,但我要问的原因是在 .Net 中有一个指定的 Stopwatch 类用于此目的)

【问题讨论】:

标签: java performance measurement


【解决方案1】:

Spring 框架有一个优秀的StopWatch class

StopWatch stopWatch = new StopWatch("My Stop Watch");

stopWatch.start("initializing");
Thread.sleep(2000); // simulated work
stopWatch.stop();

stopWatch.start("processing");
Thread.sleep(5000); // simulated work
stopWatch.stop();

stopWatch.start("finalizing");
Thread.sleep(3000); // simulated work
stopWatch.stop();

System.out.println(stopWatch.prettyPrint());

这会产生:

秒表“我的秒表”:运行时间(毫秒)= 10000 ----------------------------------------- ms % 任务名称 ----------------------------------------- 02000 020% 初始化 05000 050% 处理 03000 030% 敲定

【讨论】:

  • 拯救了我的一天!...谢谢!
  • 新版本似乎打印的是 ns 而不是 ms。现在给任何人如何显示 ms...(spring-core-5.2.8)
  • 来自 javadoc 类:This class is normally used to verify performance during proof-of-concept work and in development, rather than as part of production applications. 任何生产预期等效项?
【解决方案2】:

这是一个示例,如何使用 StopWatch 通过注释专用方法来设置多个测量值。非常有用且非常易于测量,例如服务调用超过多个嵌入式调用/操作等等。

StopWatch 层次结构

package ch.vii.spring.aop;

import java.util.Arrays;

import org.aopalliance.intercept.MethodInterceptor;
import org.aopalliance.intercept.MethodInvocation;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.stereotype.Component;
import org.springframework.util.StopWatch;

@Component
public class ProfilingMethodInterceptor implements MethodInterceptor {

    private static final Logger log = LoggerFactory.getLogger(ProfilingMethodInterceptor.class);

    public Object invoke(MethodInvocation invocation) throws Throwable {

        if (log.isInfoEnabled()) {
            String stopWatchName = invocation.getMethod().toGenericString();
            StopWatchHierarchy stopWatch = StopWatchHierarchy.getStopWatchHierarchy(stopWatchName);

            String taskName = invocation.getMethod().getName();
            stopWatch.start(taskName);

            try {
                return invocation.proceed();
            } finally {
                stopWatch.stop();
            }
        } else {
            return invocation.proceed();
        }
    }

    static class StopWatchHierarchy {
        private static final ThreadLocal<StopWatchHierarchy> stopwatchLocal = new ThreadLocal<StopWatchHierarchy>();
        private static final IndentStack indentStack = new IndentStack();

        static StopWatchHierarchy getStopWatchHierarchy(String id) {

            StopWatchHierarchy stopWatch = stopwatchLocal.get();
            if (stopWatch == null) {
                stopWatch = new StopWatchHierarchy(id);
                stopwatchLocal.set(stopWatch);
            }
            return stopWatch;
        }

        static void reset() {
            stopwatchLocal.set(null);
        }

        final StopWatch stopWatch;
        final Stack stack;

        StopWatchHierarchy(String id) {
            stopWatch = new StopWatch(id);
            stack = new Stack();
        }

        void start(String taskName) {
            if (stopWatch.isRunning()) {
                stopWatch.stop();
            }
            taskName = indentStack.get(stack.size) + taskName;
            stack.push(taskName);
            stopWatch.start(taskName);
        }

        void stop() {
            stopWatch.stop();
            stack.pop();
            if (stack.isEmpty()) {
                log.info(stopWatch.prettyPrint());
                StopWatchHierarchy.reset();
            } else {
                stopWatch.start(stack.get());
            }
        }

    }

    static class Stack {
        private int size = 0;
        String elements[];

        public Stack() {
            elements = new String[10];
        }

        public void push(String e) {
            if (size == elements.length) {
                ensureCapa();
            }
            elements[size++] = e;
        }

        public String pop() {
            String e = elements[--size];
            elements[size] = null;
            return e;
        }

        public String get() {
            return elements[size - 1];
        }

        public boolean isEmpty() {
            return size == 0;
        }

        private void ensureCapa() {
            int newSize = elements.length * 2;
            elements = Arrays.copyOf(elements, newSize);
        }
    }

    static class IndentStack {
        String elements[] = new String[0];

        public String get(int index) {
            if (index >= elements.length) {
                ensureCapa(index + 10);
            }
            return elements[index];
        }

        private void ensureCapa(int newSize) {
            int oldSize = elements.length;
            elements = Arrays.copyOf(elements, newSize);
            for (int i = oldSize; i < elements.length; i++) {
                elements[i] = new String(new char[i]).replace("\0", "|   ");
            }
        }
    }
}

顾问

package ch.vii.spring.aop;

import java.lang.reflect.Method;

import org.aopalliance.aop.Advice;
import org.springframework.aop.Pointcut;
import org.springframework.aop.support.AbstractPointcutAdvisor;
import org.springframework.aop.support.StaticMethodMatcherPointcut;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

@Component
public class ProfilingAdvisor extends AbstractPointcutAdvisor {

    private static final long serialVersionUID = 1L;

    private final StaticMethodMatcherPointcut pointcut = new StaticMethodMatcherPointcut() {
        public boolean matches(Method method, Class<?> targetClass) {
            return method.isAnnotationPresent(ProfileExecution.class);
        }
    };

    @Autowired
    private ProfilingMethodInterceptor interceptor;

    public Pointcut getPointcut() {
        return this.pointcut;
    }

    public Advice getAdvice() {
        return this.interceptor;
    }
}

ProfileExecution 注解

package ch.vii.spring.aop;

import java.lang.annotation.ElementType;
import java.lang.annotation.Inherited;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;

@Retention(RetentionPolicy.RUNTIME)
@Target(ElementType.METHOD)
@Inherited
public @interface ProfileExecution {

}

注释你的代码

package ch.vii.spring;                                                                                                                                                                 
import org.springframework.beans.factory.annotation.Autowired;                                      
import org.springframework.stereotype.Component;                                                    

import ch.vii.spring.aop.ProfileExecution;                                                          

@Component                                                                                          
public class Bean {                                                                                 
    @Autowired                                                                                      
    InnerBean innerBean;  

    @ProfileExecution                                                                               
    public void foo() {                                                                             
        innerBean.innerFoo();                                                                      
        innerBean.innerFoo2();                                                                      
        innerBean.innerFoo();                                                                       
    }                                                                                                                                                                                         
}  

public class InnerBean {
    @Autowired
    InnerInnerBean innerInnerBean;

    @ProfileExecution
    public void innerFoo() {
    }

    @ProfileExecution
    public void innerFoo2() {
        innerInnerBean.innerInnerFoo();
        innerInnerBean.innerInnerFoo();
        innerInnerBean.innerInnerFoo();
    }
}                                                                                                    

输出

09:58:39.627 [main] INFO  c.v.s.aop.ProfilingMethodInterceptor - StopWatch 'public void ch.vii.spring.Bean.foo()': running time (millis) = 215
-----------------------------------------
ms     %     Task name
-----------------------------------------
00018  008 %  foo
00026  012 %  |   innerFoo
00001  000 %  foo
00016  007 %  |   innerFoo2
00038  018 %  |   |   innerInnerFoo
00000  000 %  |   innerFoo2
00024  011 %  |   |   innerInnerFoo
00028  013 %  |   innerFoo2
00024  011 %  |   |   innerInnerFoo
00029  013 %  |   innerFoo2
00000  000 %  foo
00011  005 %  |   innerFoo
00000  000 %  foo

【讨论】:

  • 老兄,这个答案太棒了,你有 31 分……太搞笑了。谢谢你的代码让我开始,非常干净。
【解决方案3】:

【讨论】:

  • nanoTime 具有更高的分辨率(至少在 Windows 上):1-2 微秒,而currentTimeMillis 为 16 毫秒
  • Google guava 似乎使用纳秒:Stopwatch
  • Apache Commons 的新版本使用 nanoTime() 而不是 currentTimeMillis() 方法。
【解决方案4】:

查看 perf4j。 Spring的秒表主要是为了本地开发。 Perf4j 可以支持您的 POC 类型计时以及生产环境。

【讨论】:

    【解决方案5】:

    Performmetrics 提供了一个方便的 Stopwatch 类。它可以测量挂钟时间等等:如果您需要更高的准确性,它还可以测量 CPU 时间、用户时间和系统时间。 它很小,免费,您可以从 Maven Central 下载。 更多信息和示例可以在这里找到:https://obvj.net/performetrics

    Stopwatch sw = new Stopwatch();
    sw.start();
    
    // Your code here
    
    sw.stop();
    sw.printStatistics(System.out);
    

    这会产生类似于以下的输出:

    +-----------------+----------------------+--------------+
    | Counter         |         Elapsed time | Time unit    |
    +-----------------+----------------------+--------------+
    | Wall clock time |             85605718 | nanoseconds  |
    | CPU time        |             78000500 | nanoseconds  |
    | User time       |             62400400 | nanoseconds  |
    | System time     |             15600100 | nanoseconds  |
    +-----------------+----------------------+--------------+
    

    您只需传递自定义参数即可将指标转换为任何时间单位(纳秒、毫秒、秒等)。

    PS:我是该工具的作者。

    【讨论】:

      【解决方案6】:

      您可以尝试System.currentTimeMillis(),但在一些知名 IDE 下也有很好的profiling选项,例如 eclipse 和 netbeans。此外,远离 IDE,您可以在性能测量任务中尝试standalone profilers。我认为使用分析器会比使用 System.currentTimeMillis() 获得更好的结果。

      【讨论】:

      • 我想编写一个简单的集成测试,以确保某些东西“性能合理”。
      【解决方案7】:

      如果您只是想测量它,请使用秒表类,或者只是秒表。

      如果你想让它更快,consider this

      【讨论】:

        【解决方案8】:

        最好的方法是使用 System.nanoTime(),但是,如果您想像 System.Diagnostics.Stopwatch 一样获得 Ticks(经过的 Ticks),您是否需要将纳秒转换为 Ticks(1 Tick = 100 纳秒),然后开始在 nanos 和 millis、secs、mins、hours 之间进行转换,然后最后将输出格式化为 Time 表示,例如 Elapsed() 方法之一 (hh:mm:ss.sssssss),但是看起来像 Java 中的 Dates 使用只有 3 毫秒 (hh:mm:ss.sss),因此您还需要锻炼格式。

        我为 Java 做了一个秒表类 你可以从:http://carlosqt.blogspot.com/2011/05/stopwatch-class-for-java.html

        例子:

        package stopwatchapp;
        import java.math.BigInteger;
        public class StopwatchApp {
            public static void main(String[] args) {
        
                Stopwatch timer = new Stopwatch();
                timer.start();
                Fibonacci(40);
                timer.stop();
        
                System.out.println("Elapsed time in ticks: " 
                    + timer.getElapsedTicks());
                System.out.println("Elapsed time in milliseconds: " 
                    + timer.getElapsedMilliseconds());
                System.out.println("Elapsed time in seconds: " 
                    + timer.getElapsedSeconds());
                System.out.println("Elapsed time in minutes: " 
                    + timer.getElapsedMinutes());
                System.out.println("Elapsed time in hours: " 
                    + timer.getElapsedHours());
                System.out.println("Elapsed time with format: " 
                    + timer.getElapsed());
            }
        
            private static BigInteger Fibonacci(int n)
            {
                if (n < 2)
                    return BigInteger.ONE;
                else
                    return Fibonacci(n - 1).add(Fibonacci(n - 2));
            }
        }
        

        输出:

        // Elapsed time in ticks: 33432284
        // Elapsed time in milliseconds: 3343
        // Elapsed time in seconds: 3
        // Elapsed time in minutes: 0
        // Elapsed time in hours: 0
        // Elapsed time with format: 00:00:03.3432284
        

        希望这会有所帮助。

        【讨论】:

          【解决方案9】:

          如果您使用的是 JDK 9+,则可以使用 Flight Recorder。它具有极低的开销,并且使用不变的 TSC 进行计时,这比 System.nanoTime() 的侵入性要小

          @StackTrace(false)
          static class StopWatch extends Event {
            int fib;
          }
          
          public static void main(String... args) throws IOException {
              Recording r = new Recording();
              r.start();
              for (int i = 0; i < 500000; i++) {
                  StopWatch s = new StopWatch();
                  s.begin();
                  s.fib = fib(i%100);
                  s.commit();
              }
              r.stop();
              Path p = Paths.get("recording.jfr");
              r.dump(p);
              for (RecordedEvent e : RecordingFile.readAllEvents(p)) {
                  System.out.println(e.getValue("fib") + " " + e.getDuration().toNanos() + " ns");
              }
          }
          

          您也可以从命令行开始录制(-XX:StartFlightRecording),然后在配置文件(.jfc)中启用事件(如果默认禁用,@Enable(false))

          这样,JIT 通常会优化 StopWatch 调用(转义分析、内联、死码消除等),因此您只需在要测量某些内容时支付惩罚。

          【讨论】:

            【解决方案10】:
            private void WaitTimer(long ms)
            {
                long t = 0;
                long x = System.currentTimeMillis();
            
                while(t < ms)
                {
                   t = System.currentTimeMillis() - x;
                }
            }
            

            【讨论】:

            • 这将使 CPU 线程忙于等待指定的时间过去。不是睡眠程序的好方法。
            • 这是一个非常糟糕的主意。 Thread.sleep 将在不占用完整 CPU 时间的情况下执行此操作。无论如何,最初的问题是关于测量性能时间,而不是等待一段时间。
            猜你喜欢
            • 1970-01-01
            • 2012-06-18
            • 2011-09-21
            • 2019-03-27
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 2014-01-16
            • 2010-10-14
            相关资源
            最近更新 更多