关于计时:如何计算方法在Java中的执行时间?

How do I time a method's execution in Java?

如何获取方法的执行时间?是否有一个计时器实用程序类来处理诸如计时任务需要多长时间等事情?

Google上的大多数搜索都会返回调度线程和任务的计时器的结果,这不是我想要的。


总是有老式的方法:

1
2
3
4
5
long startTime = System.nanoTime();
methodToTime();
long endTime = System.nanoTime();

long duration = (endTime - startTime);  //divide by 1000000 to get milliseconds.


我同意这个简单的答案。为我工作。

1
2
3
4
5
6
7
long startTime = System.currentTimeMillis();

doReallyLongThing();

long endTime = System.currentTimeMillis();

System.out.println("That took" + (endTime - startTime) +" milliseconds");

它工作得很好。很明显,分辨率只有毫秒,使用System.NanoTime()可以做得更好。这两者都有一些限制(操作系统计划片等),但这很好地工作。

平均跑几次(越多越好),你就会得到一个好主意。


来吧,伙计们!没有人提到过番石榴的方式(可以说是很棒的):

1
2
3
4
5
import com.google.common.base.Stopwatch;

Stopwatch timer = Stopwatch.createStarted();
//method invocation
LOG.info("Method took:" + timer.stop());

好的是,stopwatch.toString()可以很好地为测量选择时间单位。也就是说,如果这个值很小,它将输出38纳秒,如果它很长,它将显示5米3秒。

甚至更好:

1
2
3
4
5
6
7
8
Stopwatch timer = Stopwatch.createUnstarted();
for (...) {
   timer.start();
   methodToTrackTimeFor();
   timer.stop();
   methodNotToTrackTimeFor();
}
LOG.info("Method took:" + timer);

注:谷歌番石榴需要Java 1.6 +


使用Java 8新API的即时和持续时间,

1
2
3
4
Instant start = Instant.now();
Thread.sleep(5000);
Instant end = Instant.now();
System.out.println(Duration.between(start, end));

输出,

1
PT5S


使用探查器(jprofiler、netbeans探查器、VisualVM、Eclipse探查器等)。你会得到最准确的结果,而且干扰最小。它们使用内置的JVM机制进行分析,这也可以为您提供额外的信息,如堆栈跟踪、执行路径,以及在必要时提供更全面的结果。

当使用一个完全集成的探查器时,对一个方法进行分析是非常困难的。右键单击,探查器->添加到根方法。然后像运行测试运行或调试器一样运行探查器。


把所有可能的方法集中到一个地方。

日期

1
2
3
4
5
6
7
8
9
Date startDate = Calendar.getInstance().getTime();
long d_StartTime = new Date().getTime();
Thread.sleep(1000 * 4);
Date endDate = Calendar.getInstance().getTime();
long d_endTime = new Date().getTime();
System.out.format("StartDate : %s, EndDate : %s
"
, startDate, endDate);
System.out.format("Milli = %s, ( D_Start : %s, D_End : %s )
"
, (d_endTime - d_StartTime),d_StartTime, d_endTime);

System.CurrentTimeMillis()。

1
2
3
4
5
6
7
long startTime = System.currentTimeMillis();
Thread.sleep(1000 * 4);
long endTime = System.currentTimeMillis();
long duration = (endTime - startTime);  
System.out.format("Milli = %s, ( S_Start : %s, S_End : %s )
"
, duration, startTime, endTime );
System.out.println("Human-Readable format :"+millisToShortDHMS( duration ) );

人可读格式

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static String millisToShortDHMS(long duration) {
    String res ="";    // java.util.concurrent.TimeUnit;
    long days       = TimeUnit.MILLISECONDS.toDays(duration);
    long hours      = TimeUnit.MILLISECONDS.toHours(duration) -
                      TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(duration));
    long minutes    = TimeUnit.MILLISECONDS.toMinutes(duration) -
                      TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(duration));
    long seconds    = TimeUnit.MILLISECONDS.toSeconds(duration) -
                      TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration));
    long millis     = TimeUnit.MILLISECONDS.toMillis(duration) -
                      TimeUnit.SECONDS.toMillis(TimeUnit.MILLISECONDS.toSeconds(duration));

    if (days == 0)      res = String.format("%02d:%02d:%02d.%04d", hours, minutes, seconds, millis);
    else                res = String.format("%dd %02d:%02d:%02d.%04d", days, hours, minutes, seconds, millis);
    return res;
}

