学计算机的那个

不是我觉到、悟到,你给不了我,给了也拿不住;只有我觉到、悟到,才有可能做到,能做到的才是我的.

0%

【译】管理代码大小

管理代码大小

GCC编译器支持多种优化代码的选项。根据您的需要,大多数这些技术都会生成更少的代码或更快的代码。在准备软件发布时,您应该尝试使用这些技术,看看哪些技术对您的代码最有利。

编译器层优化

GCC编译器支持优化选项,可以让您选择是更小的二进制大小、更快的代码还是更快的构建时间。

对于新项目,Xcode会自动禁用开发构建风格(the development build style)的优化,并为部署构建风格(the deployment build style)选择“最快,最小”选项。任何类型的代码优化都会导致构建时间变慢,因为优化过程中涉及到额外的工作。如果您的代码在开发周期中发生了变化,那么您不希望启用优化。但是,当您接近开发周期的末尾时,部署构建样式可以指示您完成的产品的大小。

表1列出了Xcode中可用的优化级别。当你选择其中一个选项时,Xcode会为给定的组或文件将适当的标志传递给GCC编译器。这些选项可以在目标级别使用,也可以作为构建样式的一部分使用。请参阅Xcode帮助以获得有关如何为项目构建设置的信息。

1
2
3
4
5
None:编译器不会尝试优化代码。当您专注于解决逻辑错误并需要快速编译时,请在开发过程中使用此选项。不要在发布可执行文件时使用此选项。
Fast:编译器执行简单的优化来提高代码性能,同时最大限度地减少对编译时间的影响。该选项还会在编译期间使用更多内存。
Faster:执行几乎所有支持的不需要考虑时间空间权衡的优化。编译器不使用此选项执行循环展开或函数内联。这个选项增加了编译时间和提高了生成代码的性能
Fastest:执行所有优化,以提高生成代码的速度。当编译器执行积极的函数内联时,此选项会增加生成的代码的大小。 通常不建议使用此选项。有关更多信息,请参见避免过多的函数内联。
Fastest,smallest: 执行通常不会增加代码大小的所有优化。这是交付代码的首选选项,因为它为您的可执行文件提供了更小的内存占用。

与任何性能增强一样,不要假设哪个选项会给您带来最好的结果。您应该始终衡量所尝试的每次优化的结果。例如,“Fastest”选项可能为特定模块生成非常快的代码,但它通常以可执行文件的大小为代价。如果运行时需要从磁盘调入代码,那么从代码生成中获得的任何速度优势都很容易丧失。

额外的优化

除了代码级优化之外,还有一些额外的技术可以用于在模块级(the module level)组织代码。下面几节将介绍这些技术。

Dead Strip Your Code

对于静态链接(statically-linked)的可执行文件,死代码剥离(dead-code stripping)是从可执行文件中删除未引用代码的过程。死剥背后的思想是,如果代码没有被引用,它就不能被使用,因此在可执行文件中也不需要它。删除死代码可以减少可执行文件的大小,并有助于减少分页(paging)。

从Xcode Tools 1.5版开始,静态链接器(ld)支持可执行文件的死剥。你可以直接从Xcode中启用这个特性,也可以将适当的命令行选项传递给静态链接器。

要在Xcode中启用死代码剥离,请执行以下操作:

  1. 选择你的target。
  2. 打开“检查器”或“获取信息”窗口并选择“构建”选项卡。
  3. 在链接设置中,启用死代码剥离(Dead Code Stripping)选项。
  4. 在“代码生成设置”(Code Generation settings)中,将“调试符号级别”选项设置为“所有符号”。

要从命令行启用死代码剥离,将-dead_strip选项传递给ld。您还应该将-gfull选项传递给GCC,为您的代码生成一组完整的调试符号。链接器使用这些额外的调试信息来死剥可执行文件。

注意:建议使用“All Symbols”或-gfull选项,即使您不打算对代码进行死剥。尽管该选项生成较大的中间文件,但它通常会生成较小的可执行文件,因为链接器能够更有效地删除重复的符号信息。

如果你不想删除任何未使用的函数,你至少应该将它们隔离在__TEXT段的单独section(a separate section)中。将未使用的函数移动到公共部分可以改善代码引用的局部性,并降低它们被加载到内存中的可能性。有关如何在公共部分(a common section)中对函数进行分组的更多信息,请参见改进引用的局部性

Strip Symbol Information

调试符号和动态绑定信息会占用大量空间,并占可执行文件的很大比例。在发布代码之前,应该去掉所有不需要的符号。

