Swift 性能探索和优化分析

Swift 性能探索和优化分析

Apple 在推出 Swift 时就将其冠以先进,安全和高效的新一代编程语言之名。前两点在 Swift 的语法和语言特性中已经表现得淋漓尽致:像是尾随闭包,枚举关联值,可选值和强制的类型安全等都是 Swift 显而易见的优点。但是对于高效一点,就没有那么明显了。在 2014 年 WWDC 大会上 Apple 宣称 Swift 具有超越 Objective-C 的性能,甚至某些情况下可以媲美和超过 C。但是在 Swift 正式发布后,很多开发者发现似乎 Swift 性能并没有像宣传的那样优秀。甚至在 Swift 经过了一年半的演进的今天,稍有不慎就容易掉进语言性能的陷阱中。本文将分析一些使用 Swift 进行 iOS/OS X 开发时性能上的考量和做法,同时,笔者结合自己这一年多来使用 Swift 进行开发的经验,也给出了一些对应办法。

为什么 Swift 的性能值得期待

Swift 具有一门高效语言所需要具备的绝大部分特点。与 Ruby 或者 Python 这样的解释型语言不需要再做什么对比了,相较于其前辈的 Objective-C,Swift 在编译期间就完成了方法的绑定,因此方法调用上不再是类似于 Smalltalk 的消息发送,而是直接获取方法地址并进行调用。虽然 Objective-C 对运行时查找方法的过程进行了缓存和大量的优化,但是不可否认 Swift 的调用方式会更加迅速和高效。

另外,与 Objective-C 不同,Swift 是一门强类型的语言,这意味 Swift 的运行时和代码编译期间的类型是一致的,这样编译器可以得到足够的信息来在生成中间码和机器码时进行优化。虽然都使用 LLVM 工具链进行编译,但是 Swift 的编译过程相比于 Objective-C 要多一个环节 -- 生成 Swift 中间代码 (Swift Intermediate Language,SIL)。SIL 中包含有很多根据类型假定的转换,这为之后进一步在更低层级优化提供了良好的基础,分析 SIL 也是我们探索 Swift 性能的有效方法。

最后,Swift 具有良好的内存使用的策略和结构。Swift 标准库中绝大部分类型都是 struct,对值类型的使用范围之广,在近期的编程语言中可谓首屈一指。原本值类型不可变性的特点,往往导致对于值的使用和修改意味着创建新的对象,但是 Swift 巧妙地规避了不必要的值类型复制,而仅只在必要时进行内存分配。这使得 Swift 在享受不可变性带来的便利以及避免不必要的共享状态的同时,还能够保持性能上的优秀

对性能进行测试

这输入标题《计算机程序设计艺术》和 TeX 的作者高德纳曾经在论文中说过:

过早的优化是万恶之源。

在 Cocoa 开发中,对于性能的测试有几种常见的方式。其中最简单是直接通过输出 log 来监测某一段程序运行所消耗的时间。在 Cocoa 中我们可以使用 CACurrentMediaTime 来获取精确的时间。这个方法将会调用 mach 底层的 mach_absolute_time(),它的返回是一个基于 Mach absolute time unit 的数字,我们通过在方法调用前后分别获取两次时刻,并计算它们的间隔,就可以了解方法的执行时间:

let start = CACurrentMediaTime()

// ...

let end = CACurrentMediaTime()

print("测量时间:\(end - start)")

为了方便使用,我们还可以将这段代码封装到一个方法中,这样我们就能在项目中需要测试性能的地方方便地使用它了:

func measure(f: ()->()) {

let start = CACurrentMediaTime()

f()

let end = CACurrentMediaTime()

print("测量时间:\(end - start)")

}

measure {

doSomeHeavyWork()

}

优化手段,常见误用及对策

多线程、算法及数据结构优化

在确定了需要进行性能改善的代码后,一个最根本的优化方式是在程序设计层面进行改良。在移动客户端,对于影响了 UI 流畅度的代码,我们可以将其放到后台线程进行运行。Grand Central Dispatch (GCD) 或者 NSOperation 可以让我们方便地在不同线程中切换,而不太需要去担心线程调度的问题。一个使用 GCD 将繁重工作放到后台线程,然后在完成后回到主线程操作 UI 的典型例子是这样的:

let queue = dispatch_get_global_queue(QOS_CLASS_DEFAULT, 0)

dispatch_async(queue) {

// 运行时间较长的代码,放到后台线程运行

dispatch_async(dispatch_get_main_queue()) {

// 结束后返回主线程操作 UI

}

}

将工作放到其他线程虽然可以避免主线程阻塞,但它并不能减少这些代码实际的执行时间。进一步地,我们可以考虑改进算法和使用的数据结构来提高效率。根据实际项目中遇到的问题的不同,我们会有不同的解决方式,在这篇文章中,我们难以覆盖和深入去分析各种情况,所以这里我们只会提及一些共通的原则。

