五分钟教你如何优雅的统计代码耗时,让你知道你的程序到底慢在哪!

如何优雅的统计代码耗时 一、前言 代码耗时统计在日常开发中算是一个十分常见的需求,特别是在需要找出代码性能瓶颈时。 可能也是受限于 Java 的语言特性,总觉得代码写起来不够优雅,大量的耗时统计代码,干扰了业务逻辑。特别是开发功能的时候,有个感受就是刚刚开发完代码很清爽优雅,结果加了一大堆辅助代码后,整个代码就变得臃肿了,自己看着都挺难受。因此总想着能不能把这块写的更优雅一点,今天本文就尝试探讨下“代码耗时统计”这一块。 在开始正文前,先说下前提,“代码耗时统计”的并不是某个方法的耗时,而是任意代码段之间的耗时。这个代码段,可能是 阅读详情

一、前言

代码耗时统计在日常开发中算是一个十分常见的需求,特别是在需要找出代码性能瓶颈时。

可能也是受限于 Java 的语言特性,总觉得代码写起来不够优雅,大量的耗时统计代码,干扰了业务逻辑。特别是开发功能的时候,有个感受就是刚刚开发完代码很清爽优雅,结果加了一大堆辅助代码后,整个代码就变得臃肿了,自己看着都挺难受。因此总想着能不能把这块写的更优雅一点,今天本文就尝试探讨下“代码耗时统计”这一块。

在开始正文前,先说下前提,“代码耗时统计”的并不是某个方法的耗时,而是任意代码段之间的耗时。这个代码段,可能是一个方法中的几行代码,也有可能是从这个方法的某一行到另一个被调用方法的某一行,因此通过 AOP 方式是不能实现这个需求的。

二、常规方法

2.1 时间差统计

这种方式是最简单的方法,记录下开始时间,再记录下结束时间,计算时间差即可。

public class TimeDiffTest {
    public static void main(String[] args) throws InterruptedException {
        final long startMs = TimeUtils.nowMs();

        TimeUnit.SECONDS.sleep(5); // 模拟业务代码

        System.out.println("timeCost: " + TimeUtils.diffMs(startMs));
    }
}

/* output: 
timeCost: 5005
*/
复制代码
public class TimeUtils {
    /**
     * @return 当前毫秒数
     */
    public static long nowMs() {
        return System.currentTimeMillis();
    }

    /**
     * 当前毫秒与起始毫秒差
     * @param startMillis 开始纳秒数
     * @return 时间差
     */
    public static long diffMs(long startMillis) {
       return diffMs(startMillis, nowMs());
    }
}
复制代码

这种方式的优点是实现简单,利于理解;缺点就是对代码的侵入性较大,看着很傻瓜,不优雅。

2.2 StopWatch

第二种方式是参考 StopWatch ,StopWatch 通常被用作统计代码耗时,各个框架和 Common 包都有自己的实现。

public class TraceWatchTest {
    public static void main(String[] args) throws InterruptedException {
        TraceWatch traceWatch = new TraceWatch();

        traceWatch.start("function1");
        TimeUnit.SECONDS.sleep(1); // 模拟业务代码
        traceWatch.stop();

        traceWatch.start("function2");
        TimeUnit.SECONDS.sleep(1); // 模拟业务代码
        traceWatch.stop();

        traceWatch.record("function1", 1); // 直接记录耗时

        System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));
    }
}

/* output: 
{"function2":[{"data":1000,"taskName":"function2"}],"function1":[{"data":1000,"taskName":"function1"},{"data":1,"taskName":"function1"}]}
*/
复制代码
public class TraceWatch {
    /** Start time of the current task. */
    private long startMs;

    /** Name of the current task. */
    @Nullable
    private String currentTaskName;

    @Getter
    private final Map<String, List<TaskInfo>> taskMap = new HashMap<>();

    /**
     * 开始时间差类型指标记录,如果需要终止,请调用 {@link #stop()}
     *
     * @param taskName 指标名
     */
    public void start(String taskName) throws IllegalStateException {
        if (this.currentTaskName != null) {
            throw new IllegalStateException("Can't start TraceWatch: it's already running");
        }
        this.currentTaskName = taskName;
        this.startMs = TimeUtils.nowMs();
    }

