< Previous PageNext Page > Hide TOC

Reducing Build Times

Xcode offers a number of features you can take advantage of to decrease build time for your project. For example, you can use distributed builds to shorten the time it takes to build your whole project or multiple projects. ZeroLink and predictive compilation, on the other hand, improve turnaround time for single file changes, thereby speeding up the edit–build–debug cycle. All of these features reduce the amount of time you spend idle while waiting for your project to build.

This chapter describes the following features:

Fix and Continue, described in Modifying Running Code, improves your debugging efficiency by allowing you to make changes to your application and see the results of your modifications without stopping your debugging session.

In this section:

Using a Precompiled Prefix Header
Predictive Compilation
Using Multiple SDKs
Distributing Builds Among Multiple Computers


Using a Precompiled Prefix Header

A precompiled header is a file in the intermediate form used by the compiler to compile a source file. Using precompiled headers, you can significantly reduce the amount of time spent building your product. Often, many of the source code files in a target include a subset of common system and project headers. For example, each source file in a Cocoa application typically includes the Cocoa.h system header, which in turn includes a number of other headers. When you build a target, the compiler spends a great deal of time repeatedly processing the same headers.

You can significantly reduce build time by providing a prefix header that includes the set of common headers used by all or most of the source code files in your target and by having Xcode precompile that prefix header.

If you have indicated that Xcode should do so, Xcode precompiles the prefix header when you build the target. Xcode then includes that precompiled header file for each of the target's source files. The contents of the prefix header are compiled only once, resulting in faster compilation of each source file. Furthermore, subsequent builds of the target can use that same precompiled header, provided that nothing in the prefix header or any of the files on which it depends has changed. Each target can have only one prefix header.

When Xcode compiles your prefix header, it generates a variant for each C language dialect used by your source files; it stores these in the folder specified by the SHARED_PRECOMPS_DIR (Precompiled Headers Cache Path) build setting. It also generates—as needed—a variant for each combination of source header and compiler flags. For example, you may have per-file compiler flags set for some of the files in the target you are building. Xcode will create a variant of the precompiled header by precompiling the prefix header with the set of compiler flags derived from the target and the individual source file. As Xcode invokes the compiler to process each source file in your target, the compiler searches the precompiled header directory for a precompiled header variant matching the language and compiler flags for the current compile. Xcode uses the first precompiled header variant that is valid for the compilation.

Precompiled headers can be reused across targets and projects. Targets that share the same prefix header and common compiler settings will automatically share the same precompiled header.

Xcode automatically regenerates the precompiled header whenever the prefix header, or any of the files it depends on are changed, so you don't need to manually maintain the precompiled header.

Creating the Prefix Header

To take advantage of precompiled headers in Xcode, you must first create a prefix header. Create a header file containing any common #include and #define statements used by the files in your target.

Note:  You can use a prefix header to include a common set of header files for each source file in your target without precompiling the prefix header.

Do not include in the prefix header anything that changes frequently. Xcode recompiles your precompiled header file when the prefix header, or any of the headers it includes, change. Each time the precompiled header changes, all of the files in the target must be recompiled. This can be an expensive operation for large projects.

Because the compiler includes the prefix header file before compiling each source file in the target, the contents of the prefix header must be compatible with each of the C language dialects used in the target. For example, if your target uses Cocoa and contains both Objective-C and C source files, the prefix header needs to include the appropriate guard macros to make it compatible with both language dialects, similar to the example shown here:

    #ifdef __OBJC__
    #import <Cocoa/Cocoa.h>
    #endif
    #define MY_CUSTOM_MACRO 1
    #include "MyCommonHeaderContainingPlainC.h"

Configuring Your Target to Use the Precompiled Header

Once you have created the prefix header, you need to set up your target to precompile that header. To do this, you must provide values for the two build settings described here. You can edit these build settings in the Build pane of the target inspector or Info window. The settings you need to change are:

You must provide values for these settings in each target that uses a precompiled prefix header, even if those targets use the same prefix header.

By default, Xcode precompiles a version of the header for each C-like language used by the target (C, C++, Objective-C, or Objective-C++). The GCC_PFE_FILE_C_DIALECTS (C Dialects to Precompile) build setting lets you explicitly specify the C dialects for which Xcode should produce versions of the precompiled header.

Sharing Precompiled Header Binaries

It is possible to share a precompiled header binary across multiple targets in multiple projects, provided that those targets use the same prefix header and compiler options. In order to share a precompiled header binary, each individual target must have the same prefix header specified. To use the same prefix header for multiple targets, for each target set the value of the Prefix Header build setting to the path to the header.

