Type Inference issues

When I was evaluating the performance of some of the code I was using, I noticed some weird performance issues due to type inference, specifically between two UIView methods:

UIView.convert(_ rect: CGRect, from coordinateSpace: UICoordinateSpace)

UIView.convert(_ rect: CGRect, from view: UIView?)

The first method converts between any two coordinate spaces, which UIView conforms to, and the second method evaluates from one view to another. The first method is, approximately 14 times slower than the second, though they both have the same final outcome. This seems to be because the coordinate-based method must be able to convert between any two coordinate spaces, rather than assuming that the views are in the same hierarchy.

The issue arises, however, in how the compiler determines which method to fire between the two, when you pass it a non-optional view. Which method should the compiler choose?

The compiler chooses to go to the non-optional variable, which is the first one, rather than the second, which would be more efficient. We can work around this by casting the view to UIView?, which will then cue the compiler to choose the second method.

Are there any cleaner methods to determine which method will be chosen? And do you think I should consider submitting this as a bug on UIKit to recommend they consider the implications on apps which may be unwittingly being hit with an artificial 14x performance penalty?

2 Likes

Could the method in UIKit be updated as follows?

 @implementation UIView (UICoordinateSpace)

 - (CGRect)convertRect:(CGRect)rect
   fromCoordinateSpace:(id <UICoordinateSpace>)coordinateSpace {
+    if ([coordinateSpace isKindOfClass:[UIView class]]) {
+        return [self convertRect:rect fromView:(UIView *)coordinateSpace];
+    }

For example the standard library has similar optimizations.

1 Like

Please do submit a bug to https://bugreport.apple.com. The UIKit folks can talk to us Swift folks about the best answer, whether it's the dynamic check or something else.

1 Like

I think the workaround would need to be a little smarter, to ensure the view is within the same hierarchy, but otherwise I think that’s doable. It would add an overhead, but is far better than the 14x slowdown. The 14x appears to be due to having to map both hierarchies back to the UIScreen’s cooridinate space, and then do a stepped conversion between the two hierarchies. This is unnecessary where the views are in the same heirarchy.

The only concern is that wouldn’t work on older versions of iOS, it would only be a future optimisation. Alternately they could rename the method in the UIKit translation, but that of course would be source breaking. Considering how relatively fast these methods are in practice anyway, in not sure it’s worth breaking source for it.

I’ll submit a bug, and let the UIKit team decide the best course of action.

2 Likes