谷歌秒表?秒表的目标是以纳秒为单位测量经过的时间。

1
2
3
4
5
com.google.common.base.Stopwatch g_SW = Stopwatch.createUnstarted();
g_SW.start();
Thread.sleep(1000 * 4);
g_SW.stop();
System.out.println("Google StopWatch  :"+g_SW);

阿帕奇公地酒店?秒表为计时提供了一个方便的API。

1
2
3
4
5
org.apache.commons.lang3.time.StopWatch sw = new StopWatch();
sw.start();    
Thread.sleep(1000 * 4);    
sw.stop();
System.out.println("Apache StopWatch  :"+ millisToShortDHMS(sw.getTime()) );

欢乐时光

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
public static void jodaTime() throws InterruptedException, ParseException{
    java.text.SimpleDateFormat ms_SDF = new SimpleDateFormat("yyyy/MM/dd HH:mm:ss.SSS");
    String start = ms_SDF.format( new Date() ); // java.util.Date

    Thread.sleep(10000);

    String end = ms_SDF.format( new Date() );      
    System.out.println("Start:"+start+"\t Stop:"+end);

    Date date_1 = ms_SDF.parse(start);
    Date date_2 = ms_SDF.parse(end);        
    Interval interval = new org.joda.time.Interval( date_1.getTime(), date_2.getTime() );
    Period period = interval.toPeriod(); //org.joda.time.Period

    System.out.format("%dY/%dM/%dD, %02d:%02d:%02d.%04d
"
,
        period.getYears(), period.getMonths(), period.getDays(),
        period.getHours(), period.getMinutes(), period.getSeconds(), period.getMillis());
}

Java 8的Java日期时间API?持续时间对象表示两个即时对象之间的一段时间。

1
2
3
4
5
6
7
8
Instant start = java.time.Instant.now();
    Thread.sleep(1000);
Instant end = java.time.Instant.now();
Duration between = java.time.Duration.between(start, end);
System.out.println( between ); // PT1.001S
System.out.format("%dD, %02d:%02d:%02d.%04d
"
, between.toDays(),
        between.toHours(), between.toMinutes(), between.getSeconds(), between.toMillis()); // 0D, 00:00:01.1001

Spring框架提供秒表实用类来测量Java中经过的时间。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
StopWatch sw = new org.springframework.util.StopWatch();
sw.start("Method-1"); // Start a named task
    Thread.sleep(500);
sw.stop();

sw.start("Method-2");
    Thread.sleep(300);
sw.stop();

sw.start("Method-3");
    Thread.sleep(200);
sw.stop();

System.out.println("Total time in milliseconds for all tasks :
"
+sw.getTotalTimeMillis());
System.out.println("Table describing all tasks performed :
"
+sw.prettyPrint());

System.out.format("Time taken by the last task : [%s]:[%d]",
        sw.getLastTaskName(),sw.getLastTaskTimeMillis());

System.out.println("
 Array of the data for tasks performed ? Task Name: Time Taken"
);
TaskInfo[] listofTasks = sw.getTaskInfo();
for (TaskInfo task : listofTasks) {
    System.out.format("[%s]:[%d]
"
,
            task.getTaskName(), task.getTimeMillis());
}

输出:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Total time in milliseconds for all tasks :
999
Table describing all tasks performed :
StopWatch '': running time (millis) = 999
-----------------------------------------
ms     %     Task name
-----------------------------------------
00500  050%  Method-1
00299  030%  Method-2
00200  020%  Method-3

Time taken by the last task : [Method-3]:[200]
 Array of the data for tasks performed ? Task Name: Time Taken
[Method-1]:[500]
[Method-2]:[299]
[Method-3]:[200]


这可能不是你想让我说的,但这是对AOP的一个很好的利用。围绕您的方法鞭打一个代理拦截器,并在其中进行计时。

遗憾的是,AOP的"什么"、"为什么"和"如何"都超出了这个答案的范围,但我可能会这样做。

编辑:这里有一个到SpringAOP的链接,可以让你开始工作,如果你愿意的话。这是IVI为Java所遇到的AOP最容易实现的实现。

