6

I'm using these Compilation Swift Flag to identify codes that slow down the compilation time:

-Xfrontend -warn-long-function-bodies=100
-Xfrontend -warn-long-expression-type-checking=100

Then after building, I get warnings like these:

Instance method 'startFadePositionTitle()' took 2702ms to type-check (limit: 500ms)

for this part of the code:

func startFadePositionTitle() -> CGFloat {
    let value: CGFloat = ((backgroundImage.frame.height/2 - contentTitle.frame.height/2) - navbarView.frame.height)/2
    return value
}

Can someone explains me what is wrong in this method and what could I possibly improve?

9
  • 3
    I’d read this if I were you. forums.swift.org/t/is-xfrontend-forbidding-enough/18791 This is not for general consumption. Commented Nov 23, 2019 at 6:26
  • 1
    Just an observation, you can simplify this to ((backgroundImage.frame.height - contentTitle.frame.height)/2 - navbarView.frame.height)/2 Commented Nov 23, 2019 at 13:07
  • 2
    Start by optimizing your architecture instead of microoptimizations for specific lines of code. If you split your code base to frameworks (modules), you won't have to solve such problems. Commented Nov 25, 2019 at 14:46
  • 1
    @Sulthan in fact, such issues can generate a lot of extra compile time. You shouldn't call them micro. :) Commented Nov 25, 2019 at 15:12
  • 2
    @TimurBernikovich That's true but the issue with type inferring is that the more types, the more compiler difficulty. Splitting the project can improve the performance of compiling individual functions. Also note that the worst problem when compiling is when a file has to be compiled multiple times due to bidirectional dependencies between files. If you keep dependencies unidirectional, you won't have such problems. However, that's hard to do without actually splitting the project into independent modules. That's why splitting helps so much. Commented Nov 25, 2019 at 19:45

4 Answers 4

10
+100

You should break it into smaller chunks so Swift can perform type-checking more easily. Also, the more you tell, the less Swift has to think. So you can help the compiler by explicitly telling it anything you already know:

func beginFadePositionTitle() -> CGFloat {
    let n: CGFloat = 2
    let a: CGFloat = self.backgroundImage.frame.height/n
    let b: CGFloat = self.contentTitle.frame.height/n
    let ab: CGFloat = a - b
    let c: CGFloat = self.navbarView.frame.height
    let abc: CGFloat = ab - c
    return abc/n
}

Instance method 'beginFadePositionTitle()' took 1ms to type-check (limit: 1ms)

This is the result when you tell everything to compiler. See the difference?

Sign up to request clarification or add additional context in comments.

Comments

1

I recommend to try this one

func startFadePositionTitle() -> CGFloat {
    return ((backgroundImage.frame.height - contentTitle.frame.height)/2.0 - 
           navbarView.frame.height)/2.0
}

with my Xcode 11.2 / Catalina (tested assuming that all those frames are CGRects), there is no such warning with those compiler flags. In the corner case it is possible to use CGFloat(2.0) in corresponding places, but I think it is superfluous.

Comments

0

You are calculating value every time when you call this method startFadePositionTitle

You can specify that once and after than you can use freely.

let value: CGFloat = ((backgroundImage.frame.height/2 - contentTitle.frame.height/2) - navbarView.frame.height)/2

Use that in viewDidLoad method

And optimize your code like that

func startFadePositionTitle() -> CGFloat {
    return value
}

2 Comments

The question is about compile-time processing, not runtime evaluation.
Your answer assumes the frames never change. That's not a good assumption.
0

Floating point division and square root take considerably longer to compute than addition and multiplication. The latter two are computed directly while the former are usually computed with an iterative algorithm. The most common approach is to use a division-free Newton-Raphson iteration to get an approximation to the reciprocal of the denominator (division) or the reciprocal square root, and then multiply by the numerator (division) or input argument (square root).

Source : Why is float division slow?

So i have made it to one division:

((x - y)/2 - z)/2 = (x-y-2z)/4

i.e. You can just write something like this

 (backgroundImage.frame.height - contentTitle.frame.height - 2.0*navbarView.frame.height)/4.0

So small change to function would be something like this

func startFadePositionTitle() -> CGFloat {
    return (backgroundImage.frame.height - contentTitle.frame.height - 2.0*navbarView.frame.height)/4.0
}

2 Comments

It's interesting but the slow compilation seemed to be related to the type casting more than the actual operation time from my test.
The question is about compile-time processing, not runtime evaluation.

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.