许多人将性能等同于速度。事实上,如果一个程序能在一秒钟内完成一项复杂的操作,你可能会认为该程序性能良好。可是,这里这个速度可能是一种误导性的测量。在复杂的软件系统中,操作速度不是固定值。如果您在不同条件下多次执行相同的操作,则完成该操作所需的时间可能差异很大。这是因为程序只是在本地系统上共享资源的许多进程之一,并且这些资源的使用(或滥用)会影响所有其他进程。

以下部分用两个不同的概念来解释性能:有效的资源使用和感知的性能。这两个概念对您设计和实现应用程序的方式都有重要影响,并且了解如何使用它们可以提高整体性能。

The Efficient Use of Resources
计算机在所有正在运行的进程中共享有限数量的资源。在最底层,这些资源分解为以下几类:
• CUP 时间
• 内存空间
• 大存储空间

所有数据都驻留在内存中或某种大容量存储设备上,并且必须由CPU操作。一个高效的应用程序仔细使用所有这些资源。以下各节提供了有关每种资源及其对程序的影响的更多详细信息。

CUP时间
CPU时间由系统分配,因此您必须尽可能地利用您的时间。由于OS X和iOS都实现了对称多处理,因此系统上的每个线程都会分配一段时间(最多10毫秒),以便在其中运行。在那段时间结束的时候(或者在很多情况下),系统收回CPU的控制权并将其交给另一个线程。

在一个具有许多活动线程的典型系统上,如果每个线程都使用其全部分配时间,那么性能会非常糟糕。这导致编写应用程序最重要的目标之一:

目标:如果你的程序无关,它不应该占用CPU时间。

实现这一目标的最佳方式是使用基于事件的模型。使用现代事件处理系统(例如Cocoa和iOS中的系统)意味着您的程序的线程仅在需要完成的工作时才运行(比如runloop,比如dispatch source)。

当你的应用程序有工作要做时,它应该尽可能有效地利用CPU时间。这意味着要选择适合您希望处理的数据量的算法。它还意味着使用其他系统资源来执行专门的操作,从而实现以下目标:

目标:尽可能将工作移出CPU。

内存空间 memory space
现代计算机硬件上的内存通常由逐渐变慢(但较大)的内存组成。CPU可用的最快内存是CPU自己的寄存器。第二快的是L1高速缓存,随后是L2和L3高速缓存。下一个最快的内存是主内存。所有最慢的内存都由OS X中驻留在磁盘上的虚拟内存页面组成,并且必须在可以使用之前分页。

在理想的世界中,每个应用程序都足够小以适应系统最快的缓存内存。不幸的是,应用程序的大部分代码和数据都驻留在主存储器或磁盘上。因此,应用程序的代码和数据的组织方式应尽量减少在这些较慢的媒体上花费的时间,这将导致以下目标:

目标:减少程序的内存占用量。

减少程序的内存占用可显着提高其性能。小内存占用通常有两个好处。首先,程序越小,占用的内存页越少。内存页面越少,通常意味着分页越少。其次,由于更加严格的优化和更好的组织,代码通常更小。因此,需要更少的指令来执行给定的任务,并且该任务的所有代码都集中在同一组内存页面上。

除了减少应用程序的内存占用量外,还应该尝试减少应用程序中可写内存页面的占用空间。可写的内存页面为你的程序存储全局的或者初始化的数据。在OS X中,如果需要,可以将这些页面写入磁盘,但这样做很慢。在这两种情况下,系统释放内存的努力都需要时间——那些本能更好的被用来执行你的代码的时间。

Mass Storage Space 大存储空间

任何计算机上的文件系统性能都很重要,因为几乎所有东西都驻留在某个文件中。您的应用程序,数据甚至操作系统本身都存放在必须从设备加载到内存中的文件中,该设备与系统的其他部分相比速度非常慢。文件系统无论是本地的还是基于网络的,都是性能最大的瓶颈之一。这导致了另一个目标:

目标:消除不必要的文件操作并延迟其他操作,直到实际需要信息的时候。

通过消除或延迟文件操作来消除此瓶颈对于提高应用程序的整体性能非常重要。在您从文件请求数据到您的程序实际看到该数据的时间之间,可能会传递数以百万计的CPU周期。如果您的程序访问大量文件,它可能会等待很多秒才能收到所有请求的数据。

需要记住的另一件重要的事情是,您的应用程序及其创建的任何文件可能位于网络上,而不是位于本地硬盘上。特别是,OS X使得网络尽可能不可见,所以你不应该对文件的所在位置做出假设。