另外,考虑到其他人非常简单的建议,我应该补充一点,当您不希望时间之类的东西侵入您的代码时,AOP是为您服务的。但在许多情况下,这种简单易行的方法是好的。


System.currentTimeMillis();不是衡量算法性能的好方法。它测量用户观看计算机屏幕的总时间。它还包括后台运行在计算机上的其他所有操作所消耗的时间。如果你的工作站上运行了很多程序,这会有很大的不同。

正确的方法是使用java.lang.management包。

从http://nadeausoftware.com/articles/2008/03/java_tip_how_get_cpu_and_user_time_benchmarking网站:

  • "用户时间"是运行应用程序自己的代码所花费的时间。
  • "系统时间"是代表应用程序运行操作系统代码所花费的时间(例如用于I/O)。

getCpuTime()方法给出了以下各项的总和:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
import java.lang.management.ManagementFactory;
import java.lang.management.ThreadMXBean;

public class CPUUtils {

    /** Get CPU time in nanoseconds. */
    public static long getCpuTime( ) {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean( );
        return bean.isCurrentThreadCpuTimeSupported( ) ?
            bean.getCurrentThreadCpuTime( ) : 0L;
    }

    /** Get user time in nanoseconds. */
    public static long getUserTime( ) {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean( );
        return bean.isCurrentThreadCpuTimeSupported( ) ?
            bean.getCurrentThreadUserTime( ) : 0L;
    }

    /** Get system time in nanoseconds. */
    public static long getSystemTime( ) {
        ThreadMXBean bean = ManagementFactory.getThreadMXBean( );
        return bean.isCurrentThreadCpuTimeSupported( ) ?
            (bean.getCurrentThreadCpuTime( ) - bean.getCurrentThreadUserTime( )) : 0L;
    }

}


用Java 8,你也可以用通常的方法做类似的事情:

1
2
Object returnValue = TimeIt.printTime(() -> methodeWithReturnValue());
//do stuff with your returnValue

有了这样的时间:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
public class TimeIt {

public static <T> T printTime(Callable<T> task) {
    T call = null;
    try {
        long startTime = System.currentTimeMillis();
        call = task.call();
        System.out.print((System.currentTimeMillis() - startTime) / 1000d +"s");
    } catch (Exception e) {
        //...
    }
    return call;
}
}

使用这种方法,您可以在代码的任何地方轻松地测量时间,而不必破坏它。在这个简单的例子中,我只是打印时间。您可以为timeit添加一个开关,例如只在debugmode中打印时间。

如果您正在使用函数,则可以执行以下操作:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
Function<Integer, Integer> yourFunction= (n) -> {
        return IntStream.range(0, n).reduce(0, (a, b) -> a + b);
    };

Integer returnValue = TimeIt.printTime2(yourFunction).apply(10000);
//do stuff with your returnValue

public static <T, R> Function<T, R> printTime2(Function<T, R> task) {
    return (t) -> {
        long startTime = System.currentTimeMillis();
        R apply = task.apply(t);
        System.out.print((System.currentTimeMillis() - startTime) / 1000d
                +"s");
        return apply;
    };
}


我们还可以使用ApacheCommons的秒表类来测量时间。

样例代码

1
2
3
4
5
6
7
8
9
org.apache.commons.lang.time.StopWatch sw = new org.apache.commons.lang.time.StopWatch();

System.out.println("getEventFilterTreeData :: Start Time :" + sw.getTime());
sw.start();

// Method execution code

sw.stop();
System.out.println("getEventFilterTreeData :: End Time :" + sw.getTime());

如果您不使用工具,并且希望对执行时间较短的方法进行计时,那么只需稍作改动:多次执行,每次执行的次数增加一倍,直到达到一秒左右。因此,调用System.NanoTime等的时间以及System.NanoTime的准确性对结果影响很大。

1
2
3
4
5
6
7
8
9
10
    int runs = 0, runsPerRound = 10;
    long begin = System.nanoTime(), end;
    do {
        for (int i=0; i<runsPerRound; ++i) timedMethod();
        end = System.nanoTime();
        runs += runsPerRound;
        runsPerRound *= 2;
    } while (runs < Integer.MAX_VALUE / 2 && 1000000000L > end - begin);
    System.out.println("Time for timedMethod() is" +
        0.000000001 * (end-begin) / runs +" seconds");

