Android 性能优化之黑科技开道(二)

news2024/12/25 18:05:06

3. 其它可以黑科技优化的方向

3.1 核心线程绑定大核

3.1.1 定义

核心线程绑定大核的思路也很容易理解,现在的 CPU 都是多核的,大核的频率比小核要高不少,如果我们的核心线程固定运行在大核上,那么应用性能自然会有所提升。

核心线程指的是 UI 线程、RenderThread 线程,因为它们直观影响用户的感受,或者在具体项目中的其它特定线程,比如语音处理,为了有更快的处理结果,语音线程也是可以列为核心线程的。

3.1.2 查看设备是否有大小核

1. 可以通过/sys/devices/system/cpu/目录下的文件获取各个核的频率

2. 尝试了下正在开发的设备,它没有大小核之分,所有核的频率全都一样,如下:

3. 当然,我们可以将此判断写到代码中,由我们的 App 智能判断是否需要绑定大小核,并找出来大核线程是哪个,具体代码这里就不贴了,原理同上,需要注意下读取权限问题

3.1.3 绑定 CPU 核实现

1. 绑定大核是通过函数 sched_setaffinity 实现的。

extern "C" JNIEXPORT void JNICALL Java_com_zj_android_startup_optimize_StartupNativeLib_bindCore(JNIEnv env, *jobject /* this */, jint thread_id, jint core) {
    cpu_set_t mask;     // CPU  核的集合
    CPU_ZERO(&mask);    // 将mask置空
    CPU_SET(core, &mask);    // 将需要绑定的  cpu  核设置给mask,核为序列0,1,2,3……
    if (sched_setaffinity(thread_id, sizeof(mask), &mask) == -1) { // 将线程绑核
        LOG  ("bind thread %d to core %d fail", thread_id, core);
    } else {
        LOG  ("bind thread %d to core %d success", thread_id, core);
    }
}

2. 如上所示,sched_setaffinity 共有 3 个参数。

  • 参数 1 是线程的 id,如果为 0 则表示主线程。
  • 参数 2 表示 cpu 序列掩码的长度。
  • 参数 3 则表示需要绑定的 cpu 序列的掩码。

3. 以上是线程绑定大核的核心代码,可以看到我们还需要获取 RenderThread 的 id ,以及 cpu 大核的序列。

4. 应用中线程的信息记录在 /proc/pid/task 的文件中,通过解析 task 文件就可以获取当前进程的所有线程,而 cpu 大核序列也可以通过解析 /sys/devices/system/cpu 目录实现。

3.2 GC 抑制

3.2.1 什么是 GC 抑制

  1. 首先 GC,就是 Java 的垃圾回收,GC 抑制指的是在 App 启动阶段,不让系统做 GC 或者是将 GC 的频繁降低,以提高启动速度

  2. 此技术在 Android10 以上的系统已加入,所以这里讨论的是 在 Android10 以下的系统中添加此功能

3.2.2 Android10 中的 GC 抑制如何实现的

1. Java 的垃圾回收机制,在 Android 5.0 之后,ART 取代了 Dalvik,ART 虚拟机在垃圾回收的时候虽然没有像 Dalvik 一样 stop the world,但在启动阶段如果发生垃圾回收,GC 线程同样抢占了不少系统资源。

2. Google 也注意到启动阶段 GC 对启动速度的影响,并在 Android 10 之后做了一定的优化,详情可见如下提交:https://cs.android.com/android/_/android/platform/art/+/a98a28262f645d100e2dee9587e7822d35ade6f9 

3. 可以看出,基本思路是在 2s 内提高后台 GC 的阈值,减少启动阶段的 GC 次数,根据 Google 的测试,抑制 GC 后效果如下:

4. 可以看出,GC 次数明显减少,启动速度也有一定的提升。

3.2.3 我们的程序是否有必要进行 GC 抑制

1. 可以通过以下代码获取 gc 的次数与耗时,方便统计 gc 对启动耗时的影响,以评估是否有必要做 GC 抑制

Debug.getRuntimeStat("art.gc.gc-count") // gc 次数
Debug.getRuntimeStat("art.gc.gc-time")  // gc 耗时
Debug.getRuntimeStat("art.gc.blocking-gc-count") // 阻塞 gc 次数
Debug.getRuntimeStat("art.gc.blocking-gc-time") // 阻塞 gc 耗时 

在电视项目的首页查看 GC 的情况,结果如下,发现从启动到首页显示出来,GC 次数和时间都是比较高的值:

2. 另外,我在 profiler 工具中观察到我们的 GC 线程可以更直观的看到,不只是在启动的时候,后续它也会频繁大量的运行,如下:

3.2.4 GC 抑制实现

