Swift 性能探索和优化分析

本文首发在 CSDN《程序员》杂志,订阅地址 http://dingyue.programmer.com.cn/
 
详细访问:http://bbs.a-coder.cn/thread-50-1-1.html
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 的数字,我们通过在方法调用前后分别获取两次时刻,并计算它们的间隔,就可以了解方法的执行时间:
  1. let start = CACurrentMediaTime()
  2. // ...
  3. let end = CACurrentMediaTime()
  4. print("测量时间:\(end - start)")
复制代码
为了方便使用,我们还可以将这段代码封装到一个方法中,这样我们就能在项目中需要测试性能的地方方便地使用它了:
  1. func measure(f: ()->()) {
  2.     let start = CACurrentMediaTime()
  3.     f()
  4.     let end = CACurrentMediaTime()
  5.     print("测量时间:\(end - start)")
  6. }
  7. measure {
  8.     doSomeHeavyWork()
  9. }
复制代码
CACurrentMediaTime 和 log 的方法适合于我们对既有代码进行探索,另一种有效的方法是使用 Instruments 的 Time Profiler 来在更高层面寻找代码的性能弱点。将程序挂载到 Time Profiler 后,每一个方法调用的耗时都将被记录。
当我们寻找到需要进行优化的代码路径后,为其建立一个单元测试来持续地检测代码的性能是很好的做法。在 Xcode 中默认的测试框架 XCTest 提供了检测并汇报性能的方法:measureBlock。通过将测试的代码块放到 measureBlock 中,Xcode 在测试时就会多次运行这段代码,并统计平均耗时。更方便的是,你可以设定一个基准,Xcode 会记录每次的耗时并在性能没有达到预期时进行提醒。这保证了随着项目开发,关键的代码路径不会发生性能上的退化。
  1. func testPerformance() {
  2.     measureBlock() {
  3.         // 需要性能测试的代码
  4.     }
  5. }
复制代码
优化手段,常见误用及对策 多线程、算法及数据结构优化,
编译器优化 等内容,请访问:http://bbs.a-coder.cn/thread-50-1-1.html
 

你可能感兴趣的:(ios,移动开发,swift,苹果)