Each target must also specify a common directory as the destination for the precompiled header generated by Xcode. The SHARED_PRECOMPS_DIR (Precompiled Headers Cache Path) build setting specifies the location of the directory to which Xcode writes the precompiled header binary. By default, this is defined as <Xcode_Persistent_Cache>/com.apple.Xcode.$(UID)/SharedPrecompiledHeaders for all newly created or upgraded projects. When Xcode invokes GCC to compile each source file in a target, GCC searches this common directory for the appropriate header binary. For any targets that use the same prefix header and compiler options, GCC uses the same precompiled header binary when it builds those targets.

Xcode also defines the build setting PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR (Precompiled Header Uses Files From Build Directory). This build setting is a hint to the build system; it indicates whether a target's prefix header includes headers files from the target's build directory. If your prefix header includes header files from the build directory, set this build setting to YES by selecting the checkbox next to the build setting in the Build pane. If your targets share a common build directory outside of the project directory, Xcode still shares precompiled headers across compatible targets. If, however, you use the default build directory location within the project directory, Xcode shares precompiled headers across compatible targets only within the same project.

If your target's prefix header does not include headers from its build directory, set the PRECOMPS_INCLUDE_HEADERS_FROM_BUILT_PRODUCTS_DIR build setting to NO, to increase the chances that Xcode will be able to use a shared precompiled header when building the target.

If you specify the same prefix header for multiple targets but do not specify a common location for the precompiled binary, Xcode precompiles the prefix header once for each target as it is built.

Regenerating Precompiled Headers

Xcode compiles your prefix header whenever it fails to find a matching precompiled header binary in the shared precompiled header location. As described earlier in this chapter, Xcode automatically rebuilds your precompiled prefix header if you make changes to the prefix file or to any files it includes. Xcode also recompiles your prefix header if you make changes to your target's build settings that affect the compiler options used to build the target's files.

You can also force Xcode to rebuild your precompiled header by:

By default, cleaning a target removes the precompiled header for that target—whether or not Xcode initially generated the precompiled header binary as part of building that target. Because rebuilding precompiled headers can be time consuming, you may want to clean an individual target without discarding the precompiled header binaries for that target if you are sharing precompiled headers across many targets in multiple projects. When you initiate a clean, Xcode displays a dialog that lets you choose whether to preserve precompiled headers. To keep precompiled headers, deselect the Also Remove Precompiled Headers option. By default, this option is enabled.

Controlling the Cache Size Used for Precompiled Headers

Xcode caches the precompiled header files that it generates. To control the size of the cache devoted to storing those files, use the BuildSystemCacheSizeInMegabytes user default. In the Terminal application, type:

defaults write com.apple.xcode BuildSystemCacheSizeInMegabytes defaultCacheSize

Specifying 0 for the cache size gives you an unlimited cache. The default cache size set by Xcode is 1024 MB. If the cache increases beyond the default size, Xcode removes as many precompiled headers as is necessary to reduce the cache to its default size when Xcode is next launched. Xcode removes the oldest files first.

Restrictions

To take advantage of Xcode’s automatic support for precompiled headers you must:

Predictive Compilation

Predictive compilation is a feature introduced to reduce the time required to compile single file changes and speed up the edit–compile–debug cycle of software development. If you have predictive compilation enabled for your project, Xcode begins compiling the files required to build the current target even before you tell Xcode to build.

Predictive compilation uses the information that Xcode maintains about the build state of targets that use the native build system. Xcode keeps the graph of all files involved in the build and their dependencies, as well as a list of files that require updating. At any point in time, Xcode knows which of the files used in building a target’s product are out of date and what actions are required to bring those files up to date. A file can be updated when all of the other files on which it depends are up to date. As files become available for processing, Xcode begins to update them in the background, even as you edit your project.

Xcode will even begin compiling a source code file while you are editing it. Xcode begins reading in and parsing headers, making progress compiling the file even before you initiate a build. When you do choose to save and build the file, much of the work has already been done.

Until you explicitly initiate a build, Xcode does not commit any of the output files to their standard location in the file system. When you indicate that you are done editing, by invoking one of the build commands, Xcode decides whether to keep or discard the output files that it has generated in the background. If none of the changes made subsequent to its generation affect the content of a file, Xcode commits the file to its intended location in the file system. Otherwise, Xcode discards its results and regenerates the output file.

You can turn on predictive compilation by selecting “Use Predictive Compilation” option in the Building pane of the Xcode Preferences window.

Predictive compilation works only with GCC 4.0 or later and native targets. All predictive compilation is done locally on your computer, regardless of whether you have distributed builds enabled. On slower machines, enabling predictive compilation may interfere with Xcode performance during editing.

Using Multiple SDKs