当然,使用挂钟的注意事项适用于:JIT编译的影响、多线程/进程等。因此,您需要先多次执行该方法,以便JIT编译器完成其工作,然后多次重复此测试,并以最低的执行时间执行。


为此我们使用了AspectJ和Java注释。如果我们需要知道一个方法的执行时间,我们只需简单地注释它。更高级的版本可以使用自己的日志级别,该级别可以在运行时启用和禁用。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
public @interface Trace {
  boolean showParameters();
}

@Aspect
public class TraceAspect {
  [...]
  @Around("tracePointcut() && @annotation(trace) && !within(TraceAspect)")
  public Object traceAdvice ( ProceedingJintPoint jP, Trace trace ) {

    Object result;
    // initilize timer

    try {
      result = jp.procced();
    } finally {
      // calculate execution time
    }

    return result;
  }
  [...]
}

很好的代码。

网址:http://www.rgagnon.com/javadetails/java-0585.html

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
import java.util.concurrent.TimeUnit;

long startTime = System.currentTimeMillis();
........
........
........
long finishTime = System.currentTimeMillis();

String diff = millisToShortDHMS(finishTime - startTime);


  /**
   * converts time (in milliseconds) to human-readable format
   * "<dd:>hh:mm:ss"
   */

  public static String millisToShortDHMS(long duration) {
    String res ="";
    long days  = TimeUnit.MILLISECONDS.toDays(duration);
    long hours = TimeUnit.MILLISECONDS.toHours(duration)
                   - TimeUnit.DAYS.toHours(TimeUnit.MILLISECONDS.toDays(duration));
    long minutes = TimeUnit.MILLISECONDS.toMinutes(duration)
                     - TimeUnit.HOURS.toMinutes(TimeUnit.MILLISECONDS.toHours(duration));
    long seconds = TimeUnit.MILLISECONDS.toSeconds(duration)
                   - TimeUnit.MINUTES.toSeconds(TimeUnit.MILLISECONDS.toMinutes(duration));
    if (days == 0) {
      res = String.format("%02d:%02d:%02d", hours, minutes, seconds);
    }
    else {
      res = String.format("%dd%02d:%02d:%02d", days, hours, minutes, seconds);
    }
    return res;
  }


JEP 230:微基准套件

仅供参考,jep 230:Microenchmark套件是一个OpenJDK项目,用于:

Add a basic suite of microbenchmarks to the JDK source code, and make it easy for developers to run existing microbenchmarks and create new ones.

这个特性是在Java 12中实现的。

Java微基准线束(JMH)

对于Java的早期版本,请看JEP 230基于Java微基准HeaveS(JMH)项目。


1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
new Timer(""){{
    // code to time
}}.timeMe();



public class Timer {

    private final String timerName;
    private long started;

    public Timer(String timerName) {
        this.timerName = timerName;
        this.started = System.currentTimeMillis();
    }

    public void timeMe() {
        System.out.println(
        String.format("Execution of '%s' takes %dms.",
                timerName,
                started-System.currentTimeMillis()));
    }

}


你可以使用Perf4j。非常酷的实用程序。用法简单

1
2
3
4
5
6
7
8
9
10
String watchTag ="target.SomeMethod";
StopWatch stopWatch = new LoggingStopWatch(watchTag);
Result result = null; // Result is a type of a return value of a method
try {
    result = target.SomeMethod();
    stopWatch.stop(watchTag +".success");
} catch (Exception e) {
    stopWatch.stop(watchTag +".fail","Exception was" + e);
    throw e;
}

更多信息可在开发者指南中找到

编辑:项目似乎已死亡


我基本上做了一些变化,但是考虑到热点编译是如何工作的,如果你想得到准确的结果,你需要去掉前几个测量值,并确保你在现实世界中使用了这个方法(阅读特定于应用程序的)应用程序。

如果JIT决定编译它,那么您的数字将会有很大的变化。所以要注意


使用jcabi方面的aop/aspectj和@Loggable注释,可以轻松、紧凑地完成:

1
2
3
4
@Loggable(Loggable.DEBUG)
public String getSomeResult() {
  // return some value
}

对该方法的每次调用都将发送到具有DEBUG日志级别的slf4j日志设施。每个日志消息都将包含执行时间。


有几种方法可以做到这一点。我通常会选择这样的方式:

