Skip to content

Conversation

@briancroom
Copy link
Contributor

I've done some experiments and am pretty confident performance regression described in SR-3701 can be attributed almost entirely to the cost of NSDictionary->Dictionary bridging for the value returned from _XCTRunThrowableBlockBridge. By returning nil instead of an empty dictionary in the common case where no exception is encountered, we skip any dictionary-bridging work which can become expensive if making assertions in a tight loop.

Resolves SR-3701.

Alternatives Considered

We had removing the exception-catching behavior altogether, because, according to @jrose-apple, Swift does not produce exception-safe ARC code, so passing ObjC exceptions across a Swift language boundary isn't a safe operation. Additionally, support for catching ObjC exceptions thrown in an assertion expression was added prior to the advent or Swift's error handling system, and the decision hasn't been revisited since.

I am still open to the possibility of making this larger change, but I wanted to start by addressing the immediate regression.

By returning `nil` instead of an empty dictionary in the common case where
no exception is encountered, we skip any dictionary-bridging work which
can become expensive if making assertions in a tight loop.
@briancroom
Copy link
Contributor Author

@swift-ci please test

@briancroom
Copy link
Contributor Author

cc @czechboy0 @ddunbar

@briancroom
Copy link
Contributor Author

@modocache may also be interested!

}

return [result retain];
return result;
Copy link
Contributor

Choose a reason for hiding this comment

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

I wonder why the original code had retain here? I don't know what the requirements on this code are as far as the expected retain convention, but I think this is a behavior change?

Copy link
Contributor

Choose a reason for hiding this comment

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

Oops, no, that was a bug. It was necessary when we were using @_silgen_name to get at the function, but it's just been leaking since I took that out. :-(

Copy link
Contributor Author

Choose a reason for hiding this comment

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

Yeah, that's what I thought based on the comment that was also removed here

@modocache
Copy link
Contributor

Out of curiosity (and apologies if this is obvious), is it dictionary bridging specifically that's expensive? If, instead of an NSDictionary, the function returned a plain ol' NSObject, would that reduce overhead? Or would it increase it?

@czechboy0
Copy link
Member

czechboy0 commented Jan 24, 2017

Looks good. We might consider adding perf tests to 1) measure how big of a difference this made and 2) catch any potential future regressions right away.

@jrose-apple
Copy link
Contributor

Bridging is expensive; returning an NSObject would avoid bridging completely, at the cost of having to downcast later on. I suggested this as an option to @ddunbar originally but I like the compromise he and @briancroom came up with if it works; it's sacrificing less type information and actually feels better for what it's trying to do. An empty dictionary and a nil dictionary really could be different here, though we put the "type": "unknown" key in to keep from ever having an empty dictionary.

@briancroom
Copy link
Contributor Author

@czechboy0 I was working with an ad-hoc performance test I whipped up while exploring this change, but I wasn't really sure how to integrate it into a suite.

@jrose-apple do you think it would make sense for me to look into adding a performance test for this to the benchmark suite? I don't see anything in there currently that exercises overlay code.

@briancroom briancroom merged commit dc4e856 into swiftlang:master Jan 24, 2017
@briancroom briancroom deleted the fix-XCTAssert-perf-regression branch January 24, 2017 18:53
@jrose-apple
Copy link
Contributor

I don't work with the benchmark suite much. @lplarson or @swiftix are probably better people to ask.

@modocache
Copy link
Contributor

On the subject of a benchmarking suite: what was @ddunbar using when he found the regression in the first place? If that's run continuously, then it could suffice for now.

@ddunbar
Copy link
Contributor

ddunbar commented Jan 24, 2017

@modocache I was running one of my own performance tests on a private project, and it happened to exercise XCTAssertEqual enough that this regressed its performance in a way I noticed. I probably would notice again if it happened, but its not directly tied to Swift in any way.

@gottesmm
Copy link
Contributor

@ddunbar Is it possible to reduce it to a test case?

@modocache
Copy link
Contributor

If it can't be, then +1 for some sort of performance test. I think the cause of the regression is too subtle for most contributors to notice, were they to undo this improvement.

@briancroom
Copy link
Contributor Author

Issue: rdar://problem/29644454

@briancroom
Copy link
Contributor Author

I'll have a look at putting together a proper performance test for this.

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.

6 participants