When developing Mac OS X applications, you normally use a system SDK, such as the Mac OS X v10.4 SDK or the Mac OS X v10.5 SDK. In this document, an SDK (software development kit) is a set of frameworks or libraries that provide particular functionality to your product. A system SDK is one included in Xcode to provide an interface to core Mac OS X functionality. System SDKs reside in <Xcode>/SDKs.

There may be times when, in addition to a system SDK, you need link against sparse SDKs. A sparse SDK is an SDK that is not a system SDK. Sparse SDKs may be provided by third parties, or you may build them yourself.

To specify additional SDKs to link against, use the ADDITIONAL_SDKS (Additional SDKs) build setting. You can specify the paths to sparse SDKs in this build setting.

At build time, the build system groups the target’s additional SDKs into a single SDK, known as a composite SDK. Composite SDKs are cached in the <Xcode_Persistent_Cache> directory (see "Overview of Xcode Projects" for details on the Xcode persistent-cache location). Therefore, targets (from any of your projects) that list the same set of additional SDKs, share the composite SDK the build system generates, which improves compilation times.

Distributing Builds Among Multiple Computers

Building a product involves several tasks. Some tasks, such as compiling precompiled headers and linking a binary, can be performed effectively only by the computer that hosts a build. However, the main task performed in a build—compiling source files—can be carried out in parallel by several computers. Builds that use several computers to compile source files are known as distributed builds. When you use distributed builds, Xcode distributes as many compilation tasks among the computers available for this purpose within a network.

Distributed builds use two types of computers: build clients and build servers.

There are two types of distributed builds available in Xcode: shared workgroup builds and dedicated network builds.

To specify whether builds started by a particular user on a computer are to be distributed to other computers (that is, whether the computer acts as a build client for that user), use the “Distribute via” option and pop-up menu in the Distributed Builds preference pane in Xcode Preferences while logged in as that user (see “Distributed Builds Preferences Pane” for details). You may also specify build servers in xcodebuild invocations.

The following sections describe the two types of distributed builds in more detail and provide guidelines for deciding which type of distributed build to use for a particular project.

Shared Workgroup Builds

Shared workgroup builds work best when building small to medium projects using up to ten build servers. Their main advantage is easy set up (including Bonjour support). Their main disadvantage is the requirements it imposes on the computers to be used as build servers by a build client.

How Shared Workgroup Builds Work

To perform a shared workgroup build, build clients distribute compilation tasks to the build servers specified in the host list in Xcode Preferences > Distributed Builds or in the build-server list given to xcodebuild. The build client performs the build set-up, such as compiling precompiled headers, and linking. (Precompiled headers are compiled by the build client to ensure that all build servers use the same precompiled header in a build.)

To distribute the compilation task of a build, Xcode and xcodebuild invoke the distcc tool instead of gcc. The distcc tool is the client-side process that distributes compilation tasks among build servers.

Build servers run the distccd daemon, which responds to compilation requests sent by distcc in the build client. When a build server receives a compilation request, it obtains the preprocessed implementaton files from the build client, invokes gcc to compile the files into an object file, and sends the object file to the build client. Figure 6-1 illustrates this process.


Figure 6-1  Shared workgroup build process

Shared workgroup build process