1
2
3
long start = System.currentTimeMillis();
// ... do something ...
long end = System.currentTimeMillis();

或者与System.NanoTime()相同;

对于基准测试方面的更多内容,似乎还有一个:http://jetm.void.fm/虽然从未尝试过。


我已经编写了一个方法,以一种可读性很强的形式打印方法执行时间。例如,要计算100万的阶乘,大约需要9分钟。因此执行时间打印为:

1
Execution Time: 9 Minutes, 36 Seconds, 237 MicroSeconds, 806193 NanoSeconds

代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
public class series
{
    public static void main(String[] args)
    {
        long startTime = System.nanoTime();

        long n = 10_00_000;
        printFactorial(n);

        long endTime = System.nanoTime();
        printExecutionTime(startTime, endTime);

    }

    public static void printExecutionTime(long startTime, long endTime)
    {
        long time_ns = endTime - startTime;
        long time_ms = TimeUnit.NANOSECONDS.toMillis(time_ns);
        long time_sec = TimeUnit.NANOSECONDS.toSeconds(time_ns);
        long time_min = TimeUnit.NANOSECONDS.toMinutes(time_ns);
        long time_hour = TimeUnit.NANOSECONDS.toHours(time_ns);

        System.out.print("
Execution Time:"
);
        if(time_hour > 0)
            System.out.print(time_hour +" Hours,");
        if(time_min > 0)
            System.out.print(time_min % 60 +" Minutes,");
        if(time_sec > 0)
            System.out.print(time_sec % 60 +" Seconds,");
        if(time_ms > 0)
            System.out.print(time_ms % 1E+3 +" MicroSeconds,");
        if(time_ns > 0)
            System.out.print(time_ns % 1E+6 +" NanoSeconds");
    }
}

Spring提供了一个实用程序类org.springframework.util.stopwatch,根据javadoc:

Simple stop watch, allowing for timing of a number of tasks, exposing
total running time and running time for each named task.

用途:

1
2
3
4
5
6
7
8
9
10
11
StopWatch stopWatch = new StopWatch("Performance Test Result");

stopWatch.start("Method 1");
doSomething1();//method to test
stopWatch.stop();

stopWatch.start("Method 2");
doSomething2();//method to test
stopWatch.stop();

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

输出:

1
2
3
4
5
6
StopWatch 'Performance Test Result': running time (millis) = 12829
-----------------------------------------
ms     %     Task name
-----------------------------------------
11907  036%  Method 1
00922  064%  Method 2

方面:

1
2
3
4
5
6
7
8
9
@Around("execution(* my.package..*.*(..))")
public Object logTime(ProceedingJoinPoint joinPoint) throws Throwable {
    StopWatch stopWatch = new StopWatch();
    stopWatch.start();
    Object retVal = joinPoint.proceed();
    stopWatch.stop();
    log.info(" execution time:" + stopWatch.getTotalTimeMillis() +" ms");
    return retVal;
}


如果你想要挂钟时间

1
2
3
4
long start_time = System.currentTimeMillis();
object.method();
long end_time = System.currentTimeMillis();
long execution_time = end_time - start_time;

正如"skaffman"所说,使用AOP或者可以使用运行时字节码编织,就像单元测试方法覆盖工具用来透明地向调用的方法添加时间信息一样。