    /**
     * 终止时间差类型指标记录,调用前请确保已经调用
     */
    public void stop() throws IllegalStateException {
        if (this.currentTaskName == null) {
            throw new IllegalStateException("Can't stop TraceWatch: it's not running");
        }
        long lastTime = TimeUtils.nowMs() - this.startMs;

        TaskInfo info = new TaskInfo(this.currentTaskName, lastTime);

        this.taskMap.computeIfAbsent(this.currentTaskName, e -> new LinkedList<>()).add(info);

        this.currentTaskName = null;
    }

    /**
     * 直接记录指标数据,不局限于时间差类型
     *  @param taskName 指标名
     * @param data 指标数据
     */
    public void record(String taskName, Object data) {
        TaskInfo info = new TaskInfo(taskName, data);

        this.taskMap.computeIfAbsent(taskName, e -> new LinkedList<>()).add(info);
    }

    @Getter
    @AllArgsConstructor
    public static final class TaskInfo {
        private final String taskName;

        private final Object data;
    }
}
复制代码

我是仿照 org.springframework.util.StopWatch 的实现,写了 TraceWatch 类,这个方法提供了两种耗时统计的方法:

  • 通过调用 Start(name) 和 Stop() 方法,进行耗时统计。
  • 通过调用 Record(name, timeCost),方法,直接记录耗时信息。

这种方式本质上和“时间差统计”是一致的,只是抽取了一层,稍微优雅了一点。

注:你可以根据自己的业务需要,自行修改 TraceWatch 内部的数据结构,我这里简单起见,内部的数据结构只是随便举了个例子。

三、高级方法

第二节提到的两种方法,用大白话来说都是“直来直去”的感觉,我们还可以尝试把代码写的更简便一点。

3.1 Function

在 jdk 1.8 中,引入了 java.util.function 包,通过该类提供的接口,能够实现在指定代码段的上下文执行额外代码的功能。

public class TraceHolderTest {
    public static void main(String[] args) {
        TraceWatch traceWatch = new TraceWatch();

        TraceHolder.run(traceWatch, "function1", i -> {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        String result = TraceHolder.run(traceWatch, "function2", () -> {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
                return "YES";
            } catch (InterruptedException e) {
                e.printStackTrace();
                return "NO";
            }
        });

        TraceHolder.run(traceWatch, "function1", i -> {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        });

        System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));
    }
}

/* output: 
{"function2":[{"data":1004,"taskName":"function2"}],"function1":[{"data":1001,"taskName":"function1"},{"data":1002,"taskName":"function1"}]}
*/
复制代码
public class TraceHolder {
    /**
     * 有返回值调用
     */
    public static <T> T run(TraceWatch traceWatch, String taskName, Supplier<T> supplier) {
        try {
            traceWatch.start(taskName);

            return supplier.get();
        } finally {
            traceWatch.stop();
        }
    }

    /**
     * 无返回值调用
     */
    public static void run(TraceWatch traceWatch, String taskName, IntConsumer function) {
        try {
            traceWatch.start(taskName);

            function.accept(0);
        } finally {
            traceWatch.stop();
        }
    }
}
复制代码

这里我利用了 Supplier 和 IntConsumer 接口,对外提供了有返回值和无返回值得调用,在 TraceHolder 类中,在核心代码块的前后,分别调用了前文的 TraceWatch 的方法,实现了耗时统计的功能。

3.2 AutoCloseable

除了利用 Function 的特性,我们还可以使用 jdk 1.7 的 AutoCloseable 特性。说 AutoCloseable 可能有同学没听过,但是给大家展示下以下代码,就会立刻明白是什么东西了。

// 未使用 AutoCloseable
public static String readFirstLingFromFile(String path) throws IOException {
    BufferedReader br = null;
    try {
        br = new BufferedReader(new FileReader(path));
        return br.readLine();
    } catch (IOException e) {
        e.printStackTrace();
    } finally {
        if (br != null) {
            br.close();
        }
    }
    return null;
}

// 使用 AutoCloseable
public static String readFirstLineFromFile(String path) throws IOException {
    try (BufferedReader br = new BufferedReader(new FileReader(path))) {
        return br.readLine();
    }
}
复制代码

在 try 后方可以加载一个实现了 AutoCloseable 接口的对象,该对象作用于整个 try 语句块中,并且在执行完毕后回调 AutoCloseable#close() 方法。

让我们对 TraceWatch 类进行改造:

「1.实现 AutoCloseable 接口,实现 close() 接口:」

@Override
public void close() {
    this.stop();
}
复制代码

「2.修改 start() 方法,使其支持链式调用:」