要从可执行文件中剥离调试符号,请将Xcode构建风格设置更改为“部署”并重新构建可执行文件。如果您愿意,还可以在逐个目标的基础上生成调试符号。有关构建样式和目标设置的更多信息,请参阅Xcode Help。

若要手动从可执行文件中删除动态绑定符号,请使用strip工具。此工具删除通常由动态链接器用于在运行时绑定外部符号的符号信息。删除不希望动态绑定的函数的符号可以减少可执行文件的大小,并减少动态连接器必须绑定的符号数量。通常,你会使用这个命令不带任何选项来删除非外部符号,如下例所示:

1
2
% cd ~/MyApp/MyApp.app/Contents/MacOS
% strip MyApp

该命令相当于带-u-r选项运行strip命令。它删除任何标记为non-external的符号,但不删除标记为external的符号。

手动去除动态绑定符号的另一种方法是使用导出文件来限制在构建时导出的符号。导出文件标识可执行文件中运行时可用的特定符号。有关创建导出文件的详细信息,请参见最小化导出的符号

消除c++异常处理开销

当抛出异常时,c++运行时库必须能够将堆栈展开到第一个匹配catch块的位置。为此,GCC编译器为每个可能引发异常的函数生成堆栈展开信息。此展开信息存储在可执行文件中,并描述堆栈上的对象。这些信息使得在抛出异常时调用这些对象的析构函数来清除它们成为可能。

即使您的代码没有抛出异常,GCC编译器仍然会在默认情况下为c++代码生成堆栈展开信息。如果大量使用异常,这些额外的代码会显著增加可执行文件的大小。

  • 禁用异常
    你可以通过禁用target的“Enable c++ Exceptions”构建选项来禁用Xcode中的异常处理。在命令行中,将-fno-exceptions选项传递给编译器。此选项删除函数的堆栈展开信息。但是,您仍然必须从代码中删除任何try、catchthrow语句。

  • 选择性禁用异常
    如果您的代码在某些地方使用了异常,但不是在所有地方都使用了异常,您可以通过在方法声明中添加空异常规范来显式地识别不需要展开信息的方法。例如,在下面的代码中,编译器必须为my_function生成堆栈展开信息,因为my_other_function或它调用的函数可能抛出异常。

1
2
3
4
5
extern int my_other_function (int a, int b);
int my_function (int a, int b)
{
return my_other_function (a, b);
}

然而,如果你知道my_other_function不能抛出异常,你可以通过在函数声明中包含空的异常规范(throw())向编译器发出信号。因此,你可以这样声明前面的函数:

1
2
3
4
5
extern int foo (int a, int b) throw ();
int my_function (int a, int b) throw ()
{
return foo (a, b);
}
  • 尽量减少异常使用
    在编写代码时,请谨慎考虑异常的使用。异常应该用来表示特殊情况——也就是说,它们应该用来报告您没有预料到的问题。如果从文件中读取并得到文件结束错误,则不希望抛出异常,因为这是一种已知的错误类型,可以轻松处理。如果您试图从您知道已打开的文件中读取,但被告知文件ID无效,那么您可能会抛出异常。

避免过多的函数内联

尽管内联函数在某些情况下可以提高速度,但如果过度使用,它们也会降低OS X上的性能。内联函数消除了调用函数的开销,但这样做的方法是将每个函数调用替换为代码的副本。如果频繁调用内联函数,这些额外的代码会迅速增加,使可执行文件膨胀,并导致分页问题

如果使用得当,内联函数可以节省时间,并且对代码占用空间的影响最小。记住,内联函数的代码通常应该非常短,并且不经常调用。如果在函数中执行代码所花费的时间小于调用函数所花费的时间,则该函数很适合进行内联。通常,这意味着内联函数的代码应该不超过几行。您还应该确保从代码中尽可能少的地方调用函数。即使是一个很短的函数,如果它内联在几十个或数百个地方,也会导致过度膨胀。

此外,您应该意识到GCC的“Fastest”优化级别通常应该避免。在此优化级别,编译器会积极尝试创建内联函数,即使对于未标记为内联的函数也是如此。不幸的是,这样做会显著增加可执行文件的大小,并由于分页而导致更糟糕的性能问题

将框架构建为单个模块

大多数共享库不需要Mach-O运行时的模块特性。此外,跨模块调用所引起的开销与跨库调用相同。因此,您应该将项目的所有中间对象文件链接到一个单独的模块中。

要组合目标文件,必须在链接阶段将-r选项传递给ld。如果你使用Xcode来构建你的代码,默认情况下这是为你完成的。