您可以查看像emma这样的开源工具使用的代码(http://downloads.sourceforge.net/emma/emma-2.0.5312-src.zip)?modtime=1118607545&big_mirror=0)。另一个开源覆盖工具是http://prdownloads.sourceforge.net/cobertura/cobertura-1.9-src.zip?下载。

如果你最终能做到你想做的,请。与这里的社区分享你的蚂蚁任务/jar。


我修改了正确答案中的代码,以在几秒钟内获得结果:

1
2
3
4
5
6
7
long startTime = System.nanoTime();

methodCode ...

long endTime = System.nanoTime();
double duration = (double)(endTime - startTime) / (Math.pow(10, 9));
Log.v(TAG,"MethodName time (s) =" + duration);


1
2
3
4
long startTime = System.currentTimeMillis();
// code goes here
long finishTime = System.currentTimeMillis();
long elapsedTime = finishTime - startTime; // elapsed time in milliseconds

您可以使用提供各种测量仪器的度量库。增加依赖性:

1
2
3
4
5
6
7
<dependencies>
    <dependency>
        <groupId>io.dropwizard.metrics</groupId>
        metrics-core</artifactId>
        <version>${metrics.version}</version>
    </dependency>
</dependencies>

并为您的环境配置它。

方法可以用@timed:

1
2
3
4
@Timed
public void exampleMethod(){
    // some code
}

或用计时器包装的代码:

1
2
3
4
final Timer timer = metricsRegistry.timer("some_name");
final Timer.Context context = timer.time();
// timed code
context.stop();

聚合度量可以导出到console、jmx、csv或其他。

@Timed度量输出示例:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
com.example.ExampleService.exampleMethod
             count = 2
         mean rate = 3.11 calls/minute
     1-minute rate = 0.96 calls/minute
     5-minute rate = 0.20 calls/minute
    15-minute rate = 0.07 calls/minute
               min = 17.01 milliseconds
               max = 1006.68 milliseconds
              mean = 511.84 milliseconds
            stddev = 699.80 milliseconds
            median = 511.84 milliseconds
              75% <= 1006.68 milliseconds
              95% <= 1006.68 milliseconds
              98% <= 1006.68 milliseconds
              99% <= 1006.68 milliseconds
            99.9% <= 1006.68 milliseconds

您可以使用Spring Core项目中的秒表类:

代码:

1
2
3
4
5
StopWatch stopWatch = new StopWatch()
stopWatch.start();  //start stopwatch
// write your function or line of code.
stopWatch.stop();  //stop stopwatch
stopWatch.getTotalTimeMillis() ; ///get total time

秒表文件:简单的秒表,允许对许多任务计时,显示每个指定任务的总运行时间和运行时间。隐藏System.CurrentTimeMillis()的使用,提高应用程序代码的可读性,减少计算错误的可能性。请注意,此对象不是为线程安全而设计的,并且不使用同步。此类通常用于在概念验证和开发期间验证性能,而不是作为生产应用程序的一部分。


如果你只是想知道时间,你可以试试这个方法。

1
2
3
long startTime = System.currentTimeMillis();
//@ Method call
System.out.println("Total time [ms]:" + (System.currentTimeMillis() - startTime));

好的,这是一个简单的类,用于简单的函数计时。下面有一个例子。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
public class Stopwatch {
    static long startTime;
    static long splitTime;
    static long endTime;

    public Stopwatch() {
        start();
    }

    public void start() {
        startTime = System.currentTimeMillis();
        splitTime = System.currentTimeMillis();
        endTime = System.currentTimeMillis();
    }

    public void split() {
        split("");
    }

    public void split(String tag) {
        endTime = System.currentTimeMillis();
        System.out.println("Split time for [" + tag +"]:" + (endTime - splitTime) +" ms");
        splitTime = endTime;
    }

    public void end() {
        end("");
    }
    public void end(String tag) {
        endTime = System.currentTimeMillis();
        System.out.println("Final time for [" + tag +"]:" + (endTime - startTime) +" ms");
    }
}

使用样品:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
public static Schedule getSchedule(Activity activity_context) {
        String scheduleJson = null;
        Schedule schedule = null;
/*->*/  Stopwatch stopwatch = new Stopwatch();

        InputStream scheduleJsonInputStream = activity_context.getResources().openRawResource(R.raw.skating_times);
/*->*/  stopwatch.split("open raw resource");

        scheduleJson = FileToString.convertStreamToString(scheduleJsonInputStream);
/*->*/  stopwatch.split("file to string");

        schedule = new Gson().fromJson(scheduleJson, Schedule.class);
/*->*/  stopwatch.split("parse Json");
/*->*/  stopwatch.end("Method getSchedule");
    return schedule;
}

控制台输出示例:

1
2
3
Split time for [file to string]: 672 ms
Split time for [parse Json]: 893 ms
Final time for [get Schedule]: 1565 ms

在Java 8中,引入了一个新的类EDCOX1(6)。按照DOC:

Instant represents the start of a nanosecond on the time line. This
class is useful for generating a time stamp to represent machine time.
The range of an instant requires the storage of a number larger than a
long. To achieve this, the class stores a long representing
epoch-seconds and an int representing nanosecond-of-second, which will
always be between 0 and 999,999,999. The epoch-seconds are measured
from the standard Java epoch of 1970-01-01T00:00:00Z where instants
after the epoch have positive values, and earlier instants have
negative values. For both the epoch-second and nanosecond parts, a
larger value is always later on the time-line than a smaller value.

这可用于:

1
2
3
4
5
6
7
8
Instant start = Instant.now();
try {
    Thread.sleep(7000);
} catch (InterruptedException e) {
    e.printStackTrace();
}
Instant end = Instant.now();
System.out.println(Duration.between(start, end));

它打印PT7.001S


我机器的性能测量

  • system.nantime():750纳秒
  • system.currentTimeMillis():18纳秒

如前所述,System.NanoTime()被认为是用来测量经过的时间。只要知道成本,如果使用在一个循环或类似。


如果Java有更好的功能支持,那么需要被测量的动作可以被封装成一个块:

1
2
3
measure {
   // your operation here
}

在Java中,这可以通过匿名函数来完成,它看起来过于冗长。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
public interface Timer {
    void wrap();
}


public class Logger {

    public static void logTime(Timer timer) {
        long start = System.currentTimeMillis();
        timer.wrap();
        System.out.println("" + (System.currentTimeMillis() - start) +"ms");
    }

    public static void main(String a[]) {
        Logger.logTime(new Timer() {
            public void wrap() {
                // Your method here
                timeConsumingOperation();
            }
        });

    }

    public static void timeConsumingOperation() {
        for (int i = 0; i<=10000; i++) {
           System.out.println("i=" +i);
        }
    }
}


您可以使用JavaAgent来修改Java类字节,添加监视器代码动态。在GITHUB上有一些开源工具可以为您做到这一点。如果你想自己去做,只需实现javaagent,使用javassist修改你想要监视的方法,在你的方法返回之前修改监视代码。它是干净的,你可以监视你甚至没有源代码的系统。


以下是与谷歌搜索所用时间相似的打印好的字符串就绪格式秒数:

1
2
3
4
5
6
7
8
9
        long startTime = System.nanoTime();
        //  ... methodToTime();
        long endTime = System.nanoTime();
        long duration = (endTime - startTime);
        long seconds = (duration / 1000) % 60;
        // formatedSeconds = (0.xy seconds)
        String formatedSeconds = String.format("(0.%d seconds)", seconds);
        System.out.println("formatedSeconds ="+ formatedSeconds);
        // i.e actual formatedSeconds = (0.52 seconds)


我实现了一个简单的计时器,我认为它非常有用:

1
2
3
4
5
6
7
8
9
10
11
12
public class Timer{
    private static long start_time;

    public static double tic(){
        return start_time = System.nanoTime();
    }

    public static double toc(){
        return (System.nanoTime()-start_time)/1000000000.0;
    }

}

这样,您可以对一个或多个操作计时:

1
2
3
4
5
6
7
8
Timer.tic();
// Code 1
System.out.println("Code 1 runtime:"+Timer.toc()+" seconds.");
// Code 2
System.out.println("(Code 1 + Code 2) runtime:"+Timer.toc()+"seconds");
Timer.tic();
// Code 3
System.out.println("Code 3 runtime:"+Timer.toc()+" seconds.");

System.nanoTime()是一个非常精确的系统实用程序,用于测量执行时间。但是要小心,如果您在先发制人的调度器模式(默认)下运行,这个实用程序实际上测量的是墙壁时钟时间,而不是CPU时间。因此,您可能会注意到从运行到运行的不同执行时间值,这取决于系统负载。如果你寻找CPU时间,我认为在实时模式下运行你的程序会有好处。您必须使用rt-linux。链接:Linux实时编程


在JavaEE中工作的一个策略是:

  • 用用@AroundInvoke注释的方法创建类;

    1
    2
    3
    4
    5
    6
    7
    8
    9
    10
    11
    12
    @Singleton
    public class TimedInterceptor implements Serializable {

        @AroundInvoke
        public Object logMethod(InvocationContext ic) throws Exception {
            Date start = new Date();
            Object result = ic.proceed();
            Date end = new Date();
            System.out.println("time:" + (end.getTime - start.getTime()));
            return result;
        }
    }
  • 注释要监视的方法:

    1
    2
    @Interceptors(TimedInterceptor.class)
    public void onMessage(final Message message) { ...
  • 我希望这能有所帮助。