精确计算应用的冷启动耗时

news2024/11/12 9:27:55

在iOS项目中,冷启动时间是指从用户点击应用图标开始,到应用完全加载并呈现出第一个界面(可能需要网络请求必要的数据)所花费的时间。这里以 main 函数为界,分为两个时间段:

  • 从用户点击应用图标 ~ invoke main func
  • invoke main func ~ 首屏渲染完成

计算 main 函数调用之前的耗时

通过添加环境变量 DYLD_PRINT_STATISTICS,可以准确计算应用程序的 Pre-main Time(即在 main 函数之前的启动时间)。DYLD_PRINT_STATISTICS 环境变量由动态链接器 dyld 提供,用于输出启动过程中各种操作的时间统计信息。

使用 DYLD_PRINT_STATISTICS 计算 Pre-main Time

  1. 打开 Xcode 并选择你的项目
  2. 选择项目的 Scheme,点击 Edit Scheme…
  3. 在左侧选择 Run 选项卡,然后选择 Arguments 子选项卡。
  4. Environment Variables 部分,点击 + 按钮添加新的环境变量:
    • Name: DYLD_PRINT_STATISTICS
    • Value: 1
  5. 点击 Close 保存更改。

运行应用并查看输出

  1. 运行你的应用程序。在 Xcode 的调试控制台中,你会看到 dyld 输出的详细启动时间统计信息,包括以下几个部分:
    • Total pre-main time:从启动应用程序到调用 main 函数之间的总时间。
    • dylib loading time:加载动态库所花费的时间。
    • rebase/binding time:重定位和绑定符号的时间。
    • ObjC setup time:Objective-C 运行时环境的初始化时间。
    • initializer time:执行全局和静态变量的初始化时间。

解析输出示例

以下是调试控制台中可能看到的输出示例:

Total pre-main time: 200.21 milliseconds (100.0%)
         dylib loading time:  50.03 milliseconds (25.0%)
        rebase/binding time:  30.01 milliseconds (15.0%)
            ObjC setup time:  20.05 milliseconds (10.0%)
           initializer time: 100.12 milliseconds (50.0%)

解释输出

  • Total pre-main time:总的 Pre-main 时间,从应用程序启动到 main 函数开始执行的时间。这包括动态库加载、重定位和符号绑定、Objective-C 运行时初始化、以及全局和静态变量的初始化时间。
  • dylib loading time:加载动态库(dylibs)所花费的时间。
  • rebase/binding time:重定位(rebase)和符号绑定(binding)所花费的时间。
  • ObjC setup time:初始化 Objective-C 运行时环境所花费的时间。
  • initializer time:执行全局和静态变量初始化所花费的时间。

通过这些数据,可以了解应用启动过程中各个阶段的时间消耗,特别是 Pre-main 时间,这对于优化应用的启动性能非常有帮助。

优化建议

根据 Pre-main 时间的统计信息,可以针对性地进行优化,例如:

  1. 减少动态库数量:减少应用程序依赖的动态库数量,可以显著减少动态库加载时间。
  2. 优化符号绑定:通过减少符号绑定和重定位的数量,可以降低 rebase/binding 时间。
  3. 优化全局变量初始化:避免复杂和耗时的全局变量初始化,尽量推迟到实际使用时再初始化。
  4. 减少 Objective-C 类的数量:减少 Objective-C 类的数量和复杂性,可以降低 ObjC setup 时间。

通过这些优化措施,可以有效减少 Pre-main 时间,从而提升应用的启动速度。

小结

通过添加环境变量 DYLD_PRINT_STATISTICS,可以准确计算和分析应用的 Pre-main 时间。这为优化应用的启动性能提供了详细的参考数据,使开发者能够更有针对性地进行优化。


计算 main 函数到首屏渲染完成的耗时

main 函数到完全呈现出第一个界面的时间,特别是在第一个界面涉及网络请求的情况下,计算这一段时间可以通过以下几步来实现:

方法 1:使用 os_signpost API 记录时间

在应用的关键点添加 os_signpost 标记,并结合 Instruments 工具进行分析。

步骤 1:设置 os_signpost 标记

main 函数中和第一个界面呈现完成后添加 os_signpost 标记。

import os.signpost

let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Performance")

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    var signpostID = OSSignpostID(log: log)

    static func main() {
        os_signpost(.begin, log: log, name: "App Launch", signpostID: OSSignpostID(log: log))
        UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 初始化代码...
        return true
    }
}

class YourViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        // 假设这是进行网络请求并更新界面的方法
        fetchDataAndUpdateUI()
    }
    
    func fetchDataAndUpdateUI() {
        // 开始网络请求
        let log = OSLog(subsystem: Bundle.main.bundleIdentifier!, category: "Performance")
        let signpostID = OSSignpostID(log: log)
        os_signpost(.begin, log: log, name: "Fetch Data", signpostID: signpostID)
        
        // 模拟网络请求
        DispatchQueue.global().async {
            sleep(2) // 模拟网络延迟
            DispatchQueue.main.async {
                // 完成网络请求并更新UI
                os_signpost(.end, log: log, name: "Fetch Data", signpostID: signpostID)
                os_signpost(.end, log: log, name: "App Launch", signpostID: OSSignpostID(log: log))
                // 更新UI代码...
            }
        }
    }
}
步骤 2:使用 Instruments 工具进行分析
  1. 打开 Xcode 并选择你的项目
  2. 选择 Product > Profile 或使用快捷键 Command + I 启动 Instruments。
  3. 在 Instruments 中选择 Signpost Instrument 模板并点击 Choose
  4. 在 Instruments 界面中,点击 Record 按钮,启动你的应用。
  5. 在左侧的活动记录(Activity Trace)中,可以看到 App LaunchFetch Data 的开始和结束时间。

通过这种方法,可以准确地测量从 main 函数开始到第一个界面完全呈现出来的时间,包括网络请求所花费的时间。

方法 2:手动记录时间戳

在关键点手动记录时间戳,并计算总耗时。

步骤 1:记录时间戳

AppDelegate 和视图控制器中记录时间戳。

import UIKit

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate {
    var window: UIWindow?
    static var launchStartTime: TimeInterval?
    static var firstViewControllerLoadTime: TimeInterval?
    
    static func main() {
        launchStartTime = Date().timeIntervalSince1970
        UIApplicationMain(CommandLine.argc, CommandLine.unsafeArgv, nil, NSStringFromClass(AppDelegate.self))
    }

    func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey: Any]?) -> Bool {
        // 初始化代码...
        return true
    }
}

class YourViewController: UIViewController {
    override func viewDidAppear(_ animated: Bool) {
        super.viewDidAppear(animated)
        
        // 假设这是进行网络请求并更新界面的方法
        fetchDataAndUpdateUI()
    }
    
    func fetchDataAndUpdateUI() {
        // 开始网络请求
        let startTime = Date().timeIntervalSince1970
        
        // 模拟网络请求
        DispatchQueue.global().async {
            sleep(2) // 模拟网络延迟
            DispatchQueue.main.async {
                // 完成网络请求并更新UI
                let endTime = Date().timeIntervalSince1970
                if let launchStartTime = AppDelegate.launchStartTime {
                    let totalLaunchTime = endTime - launchStartTime
                    print("Total launch time including network request: \(totalLaunchTime) seconds")
                }
                // 更新UI代码...
            }
        }
    }
}
步骤 2:运行并查看输出
  1. 运行你的应用程序
  2. 在调试控制台中查看总的启动时间,包括网络请求的时间。

小结

通过上述方法,可以准确计算从 main 函数到第一个界面完全呈现出来的时间,尤其是在涉及网络请求的情况下。使用 os_signpost API 结合 Instruments 工具进行分析,可以提供更详细和精确的性能测量数据。而手动记录时间戳的方法则相对简单,但同样能提供一个大致的时间估计。

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

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

相关文章

Open3D 计算点云的平均密度

目录 一、概述 1.1基于领域密度计算原理 1.2应用 二、代码实现 三、实现效果 2.1点云显示 2.2密度计算结果 一、概述 在点云处理中,点的密度通常表示为某个点周围一定区域内的点的数量。高密度区域表示点云较密集,低密度区域表示点云较稀疏。计算…

【Linux】记录一起网站劫持事件

故事很短,处理也简单。权当记录一下,各位安全大大们手下留情。 最近一位客户遇到官网被劫持的情况,想我们帮忙解决一下(本来不关我们的事,毕竟情面在这…还是无偿地协助一下),经过三四轮“谦让…

数据结构--堆,堆排序

1.树概念及结构 1.1树的概念 树是一种 非线性 的数据结构,它是由 n ( n>0 )个有限结点组成一个具有层次关系的集合。 把它叫做树是因 为它看起来像一棵倒挂的树,也就是说它是根朝上,而叶朝下的 。 有一个 特殊的结…

纳米尺度表面分析:微区XPS优势-测试狗材料测试

纳米尺度表面分析:微区XPS优势 随着科学技术的不断发展,纳米技术在各个领域得到了广泛的应用,纳米尺度表面分析作为纳米技术研究的重要手段,对于揭示材料微观结构、理解材料性能与功能机制具有重要意义;在这其中&#…

群体优化算法---文化算法介绍,求解背包问题

介绍 文化算法(Cultural Algorithm, CA)是一种基于文化进化理论的优化算法,首次由Robert G. Reynolds在20世纪90年代提出。文化算法通过模拟人类社会中的文化进化过程,利用个体与群体的双重进化机制来解决优化问题。其基本思想是…

S271系列RTU在旅游景区人流监控中的应用案例