对于重复的工作,合理地利用缓存的方式可以极大提高效率,这是在优化时可以优先考虑的方式。Cocoa 开发中 NSCache 是专门用来管理缓存的一个类,合理地使用和配置 NSCache 把开发者中从管理缓存存储和失效的工作中解放出来。关于 NSCache 的详细使用方法,可以参看 NSHipster 关于这方面的文章以及 Apple 的相关文档。

在程序开发时,数据结构使用上的选择也是重要的一环。Swift 标准库提供了一些很基本的数据结构,比如 ArrayDictionary和 Set 等。这些数据结构都是配合泛型的,在保证数据类型安全的同时,一般来说也能为我们提供足够的性能。关于这些数据的容器类型方法所对应的复杂度,Apple 都在标准库的文档或者注释中进行了标记。如果标准库所提供的类型和方法无法满足性能上的要求,或者没有符合业务需求的数据结构的话,那么考虑使用自己实现的数据结构也是可选项。

如果项目中有很多数学计算方面的工作导致了效率问题的话,考虑并行计算能极大改善程序性能。iOS 和 OS X 都有针对数学或者图形计算等数字信号处理方面进行了专门优化的框架:Accelerate.framework,利用相关的 API,我们可以轻松快速地完成很多经典的数字或者图像处理问题。因为这个框架只提供一组 C API,所以在 Swift 中直接使用会有一定困难。如果你的项目中要处理的计算相对简单的话,也可以使用 Surge,它是一个基于 Accelerate 框架的 Swift 项目,让我们能在代码里从并行计算中获得难以置信的性能提升。

最编译器优化

Swift 编译器十分智能,它能在编译期间帮助我们移除不需要的代码,或者将某些方法进行内联 (inline) 处理。编译器优化的强度可以在编译时通过参数进行控制,Xcode 工程默认情况下有 Debug 和 Release 两种编译配置,在 Debug 模式下,LLVM Code Generation 和 Swift Code Generation 都不开启优化,这能保证编译速度。而在 Release 模式下,LLVM 默认使用 "Fastest, Smallest [-Os]",Swift Compiler 默认使用 "Fast [-O]",作为优化级别。我们另外还有几个额外的优化级别可以选择,优化级别越高,编译器对于源码的改动幅度和开启的优化力度也就越大,同时编译期间消耗的时间也就越多。虽然绝大部分情况下没有问题,但是仍然需要当心的是,一些优化等级采用的是激进的优化策略,而禁用了一些检查。这可能在源码很复杂的情况下导致潜在的错误。如果你使用了很高的优化级别,请再三测试 Release 和 Debug 条件下程序运行的逻辑,以防止编译器优化所带来的问题。

值得一提的是,Swift 编译器有一个很有用的优化等级:"Fast, Whole Module Optimization",也即 -O -whole-module-optimization。在这个优化等级下,Swift 编译器将会同时考虑整个 module 中所有源码的情况,并将那些没有被继承和重载的类型和方法标记为 final,这将尽可能地避免动态派发的调用,或者甚至将方法进行内联处理以加速运行。开启这个额外的优化将会大幅增加编译时间,所以应该只在应用要发布的时候打开这个选项。

虽然现在编译器在进行优化的时候已经足够智能了,但是在面对编写得非常复杂的情况时,很多本应实施的优化可能失效。因此保持代码的整洁、干净和简单,可以让编译器优化良好工作,以得到高效的机器码。

Swift 还是一门很新的语言,并且处于高速发展中。因为现在 Swift 只用于 Cocoa 开发,因此它和 Cocoa 框架还有着千丝万缕的联系。很多时候由于这些原因,我们对于 Swift 性能的评估并不公正。这门语言本身设计就是以高性能为考量的,而随着 Swift 的开源和进一步的进化,以及配套框架的全面重写,相信在语言层面上我们能获得更好的性能和编译器的支持。

最好的优化就是不用优化。在软件开发中,保证书写正确简洁的代码,在项目开始阶段就注意可能存在的性能缺陷,将可扩展性的考虑纳入软件构建中,按照实际需求进行优化,不要陷入为了优化而优化的怪圈,这些往往都可以让我们避免额外的优化时间,让我们的工作得更加愉快

未来CTO

 


关注我CTO之路从此开始

微信号:wlaicto


时间: 2024-11-04 15:21:39

Swift 性能探索和优化分析的相关文章

SqlServer性能检测和优化工具使用详细

原文:SqlServer性能检测和优化工具使用详细 工具概要 如果你的数据库应用系统中,存在有大量表,视图,索引,触发器,函数,存储过程,sql语句等等,又性能低下,而苦逼的你又要对其优化,那么你该怎么办?哥教你,首先你要知道问题出在哪里?如果想知道问题出在哪里,并且找到他,咱们可以借助本文中要讲述的性能检测工具--sql server profiler(处在sql安装文件--性能工具--sql server profiler) 如果知道啦问题出现在哪里,如果你又是绝世高手,当然可以直中要害,写

