-
Notifications
You must be signed in to change notification settings - Fork 10.5k
Adopt bulk Dictionary creation in bridging #21235
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
Adopt bulk Dictionary creation in bridging #21235
Conversation
6445f15
to
7d4a2b6
Compare
I think I understand what's going wrong now :) |
Ok maybe I don't. Somehow we're blowing up trying to retain the thing we just copied out of the NSDictionary. Before I thought we just weren't retaining it (which is probably true, but wasn't the problem). |
d186f68
to
80ef8d5
Compare
@@ -107,19 +107,84 @@ extension Dictionary : _ObjectiveCBridgeable { | |||
result = builder.take() | |||
} | |||
|
|||
private static func _typePun<T>(index:Int, of buffer: UnsafeBufferPointer<AnyObject>, to: T.Type) -> UnsafeMutablePointer<T> { | |||
let typedBase = UnsafeMutableRawPointer(mutating: buffer.baseAddress!).assumingMemoryBound(to: T.self) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This part is a bit ridiculous, but I'm trying really hard to a) avoid binding the memory at base to T prematurely, and b) avoid doing pointer math myself when Swift can already learn the size and alignment from T itself
_ objectBuffer: UnsafeBufferPointer<AnyObject>, | ||
to: T.Type | ||
) { | ||
for i in (0..<objectBuffer.count).reversed() { |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This has to go in reverse because if sizeof(T) > sizeof(AnyObject), we'll be shifting each element as we bridge it
80ef8d5
to
2853a45
Compare
@swift-ci please benchmark |
@swift-ci please test |
@swift-ci please benchmark |
@swift-ci please test |
Build failed |
d55e8f1
to
8783e66
Compare
@swift-ci please test |
@swift-ci please benchmark |
@swift-ci please test |
@swift-ci please benchmark |
Build failed |
Build failed |
Build comment file:Performance: -O
Performance: -Osize
Performance: -Onone
Code size: -swiftlibs
How to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the Noise: Sometimes the performance results (not code size!) contain false Hardware Overview
|
@atrick please feel free to ignore the dictionary machinery, it was suggested that I run the type punning/unsafe pointer games I'm playing past you to make sure they're ok |
Hm. Duplicate keys test wasn't failing locally… I'll have to have a look later. |
) -> Bool { | ||
if _isBridgedVerbatimToObjectiveC(T.self) { | ||
_verbatimBridge(objectBuffer, count: count, to: T.self) | ||
return true |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This doesn't look right -- verbatim bridging should still perform a checked downcast, or we would allow casting an NSDictionary
of NSString
keys to [NSNumber: Any]
.
(Maybe there is room to add an unchecked downcasting variant for use in case we're totally sure.)
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Oops, it looks like we don't perform this check for verbatim-bridged forced downcasts on master, either -- we only check conditional downcasts. That seems like a bug!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Except it isn't -- the check is of course still performed when we're downcasting individual items. This is by design; bridging is done lazily in these cases. Sorry for the brain hiccup!
Despite my confusion about forced casts, conditional casts are always expected to eagerly check that all items can be bridged/downcasted to the target type.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I think I have this right in the most recent update I pushed. The verbatim ones use ? and !, and the non verbatim ones use Swift._forceBridgeFromObjectiveC and Swift._conditionallyBridgeFromObjectiveC. It's possible we could merge some of the cases now, I'm not entirely sure.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
I noticed two issues: (1) downcasts can and should fail even with verbatim bridging, and (2) if an optional cast fails, we should make sure we don't leak previously bridged items. (There should be regressions tests for these -- I'll add some soon.)
The type punning looks valid to me, but I'm by no means an expert in that! Nice work
Update: It is okay not to check nsdict as! [K: V]
at the point of the cast in the verbatim-bridging case -- however, nsdict as? [K: V]
must still be fully verified.
success = _conditionallyBridge(anyKeyPtr, count: numElems, to: Key.self) && | ||
_conditionallyBridge(anyValuePtr, count: numElems, to: Value.self) | ||
|
||
initializedCount = success ? numElems : 0 |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Ah, this is a neat trick. 👌
Figured out why I wasn't seeing this locally. I needed to rebase. |
The memory leaks the new tests noticed are really looking like a compiler bug |
01826d9
to
016ced2
Compare
@swift-ci please test and merge |
@swift-ci please benchmark |
success = _conditionallyBridge(objectVals, | ||
count: numElems, to: Value.self) | ||
if !success { | ||
(UnsafeMutableRawPointer(mutating: objectKeys).assumingMemoryBound(to: |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
For reference, this was the fix for the memory leak >.< cleaning up the buffer that failed worked fine, but if the second buffer failed we wouldn't clean up the first one.
@swift-ci please benchmark |
@swift-ci please test and merge |
Build comment file:Performance: -O
Performance: -Osize
Performance: -Onone
Code size: -swiftlibs
How to read the dataThe tables contain differences in performance which are larger than 8% and differences in code size which are larger than 1%.If you see any unexpected regressions, you should consider fixing the Noise: Sometimes the performance results (not code size!) contain false Hardware Overview
|
@swift-ci please test and merge |
Now what >.< |
That looks like the spurious failure in the Ackermann benchmark @palimondo is fixing. Not your fault. @swift-ci Please test macOS |
Build failed |
@jrose-apple The fix for that spurious lit test failure involving |
Hm. It was Ackermann that failed and the next run passed. Maybe it didn't get fixed all the way? |
I'd like to make sure. @Catfish-Man Do you have a link to that failed CI build? |
@jrose-apple Could you please triple check the ERE in that fix? |
@palimondo unfortunately I don't, sorry! |
@Catfish-Man All is good. @jrose-apple helped me see the error of my ways. Fix is on its way, pending CI test. |
Karoy's new Dictionary initializer gives us access to the uninitialized backing buffers, which we can bulk-copy the contents of the NSDictionary into.