This list explains the process shown in Figure 6-1:

  1. Xcode (or xcodebuild) invokes distcc in the build client to compile a source file.

  2. Process distcc preprocesses the source file using the compiler specified by the target, finds a compatible build server, and sends the preprocessed source file to the distccd process running on that computer. See “Requirements for Using Shared Workgroup Builds” for information on how compatible build servers are identified.

  3. The distccd process in the build server invokes gcc with the file provided by distcc as input and returns the output gcc generates.

  4. The distcc process in the build client places the output from distccd in the project’s build directory.

  5. The distcc process tells Xcode (or xcodebuild whether the compilation task was successful.

If a build client’s compilation request fails—for example, if communication with the build server is lost or the build server cannot execute the compilation request—Xcode (or xcodebuild) performs the compilation on the build client.

Build servers respond to compilation requests as they are able. That is, a build server that is processing a compilation request, may not respond to another compilation request from a build client until it’s done. Xcode and xcodebuild distribute compilation operations to the most capable build servers first. But, when there are enough compilation operations to distribute among the available build servers, Xcode uses all of them, regardless of their processing power. For example, when a build client has three build servers available, two fast ones and one slow one, and a build requires three compilation tasks, all the servers receive a compilation request, even it the two fast servers would complete the tasks faster by themselves than when the slower server is used.

Build servers cache precompiled headers to speed-up subsequent build requests that require the same precompiled header. Before compiling a preprocessed file, the build server determines whether it needs to get a new copy of the precompiled header from the build client. Therefore, modifying precompiled headers (as for in non–distributed builds) adversely affects the performance of shared workgroup builds.

Requirements for Using Shared Workgroup Builds

Use of distributed builds is subject to the following constraints:

Sharing a Computer as a Shared Workgroup Build Server

To advertise a computer as a shared workgroup build server, select the appropriate option in Xcode Preferences > Distributed Builds. As an alternative, you may execute the following command:

sudo /bin/launchctl load -w /System/Library/LaunchDaemons/distccd.plist

Note: When you make a computer a build server, build clients may use it at any time, whether Xcode is running and whether you are logged in to the computer. See “Distributed Builds Preferences Pane” for more information.

You should not share your computer if you are using it to host distributed builds. That is, you should not have both “Share my computer for building” and “Distribute builds” selected in the Distributed Builds preference pane. Because precompilation and the distribution of build tasks are done on your local computer, allowing others to distribute build tasks to your computer can significantly slow down your own builds.

Shared Workgroup Builds and Firewalls

To use shared workgroup builds across a firewall, the firewall must allow traffic on port 3632. Some firewalls allow Bonjour traffic, making build servers behind the firewall visible in the Bonjour distributed-build set. However, the distributed build will not work. When Xcode attempts to distribute compilation tasks and receives no response from the build servers behind the firewall, it times out and performs the compilation tasks on the build client.

To allow traffic on port 3632 on build clients with a firewall active, use the Firewall pane in Sharing Preferences, as shown in Figure 6-2.


Figure 6-2  Allowing distributed builds traffic through the Mac OS X firewall

Allowing distributed builds traffic through the Mac OS X firewall

Dedicated Network Builds

Dedicated network builds are appropriate for large projects and and with more than 10 build servers. Dedicated network builds offer better performance than shared workgroup builds when building large projects. However, they don’t offer autodiscovery of build servers.

How Dedicated Network Builds Work

To perform a dedicated network build, build clients distribute compilation tasks to the build servers specified in the host list in the Xcode Preferences > Distributed Builds or in the build-server list given to xcodebuild. The build client performs the build set-up, including compiling precompiled headers, and linking. (Precompiled headers are compiled by the build client to ensure that all build servers use the same precompiled header in a build.)

Because build servers must download several files from the build client before they can start compiling, the first two or three builds of the same project a build client carries out are slow. Subsequent builds become faster as the build servers cache the necessary files.

Requirements for Using Dedicated Network Builds

For a build client to successfully distribute compilation operations using dedicated network builds to a set of build servers, the following requirements must be met:

  1. The build client and build servers must run Mac OS X v10.4 or later.

  2. The build client and build servers must run the same version of Mac OS.

  3. The build servers must have Xcode 2.3 or later installed.

Sharing a Computer as a Dedicated Network Build Server

To advertise a computer as a dedicated network build server, select the appropriate option in Xcode Preferences > Distributed Builds. Alternatively, you may execute the following command:

sudo /bin/launchctl load -w /System/Library/LaunchDaemons/com.apple.dnbvolunteer.plist

Dedicated Network Builds and Firewalls

To use shared workgroup builds across a firewall, the firewall must allow traffic on port 51500.

Getting the Most Out of Distributed Builds

Using Xcode to distribute builds across multiple computers will reduce the time it takes to build your product. However, the degree of improvement in build times you get depends on many factors. These are a few of them:

Xcode must completely finish building one target before starting to build another, which means that all the source files for a given target must complete compiling before any files from dependent targets can start compiling. Therefore, to maximize the potential for parallel compilation, instead of many targets with few source files per target, you should have fewer targets with more source files per target. A target with a single file will effectively stall all the build servers.

For shared workgroup builds, Xcode provides the default preference XCMaxNumberOfDistributedTasks to control the maximum number of parallel compilation tasks allowed during a build. Use this preference only if you want to reduce the number of compilation tasks Xcode creates to complete a build. For more information on these preferences, and on how to set them, see Xcode User Default Reference.

Distributed Builds Preferences

The Distributed Builds pane in Xcode preferences contains options for distributing build tasks to other computers in your network. Figure 6-3 shows the Distributed Builds pane.


Figure 6-3  Distributed Builds preferences pane

Distributed Builds preferences pane

Here is what the pane contains:

For more information about distributed builds in Xcode, see Xcode Build System Guide.



< Previous PageNext Page > Hide TOC


© 2009 Apple Inc. All Rights Reserved. (Last updated: 2009-02-04)


Did this document help you?
Yes: Tell us what works for you.
It’s good, but: Report typos, inaccuracies, and so forth.
It wasn’t helpful: Tell us what would have helped.