GC 工作的原理

GC 主要是通过 HeapTaskDaemon 线程实现的,这是一个守护线程,在 Zygote 线程启动后这个线程也就启动了,启动后主要做了以下工作:

  1. 从 HeapTaskDaemon.runInternal()方法开始一步步调用到 native 层的 task_processor.RunAllTasks() 方法。

  2. 当 TaskProcessor 中的 tasks 为空时,会休眠等待,否则会取出第一个 HeapTask 并执行其 Run 方法。

    而 HeapTask 的 Run 方法是一个虚函数,需要子类来实现。

class HeapTask : public SelfDeletingTask {
};
 
class SelfDeletingTask : public Task {
};
 
class Task : public Closure {
};
 
class Closure {
 public:
  virtual ~Closure() { }
  // 定义 Run 虚函数
  virtual void Run(Thread* self) = 0;
};

HeapTask 就是垃圾回收的任务,有多个子类,比如最常见的 ConcurrentGCTask 就是其子类,在 Java 内存达到阈值时就会执行这个 Task,用于执行并发 GC。

GC 抑制方案:Native 层的 Hook

在了解了 HeapTaskDaemon 的执行流程之后,我们想到,如果启动时在 ConcurrentGCTask 的 Run 方法执行前休眠一段时间,不就可以实现 GC 抑制了吗?

而 Run 方法正好是虚函数,虚函数与 Java 中的抽象函数类似,留给子类去扩展实现多态。

虚函数和外部库函数一样都没法直接执行,需要在表中去查找函数的真实地址,那么我们是不是可以使用类似 PLT Hook 的思路,使用自定义函数的地址替换原有函数地址,实现 Hook 呢?

答案是肯定的,如上图所示,一个类中如果存在虚函数,那么编译器就会为这个类生成一张虚函数表,并且将虚函数表的地址放在对象实例的首地址的内存中。同一个类的不同实例,共用一张虚函数表的。

因此我们的主要思路如下:

  1. 启动时将虚函数表中的 Run 函数地址替换为自定义函数地址。

  2. 在自定义函数内部休眠一段时间,抑制 GC。

休眠完成后将虚函数表中的函数地址替换回来,避免影响后续执行。

3.3 字节码插桩与性能监控

3.3.1 性能监控的流程

基于性能问题,我们可以进行一个性能方面的监控,以达到随时了解情况,随时进行优化的目的。市场上有很多商业化的 APM 平台,比如著名的 NewRelic,还有国内的 听云、OneAPM 等等,还有我们自己也有性能监控平台。这些平台的工作流程如下:

  1. 首先在客户端(Android、iOS、Web 等)采集数据;

  2. 接着将采集到的数据整理上报到服务器;

  3. 服务器接收到数据后建模、存储、挖掘分析,让后将数据可视化,供用户使用。

其中客户端数据采集时使用字节码插桩比较方便快捷,并且具有较大的通用性

3.3.2 字节码插桩原理

字节码插桩的原理就是在 Android 打包的时候,通过 ASM 等框架将 Java 字节码,插入到特定位置上,达到自动加入某些重复代码的目的,也即是 AOP 编程,如下是 Android 打包的流程:

插桩入口

在打包过程中,会将所有 class 文件,包括第三方的 class 文件打包成一个或者多个 dex 文件。这其中涉及到两个很关键的环节:

javac:将 。java 格式的源代码文件编译成 class 文件;

dex: 将 class 格式的文件打包汇总,组成一个或者多个 dex 文件。

我们想要对字节码进行修改,只需要在 javac 之后 dex 之前遍历所有的字节码文件,并按照一定的规则过滤修改就好了,这里便是字节码插桩的入口。

那么我们到底如何介入打包过程,在 class 转换为 dex 文件的时候实现对字节码的修改呢?

答案是 transform api。Android Gradle Plugin 1.5.0 及以上版本,Google 官方提供了 transform api 作为字节码插桩的入口。我们只需要实现一个自定义的 Gradle Plugin,然后在编译阶段去修改字节码文件即可。

修改字节码

找到了插桩入口,接下来就要对字节码进行修改。对于字节码的修改,比较常用的框架有 Javassist 和 ASM。具体的使用就不进行介绍了,有框架使用的话,写字节码还是比较方便的。

4. 总结

本篇主要介绍了一些 Android 中实用的黑科技,包括 Hook 技术,线程自定义调整,GC 抑制,字节码插桩等,在电视版智家 App9.0 项目中已经验证了部分技术,还有一些技术正在规划中,后续将会逐步的提升我们的 App 性能。

最后,讨论一个问题,这些黑科技是"奇淫巧技"吗,还是合理合法的使用呢?

