优雅的实现程序计时器

Posted1 by 呼延十 on April 28, 2019 Hot:

日常编码中,如果想对某一段程序计时,应该怎么做呢?比较简单粗暴的办法就是开始和结束各自取当前时间戳.

        long start = System.currentTimeMillis();
        dosomething();
        System.out.println("做了一些事情:" + (System.currentTimeMillis() - start));

其实看起来还好对不对,我偶尔这么用一下也觉得还好,知道某一次维护某一份代码.

前任因为需要对程序的性能做一些优化,所以要找到程序耗时较高的部分,所以几个主要的类里面到处是这种代码,在他进行优化完毕之后并没有进行删除.

而我当时程序已经运行良好了,需要进行代码clean(因为实在是太丑了),一个个删除实在是累skr人.

而且long start = System.currentTimeMillis();这一句实在是不好找,因为他没有引用别的参数,所以并不会有编译器来提醒我哪里还有遗漏的,导致在那之后好久都会偶尔又找到一条呢!

而我在日常写一些奇怪的类的时候,也有打印耗时的需求,比如我前面一些博客里面,说怎么操作优化了效率,总不能空口白话,因此也需要经常的打印程序耗时.我感觉到上面的这种粗暴的办法太傻了,但是也没想到什么好办法,知道看到了Ticker类.

这个类的实现功能是,可以对程序分段计时并标注,并且将代码封装起来,尽量少的侵入业务代码,同时最后以较好的可读性打印出来.

实现方法,维护一个<string,long>的list,注意前面这么写只是代表了一个对象,而不是一个Map.用户每次手动调用计时的时候,计算与前一次计时之间的间隔时间,将其保存起来,同时,Ticker保存初始化的时间,以及最终调用toString的时间,因此你可以很清楚的看到一个类似于:

thing1: 10ms
thing2: 20ms
total: 30ms

这样子的输出.

下面是类的代码以及使用示例:

package util;

import java.util.ArrayList;
import java.util.List;

public class Ticker {

    private final List<T> tags = new ArrayList<>(2);

    private final long start;
    private long last;
    private long end;

    public Ticker() {
        start = System.currentTimeMillis();
        last = start;
    }

    public void tack() {
        last = System.currentTimeMillis();
    }

    public void tick(String tag) {
        long now = System.currentTimeMillis();
        tags.add(new T(tag, (int) (now - last)));
        last = now;
    }

    public int total() {
        if (end == 0)
            end = System.currentTimeMillis();
        return (int) (end - start);
    }

    public long getBegin() {
        return start;
    }

    @Override
    public String toString() {
        return summary();
    }

    private String summary() {
        end = System.currentTimeMillis();
        StringBuilder sb = new StringBuilder(32);
        for (T tag : tags) {
            sb.append(tag.tag).append(" ").append(tag.since).append("ms, ");
        }

        sb.append("total ");
        if ((end - start) < 10000) {
            sb.append(end - start).append("ms");
        } else {
            sb.append((end - start) / 1000).append("s");
        }
        return sb.toString();
    }

    static class T {
        final String tag;
        final int since;

        T(String tag, int since) {
            this.tag = tag;
            this.since = since;
        }
    }

    private static void db() throws InterruptedException {
        Thread.sleep(1000);
    }

    private static void col() {
        for (int i = 0; i < 10000; ++i) {
            int j = i;
        }
    }


    public static void main(String[] args) throws InterruptedException {
        Ticker ticker = new Ticker();
        db();
        ticker.tick("db");
        col();
        ticker.tick("col");
        System.out.println(ticker.toString());
    }
}


main方法中的测试代码输出:

db 1005ms, col 1ms, total 1006ms

是不是感觉好很多了呢.

完全没有什么依赖,没有什么副作用,copy到项目里就能用,眼睛舒服了许多.


完。





ChangeLog

2019-04-28 完成

以上皆为个人所思所得,如有错误欢迎评论区指正。

欢迎转载,烦请署名并保留原文链接。

联系邮箱:huyanshi2580@gmail.com

更多学习笔记见个人博客——>呼延十