The Perception of Speed(对速度的感觉)
即使您调整您的应用程序代码以获得最佳性能,您的应用程序完全有可能对用户显得很慢。这个问题是不可避免的:如果你有很多工作要做,你需要CPU时间和资源来完成这项工作。那么有些东西可以为应用程序提供一种看起来快的感觉,用来实现以下目标:

目标:让您的程序对用户作出响应。

性能指标 Tracking Performance

确保高性能的唯一方法是在产品设计中包含性能目标,并在整个开发过程中针对这些目标测量产品。 高性能不是在开发周期结束时可以植入代码的功能; 它与该周期密切相关。 编写代码时,了解其对程序整体性能的影响非常重要。 如果您尽早发现性能问题,那么您有很好的机会在太迟之前解决它们。

确定您是否达到或超过特定目标的方法是收集度量标准。 Apple提供了几种工具来监视和分析程序的性能。 您还可以将测量工具直接构建到您的代码中,以帮助自动化收集数据的过程。 无论您选择哪种方法,都需要定期使用这些工具并分析结果

建立基准指标 Establish Your Baseline Metrics

您需要做的第一件事就是决定一组您要衡量的基准指标。选择您认为对用户来说最重要的任务,并确定执行这些任务的一组约束。例如,您可能希望应用程序在不到1秒的时间内加载并显示初始窗口,或者您可能希望将总内存使用量保持在给定的目标范围内。

一旦你有了你想跟踪的任务列表,你需要确定每个任务的性能目标。对于现有产品,您可能只是试图改进以前版本的性能。您也可以尝试衡量竞争产品的性能,并设定达到或超过其性能的目标。如果您有新产品,您可能需要尝试使用数字来找到合理的值。或者,您可能希望建立积极的基准值并尝试尽可能接近它们。

与任何性能测量一样,一致性非常重要。您建立基准指标的流程应该包含您收集这些指标的系统信息。详细记录系统的硬件和软件配置,并始终针对相同配置运行测试。尝试使用尽可能慢的硬件配置来建立基线。在快速机器上进行测量可能会导致您相信您的软件运行良好,但许多用户将使用运行速度较慢的处理器和较少内存的计算机。

Measure Early, Measure Often

性能数据不是您可以收集一次,并希望找到程序中的所有性能瓶颈。如果您保持程序性能的历史记录,则更容易发现问题。通过维护历史记录,可以轻松查看应用程序的性能是提高还是下降。如果数据下降,您可以在产品发布前采取措施纠正问题。
定期测量性能的另一个原因是您可以将这些结果与代码签入关联起来。如果特定里程碑的绩效下降,您可以查看在此期间签入的代码并尝试找出原因。同样,如果性能提高,您可以使用最近的代码签入作为优秀编程实践的模型,并鼓励您的团队使用类似的技术。
只要有部分功能的程序,您就应该开始进行性能测量。随着新功能的添加,您可以为这些功能添加测量。将一组自动诊断程序直接结合到您的程序中可以让您的团队成员立即看到结果。掌握这些信息可以让他们在检查代码之前更容易解决性能问题。

基础的性能优化小贴士

为你程序的关键任务编写代码
在设计程序时,请考虑用户将遇到的任务或工作流程。在您的实施阶段,请务必监视代码以执行这些任务,并确保其性能不会降至可接受的水平以下。如果是这样,你应该立即采取行动纠正这些问题。
程序执行的关键任务因程序而异。例如,在文本输入和显示期间文字处理器可能需要快速,而文件实用程序需要快速扫描硬盘上的文件和目录。由您决定您的用户最有可能执行哪些任务。

关于绘制的代码

大多数程序都会进行一些绘图。如果您的程序仅使用标准窗口和控件,那么您可能不需要过多担心绘图性能。但是,如果您执行任何自定义绘图,则需要监视绘图代码并确保其在可接受的水平上执行。尤其是,如果您支持以下任何一项,则应该研究如何优化绘图代码。

• Live resizing 适时的大小调整
• Custom view drawing code, especially if portions of the view can be updated without updating the whole view 定制的视图绘制代码,尤其是如果更新的是部分视图而不是全部视图
• 纹理图形
• Entirely opaque views 全透明的视图

关于启动时间初始化的代码

启动时间是初始化程序数据结构并准备应用程序以接收用户输入的位置。 但是,许多计划在发布时间内的工作量比必要的要多得多。 在很多情况下,启动时执行的任务可以推迟到应用程序发布其用户界面并开始处理事件之后。 这种延期让用户觉得你的应用程序很快,这是一个很好的第一印象。

