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

Optimize bottlenecks #803

Merged
merged 4 commits into from
Apr 3, 2020
Merged

Conversation

michaeleisel
Copy link
Contributor

These are some bottlenecks that were causing significant performance overhead for me. The changes are rough, but if they look good overall I can clean them up

@@ -4,18 +4,36 @@ import PathKit
import Yams

extension Dictionary where Key: JSONKey {

public func json<T: NamedJSONDictionaryConvertible>(atKeyPath keyPath: JSONUtilities.KeyPath, invalidItemBehaviour: InvalidItemBehaviour<T> = .remove) throws -> [T] {
public func json<T: NamedJSONDictionaryConvertible>(atKeyPath keyPath: JSONUtilities.KeyPath, invalidItemBehaviour: InvalidItemBehaviour<T> = .remove, parallel: Bool = false) throws -> [T] {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

parallelizing the json parsing

Copy link
Contributor Author

Choose a reason for hiding this comment

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

fyi if we used zippyjson for the json parsing, even single-threaded it would be a lot (maybe 10x?) faster. but parallelizing is a simple win for this commit

@@ -10,11 +10,14 @@ import ProjectSpec
import PathKit

public class CarthageDependencyResolver {
Copy link
Contributor Author

Choose a reason for hiding this comment

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

parallelizing dependency resolution

Comment on lines 17 to 18
let jsonDictionary: JSONDictionary = try! dictionary.json(atKeyPath: .key(key))
let item = try! T(name: key, jsonDictionary: jsonDictionary)
Copy link

Choose a reason for hiding this comment

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

no need to use exclamation mark on try

Copy link
Contributor Author

Choose a reason for hiding this comment

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

agreed. any other concerns?

Copy link

Choose a reason for hiding this comment

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

nope. but bear in mind i am not a real reviewer 🙈

Copy link
Collaborator

@freddi-kit freddi-kit Mar 26, 2020

Choose a reason for hiding this comment

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

According to another paring function, it would be better to set try, not try!

public func json<T: NamedJSONConvertible>(atKeyPath keyPath: JSONUtilities.KeyPath, invalidItemBehaviour: InvalidItemBehaviour<T> = .remove) throws -> [T] {

This is also because paring error should be handled on here

public init(basePath: Path = "", jsonDictionary: JSONDictionary) throws {

Copy link
Contributor Author

Choose a reason for hiding this comment

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

looking at this further, i think it would be simpler to use try! instead of try. the issue is that these blocks are being run asynchronously, so they don't just bubble up to this function's caller. there are other strategies, like storing a result type in the list and then if any of the results are failures, throw the first failure as the error out of this function. we can do that if people want though

Copy link
Contributor Author

Choose a reason for hiding this comment

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

eh, i'll just do it with try actually

@brentleyjones brentleyjones requested a review from yonaskolb March 16, 2020 17:22
@michaeleisel
Copy link
Contributor Author

Is this project still maintained?

@yonaskolb
Copy link
Owner

Thanks for your work here @michaeleisel. Would you be able to post some timings before and after this PR (after a rebase) for a large project, and also for the performance tests. Thanks!

class CarthageVersionLoader {

private let buildPath: Path
private var cachedFiles: [String: CarthageVersionFile] = [:]
private var cachedFilesMutex: Mutex<[String: CarthageVersionFile]> = Mutex([:])
Copy link
Contributor Author

Choose a reason for hiding this comment

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

note that we'll need to be careful in the future not to add any non-thread-safe state to CarthageVersionLoader. for now it's just cachedFiles that needs this, but something to keep in mind

Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's add a comment to CarthageVersionLoader to warn future us.

@michaeleisel
Copy link
Contributor Author

For carthage dependencies, on my 8 (physical) core machine it reduces the time from 600ms to 200ms for a large project. For the JSON parsing, it goes from 1,500 ms to 300ms. for performance testing, i can take a look. but first, are we good with these at a high level? (i.e., excluding stuff like try vs. try!, which can easily be fixed)

@brentleyjones
Copy link
Collaborator

I can't speak for @yonaskolb, but I'm interested in this. From a cursory glance I'm good with it at a high level.

@michaeleisel
Copy link
Contributor Author

ok i'm happy with it now personally as well. here are the perf test results: https://gist.github.com/michaeleisel/3d1a5316d233571ca5e2ec9531351c82

@michaeleisel
Copy link
Contributor Author

are we good to merge?

Copy link
Collaborator

@brentleyjones brentleyjones left a comment

Choose a reason for hiding this comment

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

Seems our project doesn't benefit much from this (not to say it's not a good change).

image

The spike at the start is this change. The spikes at the end are from tuist/XcodeProj#529.


Can you add a CHANGELOG entry? I think this is good to be merged.

class CarthageVersionLoader {

private let buildPath: Path
private var cachedFiles: [String: CarthageVersionFile] = [:]
private var cachedFilesMutex: Mutex<[String: CarthageVersionFile]> = Mutex([:])
Copy link
Collaborator

Choose a reason for hiding this comment

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

Let's add a comment to CarthageVersionLoader to warn future us.

@brentleyjones
Copy link
Collaborator

More details on our performance bottleneck:

image

Seems it's all in project generation and writing. In particular these two functions are hot spots:

func getAllSourceFiles(targetType: PBXProductType, sources: [TargetSource]) throws -> [SourceFile] {
try sources.flatMap { try getSourceFiles(targetType: targetType, targetSource: $0, path: project.basePath + $0.path) }
}

(or just https://github.com/yonaskolb/XcodeGen/blob/master/Sources/XcodeGenKit/PBXProjGenerator.swift#L222)

and

https://github.com/tuist/XcodeProj/blob/master/Sources/XcodeProj/Utils/ReferenceGenerator.swift#L27

@michaeleisel
Copy link
Contributor Author

Yeah, I've only ensured it improves my own use case. I spent some time with that getAllSourceFiles. Without a deep understanding, it seems somewhat difficult to optimize. Maybe we could open another issue about it.

@michaeleisel
Copy link
Contributor Author

@brentleyjones ptal

@brentleyjones brentleyjones merged commit c3693d4 into yonaskolb:master Apr 3, 2020
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