这里引用一篇文章中的原话:

国产定制安卓系统一直都在安卓版本号更新之前,领先不只一个身位。

以至于每次的安卓大版本更新像是在追授国产定制 Android 在 N 年前魔改的功勋,甚至像是在若干个发行版本选一个最好的方案作为整个 Android 生态的标准。

招安,才是最形象的解释。

参考:如何评价谷歌刚发布的 AOSP14,在 iOS 和鸿蒙的竞争下,安卓还有哪些第三方开发的系统亮点值得关注?

国内的 Android 黑科技一直是率先发展的,遍数国内 Android 技术圈走过的路程,从之前的插件化,到双开等,哪一个在当时不算是"奇淫巧技"呢,最后不都成了 Android 官方的标配了么,所以,大胆的探索去吧,能解决我们问题的技术就是好技术。

5. 参考

  1. 盘点 Android 常用 Hook 技术

  2. 如何优雅关闭 Android 日志输出

  3. Android 中如何 Hook 住 JNI 方法

  4. JNI 函数 Hook 实战

  5. 启动优化中的一些黑科技,了解一下~

  6. Android 性能监控系列一(原理篇)

  7. 如何评价谷歌刚发布的 AOSP14,在 iOS 和鸿蒙的竞争下,安卓还有哪些第三方开发的系统亮点值得关注?

6. 团队介绍

三翼鸟数字化技术平台-场景设计交互平台」主要负责设计工具的研发,包括营销设计工具、家电VR设计和展示、水电暖通前置设计能力,研发并沉淀素材库,构建家居家装素材库,集成户型库、全品类产品库、设计方案库、生产工艺模型,打造基于户型和风格的AI设计能力,快速生成算量和报价;同时研发了门店设计师中心和项目中心,包括设计师管理能力和项目经理管理能力。实现了场景全生命周期管理,同时为水,空气,厨房等产业提供商机管理工具,从而实现了以场景贯穿的B端C端全流程系统。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/1613849.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

使用Python比较两张人脸图像并获得准确度

使用 Python、OpenCV 和人脸识别模块比较两张图像并获得这些图像之间的准确度水平。 一、原理 使用Face Recognition python 模块来获取两张图像的128 个面部编码,并比较这些编码。比较结果返回 True 或 False。如果结果为True ,那么两个图像将是相同的…

社交媒体内容创新:Kompas.ai如何引领潮流

在数字营销的激烈竞争中,社交媒体平台已成为品牌与消费者互动的主要战场。随着用户对新鲜、有趣和互动性强的内容需求不断增长,品牌必须不断创新,以维持其在社交媒体上的影响力和吸引力。本文将深入探讨社交媒体平台上内容创新的必要性及其对…

【Python】使用Python计算简单数值积分

题外话,Python语言命名的来源:(见下图)Monty Python巨蟒剧团 1、积分题目(3) 2、解析解答 3、Python计算代码 import math import scipy.integrate as integrate# 积分区间 # x_min 0.0 # 1 # x_min …

【C语言】操作符相关编程题

目录 题目一: 题目二: 题目三: 题目三: 题目四: 题目五: 题目六: 题目七: 题目八: 题目一: 题目:不创建临时变量,交换两个数…

第一届 _帕鲁杯_ - CTF挑战赛

Mis 签到 题目附件: 27880 30693 25915 21892 38450 23454 39564 23460 21457 36865 112 108 98 99 116 102 33719 21462 21069 27573 102 108 97 103 20851 27880 79 110 101 45 70 111 120 23433 20840 22242 38431 22238 22797 112 108 98 99 116 102 33719 2…

Spring Boot集成zipkin快速入门Demo

1.什么zipkin Zipkin是一款开源的分布式实时数据追踪系统(Distributed Tracking System),基于 Google Dapper的论文设计而来,由 Twitter 公司开发贡献。其主要功能是聚集来自各个异构系统的实时监控数据。Zipkin默认支持Http协议&…

提取出图像的感兴趣区域

这是我们的原图像 将图像的数值统计后进行条形图展示 import matplotlib.pyplot as plt from PIL import Image import numpy as np# 图像路径 image_path r"D:\My Data\Figure\OIP.jpg"# 打开图像 image Image.open(image_path)# 将图像转换为numpy数组 image_ar…

Redis持久化策略揭秘:如何实现高可用!

【更多精彩内容,欢迎关注小米的微信公众号“软件求生”】 大家好!我是你们的小米,很高兴和大家分享Redis的持久化知识。Redis作为一款强大的内存数据库,经常被用于缓存和存储临时数据。然而,在很多场景中,我们希望Redis的数据能够持久保存,以备不时之需。那么,Redis如…

SpringBoot 操作 Redis