对于需要在OS X 10.3.3及更早版本中运行的应用程序,提高启动时间的另一种方法是预先绑定应用程序。 预先绑定包括预先计算库地址范围并将这些值存储在应用程序二进制文件中。 这一步消除了动态加载器(dyld)在启动时计算这些地址范围的需要。 对于OS X 10.3.4版的dyld的改进使得在该版本和更高版本中不需要预先绑定。 同样,iOS中不需要预先绑定。但是有人已经实现了预绑定。比如uber的swift版,见:Swift with a hundred engineers

文件访问代码File Access Code

文件系统是将信息存入内存和CPU的瓶颈。在访问文件的时候,可能会执行数千万条指令。因此,您必须检查程序使用文件的方式并确保这些文件实际上是正确需要和正确使用的。

最大限度地减少您使用的文件数量是提高文件相关性能的一种方法。 当您访问文件时,请牢记以下几点:

• 了解系统如何缓存文件数据并知道如何优化这些缓存的使用。 避免自己缓存数据,除非您打算多次使用它。
• 尽可能地按顺序读取和写入数据。 跳过文件需要花费额外的时间来寻找新的位置。
• 尽可能从文件中读取更大的数据块,请记住,一次读取太多数据可能会导致不同的问题。 例如,读取32 MB文件的全部内容可能会在操作完成之前触发对这些内容的分页。
• 避免不必要地关闭和重新打开文件。 如果启用了缓存,则即使数据未更改,也可能会刷新缓存。

Application Footprint 应用程序大小占用

代码的大小可能会对系统性能产生巨大影响。您的程序使用的内存页越多,系统和其他程序可用的内存就越少。这种内存压力最终会导致分页和整个系统的减速。

管理你的程序占用就是组织你的代码和数据结构。您需要确保内存中有正确的部分,并且不会导致任何内存页面被不必要地读取或写入。导致大量内存占用的一些问题如下所示:

• 代码页包含未使用的代码。编译器通常通过编译模块来组织代码,这并不总是最好的技术。根据代码一起执行来组织模块可能是更好的选择。
• 静态或常量数据存储在可写页面上。在分页过程中,这些数据不必要地写入磁盘。尽你所能将这些数据移动到可以快速清除的不可写入页面。
• 程序出口的符号比实际需要的要多。符号占用空间,只有通过必须调用到程序中的外部代码模块才需要。删除不应该在外部使用的符号。
• 代码没有被编译器和链接器更好的优化。一定要尝试编译器的优化设置,并查看哪一个最适合您的程序。
• 程序包含了太多的框架。只加载程序实际使用的框架。

内存分配的代码Memory Allocation Code

程序为存储永久和临时数据结构分配内存。无论是在CPU时间还是在内存消耗中,每个内存分配都会产生与其相关的成本。了解您的程序何时分配内存以及如何使用该内存可以帮助您降低这两项成本。

了解程序的内存使用情况可以帮助确定减少使用量的方法。您可以使用可用的性能工具来确定自动发布的Objective-C对象是否在导致分页过多之前被释放。您还可以使用这些工具来查找代码中的错误引起的内存泄漏。看着你调用malloc的次数也可能指出你可以重用现有内存块而不是创建新内存块的地方。

分配内存时遵循的一条重要规则是懒惰。推迟内存分配,直到您确实需要使用的内存。对于一些其他的方式,你可以使用lazy的方式进行内存分配。

基本的优化小贴士

懒一点 Be Lazy
确保您的应用程序不会执行任何不必要的工作,这是提高性能的一种非常简单的方法。比如不去加载不必要的nib文件等。遵循的基本规则是等待用户从您的应用程序请求某些内容,然后使用必要的资源来完成请求。

善于利用可感知的性能Take Advantage of Perceived Performance
使用线程调度,idle time等方式。这样做可以让应用程序的主线程自由处理用户交互,并使程序界面对用户更加敏感。 另一种可感知的性能优化是加快程序的启动速度,

使用基于事件的处理
所有现今的mac程序都应该使用cocoa event系统或者古老的Carbon event manager。比如,iIphone app 必须使用 由UIkit提供的,基于点击的事件系统。事件系统的好处是:
• 让程序更好的响应用户
• 降低了CPU使用
• 降低了程序的工作集合——给定时间内的一大堆在内存中的代码页
• 让系统积极的管理系统电力能源

所以,不应该跳过系统的事件系统。OSX和iOS的线程使用run loop 来按需提供对timer,network enent或者其他要处理的数据。其他frameworks 也同样适用异步编程模型来处理任务,当任务完成后通知事件处理函数或者方法等。dispatch sources也用于异步在queue中处理此类工作。

提高程序任务的并发性

因为现在计算机都是多核心,并发是另一个提升程序性能的方法。

其他方法:使用专门用于加速的framwork,使应用程序更现代

end

留下评论

Your email address will not be published. Required fields are marked *