FunTester CPU 火焰图初探 - 优化 0.1%

FunTester · 2022年12月14日 · 3160 次阅读

早就听过 CPU 火焰图的强大功能,也听过几个火焰图工具,今天终于开始尝试使用 CPU 火焰图生成工具。

奈何由于各种原因,Intellij 自带的火焰图插件并不能用,着实让人不快。故而找到一个 async-profiler 分析工具作为替代品。

当时正在测试随机数性能的,所以就用了一个动态 QPS 模型的 Case,学习了 async-profiler 的使用。很意外地发现了一个性能可以优化的地方。经过尝试,CPU 使用率降低了 0.24%,也算是第一个成果了。

async-profiler

这个工具安装和使用教程,可以网上搜一下,建议去 Github 仓库看看 Wiki,这里我就不多说。

Case code

下面是 Case 的代码,用了动态 QPS 模型。

class T extends SourceCode {

    static void main(String[] args) {
        def total = 1000_0000
        def index = new AtomicInteger()
        int i = 0
        def test = {
            i++ % total
            //            index.getAndIncrement() % total
            getRandomInt(total)
            sleep(0.01)
        }
        new FunQpsConcurrent(test, "测试随机性能").start()
    }
}

下面是执行任务的方法com.okcoin.hickwall.presses.funtester.frame.execute.FunQpsConcurrent#start代码:

void start() {
    if (executor == null) executor = ThreadPoolUtil.createCachePool(Constant.THREADPOOL_MAX, "Q")
    if (Common.PERF_PLATFORM) controller = new RedisController(this)
    if (controller == null) controller = new FunTester();
    new Thread(controller, "receiver").start();
    while (key) {
        ThreadPoolUtil.executeTask(executor, qps, produce, total, name)
    }
    stop()
}

优化过程

整个main线程差不多都在上面那个while循环中。我先生成了火焰图,看了main的火焰图,如下:

CPU火焰图(优化前)

可以看出com.okcoin.hickwall.presses.funtester.frame.execute.ThreadPoolUtil#executeTask方法使用了 0.53% 的 CPU,这里看到一个getSecond方法里面使用 CPU 最多,而且创建了Calendar对象,代码如下:

if (Time.getSecond() % COUNT_INTERVAL == 0) {
    int real = total.sumThenReset() / COUNT_INTERVAL as int
    def active = executor.getActiveCount()
    def count = active == 0 ? 1 : active
    log.info("{} design QPS:{},actual QPS:{} active thread:{} per thread efficiency:{}", name, qps, real, active, real / count as int)
}

这里本意是为了间隔几秒输出当前的设计 QPS、实际 QPS、活跃线程数等信息。这里我想是不是可以直接用时间戳实现这个需求,应该会更快。所以修改后的代码如下:

if (SourceCode.getMark() % COUNT_INTERVAL == 0) {
    int real = total.sumThenReset() / COUNT_INTERVAL as int
    def active = executor.getActiveCount()
    def count = active == 0 ? 1 : active
    log.info("{} design QPS:{},actual QPS:{} active thread:{} per thread efficiency:{}", name, qps, real, active, real / count as int)
}

改完之后重新跑了一次,抓取火焰图,main线程部分如下:

CPU火焰图(优化后)

com.okcoin.hickwall.presses.funtester.frame.execute.ThreadPoolUtil#executeTask方法 CPU 使用率降低到 0.29%,但是多了一部分使用使用,整体com.okcoin.hickwall.presses.funtester.frame.execute.FunQpsConcurrent#start方法的 CPU 使用率是 0.44%,相比较之前的 0.53% 降低了 0.09%。

四舍五入也就优化了 0.1%,也算是优化效果,有所收获。然后我突然发现整个火焰图的中 CPU 占用的大多数都是那个sleep方法,可能这个之前性能测试中的随机数性能问题探索中的结论可能不够明显,甚至忽略了三个方式的差异。后续我重新设计用例测一遍。

如果觉得我的文章对您有用,请随意打赏。您的支持将鼓励我继续创作!
暂无回复。
需要 登录 后方可回复, 如果你还没有账号请点击这里 注册