public TraceWatch start(String taskName) throws IllegalStateException {
    if (this.currentTaskName != null) {
        throw new IllegalStateException("Can't start TraceWatch: it's already running");
    }
    this.currentTaskName = taskName;
    this.startMs = TimeUtils.nowMs();
    
    return this;
}
复制代码
public class AutoCloseableTest {
    public static void main(String[] args) {
        TraceWatch traceWatch = new TraceWatch();

        try(TraceWatch ignored = traceWatch.start("function1")) {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try(TraceWatch ignored = traceWatch.start("function2")) {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        try(TraceWatch ignored = traceWatch.start("function1")) {
            try {
                TimeUnit.SECONDS.sleep(1); // 模拟业务代码
            } catch (InterruptedException e) {
                e.printStackTrace();
            }
        }

        System.out.println(JSON.toJSONString(traceWatch.getTaskMap()));
    }
}

/* output: 
{"function2":[{"data":1001,"taskName":"function2"}],"function1":[{"data":1002,"taskName":"function1"},{"data":1002,"taskName":"function1"}]}
*/
复制代码

四、总结

本文列举了四种统计代码耗时的方法:

  • 时间差统计
  • StopWatch
  • Function
  • AutoCloseable

列举的方案是我目前能想到的方案。当然可能有更加优雅的方案,希望有相关经验的同学能在评论区一起交流下~

写在最后

欢迎大家关注我的公众号【风平浪静如码】,海量Java相关文章,学习资料都会在里面更新,整理的资料也会放在里面。

觉得写的还不错的就点个赞,加个关注呗!点关注,不迷路,持续更新!!!

来源:https://juejin.cn/post/6930853474380808206
perf record对C++程序耗时进行分析 本节将介绍如何使用perf工具的perf record对C++代码进行性能分析,一切操作都是在ubuntu 20下进行。 阅读详情

相关推荐

Python如何快速定位最慢的代码?

Python如何快速定位最慢的代码?

一名正义的白帽黑客 1076

java排查耗时代码的几种方式

(如果你的系统是windows执行不了这些命令,强烈建议装一个wsl1,IDEA支持把代码跑在wsl里了,调试起来会非常舒服,wsl2目前与windows本体的文件交换机制是网络传输,性能很差不建议。命令是用来获取当前的线程栈信息,也就是它的结果是瞬时的,如果我们的实际应用场景比较复杂,往往需要采样一段时间内的运行情况,再找出这段时间内总耗时最多的方法。这个火焰图是可以交互的,例如左上角的放大镜按钮可以用来高亮出包含关键字的矩形,如果方法调用层次太深,也可以点击某个矩形,只查看此矩形之上的部分。

imliuyu的博客 8912

SpringBoot 启动慢?那是因为你不知道它

前言 在 2021 年这个小学作文中的未来年份,没有想象中的汽车满天飞,也没有实现机器人满地跑。但牛逼的是我们都有一个共识: 知乎达到了人均 “谢邀~ 人在美国刚下飞机”的生活水平,虎扑的人均收入也在 30W+ ,还有就是程序员都人均精通 SpringBoot ,哪怕和算法聊技术一言不合就满嘴 SpringCould 分布式、微服务,然而实际操作可能是 分步试 、伪服务 ... 你一个小小系统开这么多应用启动不难受?(不难受因为可以装 13) SpringBoot 这启动速度也确实令人捉急,每个应用.

昨夜凛雨 1685

SpringBoot 中统计耗时的 7 种方式

除了 Spring,hutool 工具库和 Apache common 工具包都提供了各自的 StopWatch。hutool中的 StopWatch 就来自 Spring 的 StopWatch.java,用法也完全一致。除了可以通过局部时间,还可以通过 sw.getTotalTimeSeconds() 获取总的耗时。

我的博客 3009

告别手动脚本!用TSMaster Python小程序5分钟搞定CAN报文自动发送

本文介绍了如何使用TSMaster的Python小程序功能快速实现CAN报文自动发送,大幅提升汽车电子测试效率。通过简洁的Python代码示例,展示了如何替代传统手动操作和复杂C脚本,实现周期性发送、动态修改报文内容等常见需求,帮助工程师5分钟内完成自动化配置。

weixin_30292843的博客 512

2025年程序员核心能力:从写代码到系统架构思维

在AI加速普及的今天,‘会写代码’已成基础技能,真正决定工程师价值的是系统性工程能力。本文围绕领域理解、系统拆解、接口设计与成本感知四大元能力展开,解析如何将模糊需求转化为高可用、可维护、低认知负荷的技术实现。内容紧扣软件工程本质——用代码精准表达业务逻辑、控制复杂度、管理资源消耗,并支撑真实业务场景下的稳定性与扩展性。适合中高级开发者、技术负责人及希望突破执行层瓶颈的工程师进阶参考。

weixin_34357887的博客 423

快速验证claude code模型:五分钟构建待办事项应用原型

生成的代码包含三部分:HTML搭建页面结构,CSS美化样式,JavaScript处理交互逻辑。最惊喜的是,它自动把功能模块都分好了,比如单独的函数处理添加、删除、状态切换等操作。对于需要快速验证的场景,这种"描述需求-生成代码-实时调试-一键部署"的闭环确实高效。不过也要注意,AI生成的代码需要人工审查和优化,适合作为起点而非最终方案。就拿做个待办事项应用来说,从零开始写代码可能要花不少时间,但用AI生成原型真的能省下不少功夫。我的待办事项应用从生成到上线只用了不到十分钟,这种效率在传统开发中难以想象。

AmberLeopard26的博客 65

arduino uno spi驱动12864

arduino uno spi驱动12864

yvee的博客 2060

系统行为设计:从超时重试到优雅下线的工程化实践

在分布式系统中,‘功能正确’不等于‘行为可控’——真正的高可用取决于系统在流量突增、网络抖动、依赖故障等边界条件下的确定性响应。本文围绕超时建模、重试策略、优雅下线三大核心行为,解析如何基于P99.9延迟、网络RTT、安全边际等可观测数据进行量化设计,并通过可约束、可编排、可验证的工程手段落地。内容涵盖HikariCP连接池耗尽根因、gRPC服务滚动更新请求丢失、Chaos Mesh故障注入验证等典型场景,为后端工程师、SRE与Tech Lead提供一套可复用、可度量、可审计的行为设计方法论。

chiman6219的博客 383

CVE-2025-25249漏洞深度解析:FortiOS认证绕过与实战缓解

远程代码执行(RCE)是网络安全中最危险的漏洞类型之一,其核心往往源于认证机制失效或内存安全缺陷。CVE-2025-25249正属于此类高危漏洞——它不依赖用户凭证,也不需要交互触发,而是通过超长HTTP头(如X-Forwarded-For)引发内存越界写入,直接绕过FortiOS和FortiSwitchManager的完整认证流程。该漏洞影响广泛、CVSS评分高达9.8,凸显了嵌入式设备在HTTP协议栈实现中的深层风险。在企业网络中,FortiGate等设备常作为边界守门人,一旦失陷将导致root级控制权

weixin_33694620的博客 525

STM32CubeMX定时器进阶玩法:用编码器模式实现电机转速测量(附避坑清单)

本文详细介绍了如何使用STM32CubeMX配置定时器的编码器模式实现电机转速测量,包括硬件连接、CubeMX配置关键步骤、转速计算算法及常见问题解决方案。特别针对工业控制和机器人开发中的高精度需求,提供了实用的避坑清单和优化技巧,帮助开发者高效完成项目。

joy55的博客 203

智能体优雅关闭实战:Python信号处理与状态持久化

在分布式系统和长时运行服务中,优雅关闭(Graceful Shutdown)是保障服务可靠性的关键技术。其核心原理在于通过捕获系统信号(如SIGTERM、SIGINT),在进程终止前预留一个处理窗口,有序完成收尾工作。这项技术的价值在于防止数据丢失、资源泄漏和事务不一致,对于智能体、Web服务器等有状态应用至关重要。典型的应用场景包括服务滚动更新、扩缩容和故障恢复。本文聚焦于智能体生命周期管理的“最后五分钟”,通过Python asyncio和signal模块,详细演示了如何实现状态感知、平滑终止和资源清理

weixin_34062469的博客 374

【PINN物理信息网络】基于PINN物理信息网络求解泊松方程(python)

【PINN物理信息网络】基于PINN物理信息网络求解泊松方程(python)

k8291121的博客 2813

智能护理中心实时统计系统:从数据流处理到核心算法实现

实时数据处理是物联网和现代数据系统的核心,其原理在于对连续产生的数据流进行即时处理与分析,而非传统的批量处理。这项技术的价值在于能够实现秒级甚至亚秒级的业务响应,支撑实时监控、预警和决策。在智能护理中心等场景中,来自床垫传感器、环境监测设备和机器人的数据构成了典型的多源异构数据流。处理这类数据流的关键在于设计高效的单机流处理架构,并运用滑动窗口、增量计算等核心算法。本文以RoboCom大赛赛题为背景,深入探讨了如何利用Python的deque、defaultdict等数据结构构建高吞吐、低延迟的实时统计系统

weixin_33836874的博客 394

Unreal5从入门到精通之初学者碰到解决不了的问题怎么办?

在学习Unreal的过程中,你曾陷入困境了吗?对于我来说,这种情况发生得比我愿意承认的要多得多。幸运的是,除了一般地在网上搜索之外,还有很多地方可以获得虚幻引擎的帮助。

TxNet 980

Python的单元测试之unittest

是一个功能类似 unittest 的库,它集合众家标准测试库之所长,和 unittest 相比,功能更为强大,使用更为简单,OpenStack 广泛的利用它编写单元测试。unittest 提供了自动匹配发现并执行测试用例的功能,随着项目代码结构越发庞大,势必有多个测试文件,自动匹配发现并测试用例的功能在此就显得非常有用,只要满足。unittest 提供了丰富的命令行入口,可以根据需要执行某些特定的用例。的测试用例都会被 unittest 发现并执行,测试用例文件的默认匹配规则为 test*.py。

1、测试博主技术知识分享 2、pos行业知识技术分享 3、java后端技术知识分享 4、嵌入式技术知识分享 5、运维部署相关知识分享 283

五分钟上手LiteFlow:简化你的业务流程

本文主要说明LiteFlow快速入门学习

少年你真的要止步于此嘛。 2189

RK3588 开启多窗口

这样就支持多窗口了。

tzj1058756809的博客 268

Codex 与 Cursor 同场竞技:同一需求三套方案,耗时竟差 3 倍

通过真实需求对比 Codex、Cursor 和 Claude Code 的实战表现:Codex 凭借精准的算法理解在返工次数和总耗时上完胜。包含完整代码对比、耗时数据表、工具特性矩阵和七条选型军规,揭示不同 AI 编程工具的最佳使用场景与隐藏成本。

weixin_47315004的博客 14

嵌入式开发板OpenClaw部署:ESP32-S3与RISC-V平台性能评估

ESP32-S3与RISC-V平台在OpenClaw部署中的性能评估 本文对比分析了ESP32-S3和RISC-V两种嵌入式平台在OpenClaw部署中的性能表现。ESP32-S3凭借240MHz双核Xtensa LX7处理器和专用AI加速器,特别适合轻量级AI推理任务;而RISC-V凭借开源架构和模块化设计,可实现更高的能效比。文章详细介绍了ESP32-S3-DevKitC-1、SiFive HiFive1等典型开发板的硬件规格,推荐了ESP-IDF和FreeRTOS等操作系统,并提供了完整的开发环

博客 1128

温度PID控制PLC程序

温度PID稳定控制程序,适用于欧姆龙PLC,对其他PLC也具有很大参考价值

实时任意风格迁移:AdaIN算法在PyTorch中的优雅实现

当艺术创作遇上深度学习,一场关于风格与内容的革命悄然展开。想象一下,将蒙德里安的几何抽象风格应用到你的个人照片上,或者将毕加索的立体主义笔触融入城市风景——这一切不再是艺术家的专属,而是每个开发者都能实现的技术魔法。pytorch-AdaIN项目正是这场革命中的关键工具,它基于2017年ICCV的突破性论文,为实时任意风格迁移提供了高效、优雅的PyTorch实现。 ## 🎨 从理论突破到工程实

gitblog_00198的博客 212

假人有限元模型

假人模型,车辆碰撞假人有限元模型,适合做乘员安全的研究者利用

如何给开源项目发起提案

背景前段时间在使用 Pulsar 的 admin API 时,发现其中的一个接口响应非常慢:admin.topics().getPartitionedStats(topic);使用 curl 拿到的响应结果非常大,同时也非常耗时:具体的 issue 在这里:https://github.com/apache/pulsar/issues/21200后面经过分析,是因为某些 topic 的生产者和消费...

crossoverJie专栏 1003

Devexpress16.2.6破解工具

Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具Devexpress16.2.6破解工具

复分析 阿尔福斯(Ahlfors) 第三版答案 英文

答案分为十章,有部分习题没有答案,全英文复分析阿尔福斯(Ahlfors) 第三版答案

上一篇: MySQL:互联网公司常用分库分表方案汇总!
下一篇: 亿级流量架构之网关设计思路、常见网关对比
码趣资源网
博客等级 码龄4年 268粉丝 518原创
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

个

红包个数最小为10个

元

红包金额最低5元

当前余额3.43元 前往充值 >
需支付:10.00元
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付元
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值