Fast constants (with calculation) in functions?

Here is a simple Swift function

fileprivate func test()->String{ let c = Array("abc".characters) let k = UInt32(c.count) let r = Int(arc4random_uniform(k)) return String(c[r]) } 

(I chose this example because obviously this is what you can call billions of times to generate some kind of output, so you might be interested in performance when setting two constants.)

Note that getting c requires a bit of computation, and to get k he must use c .

My question is simple: every time you call this function

 test() test() test() 

Does it really calculate k and / or c every time I call it, or are they only calculated once ?

(if "only once", then like a curiosity: does it do it the first time I call a function? "or maybe the compiler will arrange it separately for launch at startup?" or, if known, it can calculate them in compilation time?)


I often use global calculated properties, rather like this

 let basicDF : DateFormatter = { print("this will only be done once per launch of the app") let formatter = DateFormatter() formatter.dateFormat = "yyyy-MM-dd" return formatter }() 

(possibly with fileprivate ). If the answer to the specified question is β€œno”, c and β€œk” are calculated every time you call the test, ”then in this case, how can you put some kind of static calculated property inside the function

+5
source share
2 answers

No, in your particular case, the compiler does not currently optimize c and / or k for constant expressions that are evaluated only once (this can be seen by examining IR in an optimized assembly) - although this can all be changed with future versions of the language.

However, it is worth noting that currently this can be done for simpler expressions, for example:

 func foo() -> Int { return 2 + 4 } 

The compiler can evaluate the addition at compile time, so the function simply does return 6 (and then it can be inlined). But, of course, you should only worry about such optimizations in the first place, if you really identified this function as a performance bottleneck.

One of the nice tricks to get static constants in a function is to define an indifferent enum with static properties in the function area, in which you can define your constant expressions:

 func test() -> String { enum Constants { static let c = Array("abc".characters) static let k = UInt32(c.count) } let r = Int(arc4random_uniform(Constants.k)) return String(Constants.c[r]) } 

Now both initializer expressions for c and k will be evaluated only once, and this will be done when they are first used (for example, when the function will be called first).

And of course, as you show, you can use the instantly evacuated closure to have a multi-line initialization expression:

 enum Constants { static let c: [Character] = { // ... return Array("abc".characters) }() // ... } 
+3
source

I think you should assume that c and k are calculated every time. The calculation can be optimized as a detail of the compiler implementation, but I would not count on it if I were you.

Swift does not have the equivalent of a C static local variable (that is, an β€œautomatic” variable inside a function whose value is maintained between function calls).

If you really want to make an effort to make sure k calculated only once, make it constant (i.e. at class level). Of course, you will also have to do the same for c , as you need, in subsequent calculations. This seems like a silly example in this case, but I often do it when the thing being created is a heavy weight, like an image or presentation that will be used over and over again:

 class MyView : UIView { lazy var arrow : UIImage = self.arrowImage() func arrowImage () -> UIImage { // ... big image-generating code goes here ... } } 

I called arrowImage() again and again, until I told myself, wait, I can make it a constant (here it is expressed as lazy var ) and calculate it only once, namely the first time arrow .

+1
source

All Articles