Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Improve Carthage dependency lookup performance #298

Merged
merged 3 commits into from
Apr 24, 2018

Conversation

keith
Copy link
Collaborator

@keith keith commented Apr 19, 2018

Previously for large projects, project generation time could be up to 40 seconds in our testing because of lookup up Carthage dependencies (regardless of if you have any). This change improves the performance of this with 2 changes.

  1. Store a dictionary of target name -> target. This allows much faster lookups than the previous O(n) array lookup (this should also improve performance of other callers of getTarget
  2. Remove the recursively lookups of carthage dependencies. Previously this traversed your entire dependency tree for each target. Now we iteratively discover the dependencies, while still only visiting them once.

@keith keith requested review from yonaskolb and kastiglione April 19, 2018 21:49
@keith
Copy link
Collaborator Author

keith commented Apr 19, 2018

This produces the same project for us and passes the tests, but since we don't have any carthage dependencies it would be useful if someone else could verify it still produces a valid project for them.

Copy link
Contributor

@soffes soffes left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I tested with my open source Canvas project. I ran the following commands to verify the project is identical before and after.

$ git clone https://github.com/soffes/Canvas && cd Canvas
$ git clone https://github.com/yonaskolb/XcodeGen
$ cd XcodeGen && swift build && cd ..
$ ./XcodeGen/.build/debug/xcodegen
$ cp Canvas.xcodeproj/project.pbxproj project.pbxproj.orig
$ rm -rf XcodeGen/.build
$ cd XcodeGen && git checkout ks/carthage-performance && swift build  && cd ..
$ ./XcodeGen/.build/debug/xcodegen
$ cmp Canvas.xcodeproj/project.pbxproj project.pbxproj.orig && echo "Same!"

After running carthage bootstrap it builds fine before and after.

for dependency in target.dependencies {
switch dependency.type {
case .carthage:
frameworks.append(dependency)
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Can this result in frameworks having duplicate dependencies in this list, and if so should this function return a unique list of dependencies?

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The carthage dependencies are de-duplicated later on when they are added to a target. This could be done here, but maybe it might be useful at some point to know how many times a dependency is being used

@@ -18,6 +18,7 @@ public struct Project {
public var fileGroups: [String]
public var configFiles: [String: String]
public var include: [String] = []
private var targetsMap: [String: Target]
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

This should be recreated on the targets didSet as well

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

done, looks like this is only used for tests, seems like we should remove that mutability.

Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The ProjectSpec module can also be used as a framework in other projects.

Personally I've never seen the point of making properties on a Struct immutable, as the property referencing that struct can just be a let to control the immutability from a single place, while still allowing easy mutability for other cases without reaching into a full blown initializer

for dependency in target.dependencies {
switch dependency.type {
case .carthage:
frameworks.append(dependency)
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The carthage dependencies are de-duplicated later on when they are added to a target. This could be done here, but maybe it might be useful at some point to know how many times a dependency is being used

if let target = project.getTarget(targetName) {
frameworks += getAllCarthageDependencies(target: target, visitedTargets: visitedTargets)
var queue: [Target] = [target]
while !queue.isEmpty {
Copy link
Owner

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Is this traversing the same graph, but just doing it in one function instead of recursively?

I think to get this really performant the carthage dependencies for a target could be cached by just running this once for all targets at the beginning, instead of each target going through the whole graph again

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It is traversing the same dependencies yes. It should have the exact same result.

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We could do that, I figured that would be a larger enough change that if we could do this fast enough it's not really worth it.

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@yonaskolb The recursive version is passing visitedTargets by value, which I suspect is why the the iterative version is much faster.

@yonaskolb yonaskolb merged commit 364f33e into master Apr 24, 2018
@yonaskolb yonaskolb deleted the ks/carthage-performance branch April 24, 2018 13:07
@keith
Copy link
Collaborator Author

keith commented Apr 24, 2018

Thanks!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

Successfully merging this pull request may close these issues.

4 participants