Android性能优化系列腾讯matrix卡顿优化之ThreadPriorityTracer源码分析

2023年 10月 6日 27.5k 0

这是性能优化系列之matrix框架的第15篇文章,我将在性能优化专栏中对matrix apm框架做一个全面的代码分析,性能优化是Android高级工程师必知必会的点,也是面试过程中的高频题目,对性能优化感兴趣的小伙伴可以去我主页查看所有关于matrix的分享。

前言

这是matrix卡顿优化分析的最后一篇,在这之后我们将会开始matrix中其他类型性能优化的分析。我们用了10多篇的篇幅完整的介绍了matrix中卡顿优化相关的内容,总结下来卡顿监控的方向有三种:

  • 借助主线程消息队列提供的printer机制实现监控-Android性能优化系列-腾讯matrix-TracePlugin卡顿优化之LooperMonitor源码分析
  • hook IdleHandler实现监控-Android性能优化系列-腾讯matrix卡顿优化之IdleHandlerLagTracer源码分析
  • 是借助系统的输入系统机制实现触摸事件的卡顿监控-Android性能优化系列-腾讯matrix卡顿优化之TouchEventLagTracer源码分析

今天要进行的分析是线程优先级导致的卡顿优化。为什么说线程优先级会导致卡顿问题?因为不同线程要根据业务的优先级设置合适的优先级,试想假如主线程设置了极低的优先级,导致迟迟得不到cpu的调度,是不是会导致主线程执行缓慢呢?这就是为什么matrix中的卡顿监控也包含线程优先级这一块的原因。

接下来步入正题,继续从几个关键方法入手进行代码分析。

  • 构造方法
  • onStartTrace
  • onStopTrace

构造方法

没有操作

onStartTrace

onStartTrace会调用到onAlive,onAlive进入了native层做初始化。

@Override
protected void onAlive() {
    super.onAlive();
    nativeInitMainThreadPriorityDetective();
}

我们找到MatrixTracer.cc中找到nativeInitMainThreadPriorityDetective方法,可以看到,这里hook了两个方法,一个是设置线程优先级的setpriority方法,确保住主线程优先级不被设置的过低,如果对主线程设置过低的优先级(过高的nice值),就会被监控到,从而上报问题提示开发者;另一个hook的方法是TimerSlack的prctl方法,确保主线程的TimerSlack值不被设置的过大。

static void nativeInitMainThreadPriorityDetective(JNIEnv *env, jclass) {
    xhook_grouped_register(HOOK_REQUEST_GROUPID_THREAD_PRIO_TRACE, ".*\.so$", "setpriority",
            (void *) my_setpriority, (void **) (&original_setpriority));
    xhook_grouped_register(HOOK_REQUEST_GROUPID_THREAD_PRIO_TRACE, ".*\.so$", "prctl",
            (void *) my_prctl, (void **) (&original_prctl));
    xhook_refresh(true);
}

TimerSlack是Linux系统为了降低系统功耗,避免timer时间参差不齐,过于的频繁的唤醒cpu,而设置的一种对齐策略。如果系统设置了大于等于10的nice值,即设置了比后台优先级还要低的优先级,即把线程设置成了后台线程,那么系统就会设置一个比较高的TimerSlack,从默认的50微秒,提高到40毫秒,从而导致wait/sleep等挂起的时间多了40ms左右。详见Android的离奇陷阱 — 设置线程优先级导致的微信卡顿惨案。

hook点设置好后,看看拦截后做了什么。

my_setpriority

在设置优先级之前,先拿到之前的优先级,和当前即将更新的优先级一块传递到Java层,也就是调用到了ThreadPriorityTracer中的onMainThreadPriorityModified方法,将优先级变化的信息提示出来,这样开发者就能知道哪里更新了主线程优先级,从而规避问题。

int my_setpriority(int __which, id_t __who, int __priority) {
    if ((__who == 0 && getpid() == gettid()) || __who == getpid()) {
        int priorityBefore = getpriority(__which, __who);
        JNIEnv *env = JniInvocation::getEnv();
        env->CallStaticVoidMethod(gJ.ThreadPriorityDetective, gJ.ThreadPriorityDetective_onMainThreadPriorityModified, priorityBefore, __priority);
    }
    return original_setpriority(__which, __who, __priority);
}

my_prctl

当TimerSlack值被改变时,判断如果大于50000微妙,也就是50毫秒,超过这个值就调用到Java层上报信息,帮助开发人员规避问题。

int my_prctl(int option, unsigned long arg2, unsigned long arg3,
             unsigned long arg4, unsigned long arg5) {
    if(option == PR_SET_TIMERSLACK) {
        if (gettid()==getpid() && arg2 > 50000) {
            JNIEnv *env = JniInvocation::getEnv();
            env->CallStaticVoidMethod(gJ.ThreadPriorityDetective, gJ.ThreadPriorityDetective_onMainThreadTimerSlackModified, arg2);
        }
    }
    return original_prctl(option, arg2, arg3, arg4, arg5);
}

onStopTrace

没有特别的操作,不用关注

总结

线程优先级的监控实现比较简单,通过hook底层的setPriority(设置线程优先级的方法)和prctl(设置TimerSlack值得方法)方法,实现对线程优先级和线程TimerSlack值发生变化得监听,从而规避由线程优先级设置不当导致得问题。

至此我们对matrix中卡顿优化相关得内容就全部分析完了,建议读者可以再回顾一下前边这十几篇文章的内容,提炼总结一下卡顿监控方面的知识点,形成自己的知识网络,从而可以更好的应对面试中性能优化相关的题目。卡顿监控是性能优化中的重中之重,因为卡顿对用户来说是最直观的问题,最有可能导致用户流失,所以要重点留意。

相关文章

服务器端口转发,带你了解服务器端口转发
服务器开放端口,服务器开放端口的步骤
产品推荐:7月受欢迎AI容器镜像来了,有Qwen系列大模型镜像
如何使用 WinGet 下载 Microsoft Store 应用
百度搜索:蓝易云 – 熟悉ubuntu apt-get命令详解
百度搜索:蓝易云 – 域名解析成功但ping不通解决方案

发布评论