S271系列RTU在旅游景区人流监控中的应用案例 随着全球旅游业的迅猛发展,旅游景区的管理者越来越关注如何利用先进的技术手段提升游客体验、优化管理效率以及确保安全。S271系列RTU作为一款先进的无线工业物联网设备,在旅游景区的人流监控中展现出了其独…

240708_昇思学习打卡-Day20-MindNLP ChatGLM-6B StreamChat

240708_昇思学习打卡-Day20-MindNLP ChatGLM-6B StreamChat 基于MindNLP和ChatGLM-6B实现一个聊天应用,本文进行简单记录。 环境配置 %%capture captured_output # 实验环境已经预装了mindspore2.2.14,如需更换mindspore版本,可更改下面mi…

物联网实训室建设可行性报告

一、建设物联网实训室的目的和意义 随着信息技术的快速发展,物联网(IoT)已成为推动社会进步和经济发展的关键技术之一。物联网技术的集成应用,不仅能够提高生产效率,还能促进智慧城市、智能家居、智能农业等多个领域的…

vscode 如何配置快速生成 vue3 模板

(1)点击 vscode 左下角的齿轮设置按钮,点击用户代码片段 (2)输入 vue,选择 vue.json 文件 (3)在注释下添加如下代码即可 {"Print to console": {"prefix": &q…

vue学习day02-Vue指令-v-html、v-show与v-if、v-else与v-else-if、v-on、v-bind、v-for、v-model

6、Vue指令 指令:带有v-前缀的特殊标签属性 (1)v-html 作用:设置元素的innerHTML 语法:v-html“表达式” 示例: 提供一个地址,这里是百度的地址,通过v-html渲染 结果&#xff…

最新国内免费使用GPT4o、4.0、3.5 的方法

为了方便大家对GPT有更好的了解,这里特地整理了一个表格做对比 这些模型展示了OpenAI在自然语言处理领域的持续进步,每一代模型都在理解和生成能力、效率和适用性方面进行了显著提升。 网站汇总 这里顺便给大家汇总一下国内同类型的网站,有…

python--实验4 字符串与正则表达式

一、实验目的: 熟悉字符串切片、运算等操作掌握常用字符串操作函数和方法掌握正则表达式 二、实验内容: 说明:基础题为必做题,提高题为选做题 1、 (基础题) 编写程序,要求用户输入一个身份证号,根据身份…

Java面试八股之MySQL支持哪些数据类型

MySQL支持哪些数据类型 MySQL支持多种数据类型,这些类型可以大致分为三大类:数值类型、日期/时间类型和字符串类型。下面是一些常见的数据类型及其用途: 数值类型 整数类型: TINYINT:通常占用1字节。 SMALLINT&am…

11 个例子讲清spark提交命令参数

目录 提交命名参数详情为什么有这么多参数如何开始学习一些具体的例子1. 基本的Spark应用提交2. 提交带有依赖的Python脚本3. 运行Spark SQL作业4. 提交Spark Streaming作业5. 使用外部包运行Spark作业6. 动态资源分配7. 使用多个配置文件8. GPU 支持9. 自定义日志配置10. 使用…

【nginx】nginx的配置文件到底是什么结构,到底怎么写?

背景:我window中下载了一个nginx,想要通过nginx来对本地的两个项目做动态代理,但是没想到下载启动都没遇见什么问题,但是在配置nginx.conf配置文件时,遇见了很多问题,查了好久没查到什么特别有用的内容&…

微信小程序开发跳转京东,淘宝小程序

没有淘宝小程序,所以只能提示他复制链接网页打开 跳转京东小程序 获取京东小程序 京东小程序appId:wx91d27dbf599dff74 或者点开京东小程序,查看详情即可获取到京东的appid 店铺页面路径 店铺首页:pages/shop/index/index?…

无人机在交通管理方面的应用与潜力

随着智能化和数字化技术的发展,无人机已经成为智慧交通管理体系中的重要一环。无人机能够搭载各种专业设备,如超清摄像头、红外热成像摄像头、目标跟踪器等,从而完成多任务的数据采集和快速机动的任务执行。这些数据通过无线传输实时回传&…

深圳航空x-s3-s4e逆向和顶象滑块动态替换问题

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用,不用于其他任何目的,抓包内容、敏感网址、数据接口等均已做脱敏处理,严禁用于商业用途和非法用途,否则由此产生的一切后果均与作者无关! 前言(lianxi a…

Redis基础教程(十六):Redis Stream

💝💝💝首先,欢迎各位来到我的博客,很高兴能够在这里和您见面!希望您在这里不仅可以有所收获,同时也能感受到一份轻松欢乐的氛围,祝你生活愉快! 💝&#x1f49…

windows10下的游戏怎么卸载?

在Windows 10中卸载游戏可以通过多种途径进行,下面是一些常见的方法: 方法一:通过“设置”应用卸载 1. 点击左下角的“开始”按钮,打开“开始”菜单。 2. 选择“设置”图标(齿轮形状)。 3. 在“设置”窗…