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?

Jaythaking
  • 2,200
  • 4
  • 25
  • 67
  • 3
    I’d read this if I were you. https://forums.swift.org/t/is-xfrontend-forbidding-enough/18791 This is not for general consumption. – matt Nov 23 '19 at 06:26
  • 1
    Just an observation, you can simplify this to `((backgroundImage.frame.height - contentTitle.frame.height)/2 - navbarView.frame.height)/2` – Kamil.S Nov 23 '19 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. – Sulthan Nov 25 '19 at 14:46
  • 1
    @Sulthan in fact, such issues can generate a lot of extra compile time. You shouldn't call them micro. :) – Timur Bernikovich Nov 25 '19 at 15:12
  • 1
    First of all try to split this long line into 2-3 smaller expressions. I believe the problem here is 2 without type. – Timur Bernikovich Nov 25 '19 at 15:13
  • 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. – Sulthan Nov 25 '19 at 19:45
  • 1
    Not an answer to your question, but I can't help recommending this code https://github.com/RobertGummesson/BuildTimeAnalyzer-for-Xcode to anyone who needs to understand build-time issues in their code. Really neat (I'm not related to the developer) – Alex Nov 28 '19 at 07:10
  • @Alex Thanks for the link, it's really interesting... Though after further investigation I've notice the issue might be in my objc files... Not sure why though – Jaythaking Nov 28 '19 at 15:07
  • 1
    @Jaythaking. Of course there are multiple reasons for the slow compiling issue but you should work on them **one by one** and not **all at once**. The update you provided is completely different topic from the original question and you should try to enhance it specifically with more attention. – Mojtaba Hosseini Nov 29 '19 at 16:45

4 Answers4

10

You should break it to smaller chunks, then Swift can do type check more easily. Also the more you tell, the less Swift has to think. So you can help compiler and tell 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?

Mojtaba Hosseini
  • 95,414
  • 31
  • 268
  • 278
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.

Asperi
  • 228,894
  • 20
  • 464
  • 690
1

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
}
1

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
}
redhatvicky
  • 1,912
  • 9
  • 8
  • It's interesting but the slow compilation seemed to be related to the type casting more than the actual operation time from my test. – Jaythaking Dec 02 '19 at 17:01