导入对应版本的依赖 <dependency><groupId>org.springframework.boot</groupId><artifactId>spring-boot-starter-data-redis</artifactId> </dependency>修改配置文件中的信息 spring:redis:host: 127.0.0.1port: 8888注意: 我这里 xsh…

SVD奇异值分解原理及应用

-------------------------------------------------------------------------------------------------------------------------------- 首先说明&#xff1a;本文的内容来自百家号“人工智能遇见磐创”大佬的整理&#xff0c;感谢原作者&#xff08;本文在原作者的基础上按…

影响钕铁硼磁钢性能的因素及方法

钕铁硼永磁材料自问世以来&#xff0c;就以其优越的磁性能而备受关注&#xff0c;被称为“磁王“&#xff0c;在市场需求的不断地增长下&#xff0c;钕铁硼生产工艺及磁体性能也不断发展和提升。我们一般用剩磁、矫顽力和最大磁能积这几个指标来衡量磁性材料的磁性能。 剩磁 B…

三. TensorRT基础入门-TensorRT简介

目录 前言0. 简述1. 什么是TensorRT2. TensorRT的工作流介绍3. TensorRT的一些限制总结参考 前言 自动驾驶之心推出的 《CUDA与TensorRT部署实战课程》&#xff0c;链接。记录下个人学习笔记&#xff0c;仅供自己参考 本次课程我们来学习课程第三章—TensorRT 基础入门&#xf…

2024年3月电风扇家电线上电商(京东天猫淘宝)销售数据排行榜

鲸参谋监测的线上电商&#xff08;京东天猫淘宝&#xff09;平台3月份的电风扇家电销售数据已出炉&#xff01; 根据鲸参谋数据显示&#xff0c;今年3月份&#xff0c;电风扇市场呈现出稳步增长态势。在线上电商平台上电风扇总销量累计约226万件&#xff0c;环比上个月上涨了2…

前程贷v6.5系统测试报告

1.引言部分 1&#xff0e;1 项目背景 本测试报告的具体编写目的&#xff0c;指出预期的读者范围。(3-4句) 项目描述 &#xff08;项目内容&#xff0c;用户需求&#xff09; 本测试报告为**&#xff08;系统名称&#xff09;**系统测试报告&#xff1b;本报告目的在于总结测试…

可平滑替代FTP的传输方案,需要具备哪些特质?

随着技术的发展和网络安全需求的提升&#xff0c;传统的FTP受安全性和效率方面的局限性&#xff0c;已经逐渐不能满足现代企业的需求。因此&#xff0c;许多企业和组织开始寻找替代FTP的方案&#xff0c;以提高文件传输的安全性、效率和便捷性。FTP传输存在的弊端及不足主要包括…

洛谷 A+B 问题 python

题目描述 输入两个整数 a,b&#xff0c;输出它们的和 输入格式 两个以空格分开的整数&#xff08;不是输入两个input&#xff08;&#xff09;解决&#xff09;。 输出格式 一个整数。 输入输出样例 输入 20 30 输出 50 这个问题的难点就是在于python当中进行两个输入数字…

JavaScript-3.DOM

通过HTML DOM,可以访问JavaScript HTML文档中的所有元素 DOM(Document Object Model) 当网页被加载时,浏览器会创建页面的文档对象模型(Document Object Model)。将网页内的元素封装成一个对象,并建立元素的层级关系,形似一棵树,称为DOM树。 通过可编程的对象模型,…

【简单介绍下Faiss原理和使用】

&#x1f3a5;博主&#xff1a;程序员不想YY啊 &#x1f4ab;CSDN优质创作者&#xff0c;CSDN实力新星&#xff0c;CSDN博客专家 &#x1f917;点赞&#x1f388;收藏⭐再看&#x1f4ab;养成习惯 ✨希望本文对您有所裨益&#xff0c;如有不足之处&#xff0c;欢迎在评论区提出…

【软考---系统架构设计师】软件架构

目录 1 一、软件架构的概念 二、软件架构风格 &#xff08;1&#xff09;数据流风格​​​​​​​ &#xff08;2&#xff09;调用/返回风格 &#xff08;3&#xff09;独立构件风格 &#xff08;4&#xff09;虚拟机风格 &#xff08;5&#xff09;仓库风格 三、架构…

知识图谱的起源与发展

文章目录 知识图谱的发展历史知识图谱的重要性知识图谱与Ontology、语义网络之间的区别知识图谱的定义 知识图谱的发展历史 知识图谱始于20世纪50年代&#xff0c;至今大致分为三个发展阶段&#xff1a;第一阶段 &#xff08;1955年—1977年&#xff09;是知识图谱的起源阶段&a…