NET程序的性能要领和优化建议

前几天在老赵的博客上看到,Bill Chiles (Roslyn 编译器的Program Manager)写了一篇文章叫做<Essential Performance Facts and .NET Framework Tips>.这篇文章是一个14页的pdf,当时我是在地铁上在Lumia手机上看的,觉得很是不错,这里也建议大家直接下载阅读原文,我这里试着翻译一下,以加深自己印象,后面也有一些思考,以下是原文内容: ----------------------------------------

Linux服务器性能评估与优化(一)

网络内容总结(感谢原创) 1.前言简介 一.影响Linux服务器性能的因素 1. 操作系统级 性能调优是找出系统瓶颈并消除这些瓶颈的过程. 很多系统管理员认为性能调优仅仅是调整一下内核的参数即可解决问题, 事实上情况并不是这样. 性能调优是实现操作系统的各个子系统之间的平衡性,这些子系统包括: ?       CPU ?       内存 ?       磁盘I/O带宽 ?       网络I/O带宽 子系统之间相互依存,任何一个子系统的负载过度都能导致其他子系统出现问题,例如: * 大量的 p

(转)一个MySQL 5.7 分区表性能下降的案例分析

一个MySQL 5.7 分区表性能下降的案例分析 原文:http://www.talkwithtrend.com/Article/216803 前言 希望通过本文,使MySQL5.7.18的使用者知晓分区表使用中存在的陷阱,避免在该版本上继续踩坑.同时通过对源码的分享,升级MySQL5.7.18时分区表性能下降的根本原因,向MySQL源码爱好者展示分区表实现中锁的运用. 问题描述 MySQL 5.7版本中,性能相关的改进非常多.包括临时表相关的性能改进,连接建立速度的优化和复制分发相关的性能改进

Mysql 索引优化分析

MySQL索引优化分析 为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字段的意义.助你了解索引,分析索引,使用索引,从而写出更高性能的sql语句.还在等啥子?撸起袖子就是干! 案例分析 我们先简单了解一下非关系型数据库和关系型数据库的区别. MongoDB是NoSQL中的一种.NoSQL的全称是Not only SQL,非关系型数据库.它的特点是性能高,扩张性强,模式灵

mySql索引优化分析

MySQL索引优化分析 为什么你写的sql查询慢?为什么你建的索引常失效?通过本章内容,你将学会MySQL性能下降的原因,索引的简介,索引创建的原则,explain命令的使用,以及explain输出字段的意义.助你了解索引,分析索引,使用索引,从而写出更高性能的sql语句.还在等啥子?撸起袖子就是干! 案例分析 我们先简单了解一下非关系型数据库和关系型数据库的区别.MongoDB是NoSQL中的一种.NoSQL的全称是Not only SQL,非关系型数据库.它的特点是性能高,扩张性强,模式灵活

Oracle_Database_11gR2性能调整与优化pdf

下载地址:网盘下载 <Oracle Database 10g 性能调整与优化>提供了监控.分析和优化Oracle Database 10g的方法,由Oracle认证大师Richard J.Niemiec倾力打造.书中包含了详细的案例研究.最佳实践和丰富的Oracle新的调整特性的代码示例. 尼米克(Niemiec.R.J),Richard被Oracle公司授予Oracle认证大师--迄今为止,全世界仅有6人获得此殊荣,因此他是世界范围内公认的0racle专家.他是独立Oracle用户组(IOU

评估、监控、性能定位和优化 磁盘 IO 和 网络 IO

1.基本概念 1.1 吞吐量 1.1.1 基本概念 ????计算机系统的吞吐量是指流入,处理和流出系统的信息的速率:它取决于信息能够多快地输入内存,CPU能够多快地取指令,数据能够多快地从内存取出或存入,以及所得结果能够多快地从内存送给一台外围设备???? 1.1.2 吞吐量单位 ????吞吐量测试结果以 比特/秒 或 字节/秒 表示???? 1.1.2 吞吐量测试 ????在测试中以一定速率发送一定数量的帧,并计算待测设备传输的帧,如果发送的帧与接收的帧数量相等,那么就将发送速率提高并重新测试

磁盘 IO 和网络 IO 该如何评估、监控、性能定位和优化

生产中经常遇到一些IO延时长导致的系统吞吐量下降.响应时间慢等问题,例如交换机故障.网线老化导致的丢包重传:存储阵列条带宽度不足.缓存不足.QoS限制.RAID级别设置不当等引起的IO延时. 一.评估 IO 能力的前提 评估一个系统IO能力的前提是需要搞清楚这个系统的IO模型是怎么样的.那么IO模型是什么,为什么要提炼IO模型呢? (一).IO模型 在实际的业务处理过程中,一般来说IO比较混杂,比如说读写比例.IO尺寸等等,都是有波动的.所以我们提炼IO模型的时候,一般是针对某一个特定的场景来建