4

I'm so confused as to what's going on but basically, this is my code. Maybe I just am stupid or do not enough above swift or something but I feel like this should take less than a second, but it takes a very long time to iterate through (i added the CFAbsoluteTimeGetCurrent) because I wanted to see how long each assignment took and they take around 0.0008949041366577148 seconds each, but over the span of 30K that adds up obviously. This is just test code but what I'm really trying to do is implement Agglomerative Clustering using a single linkage in Swift, at first I thought that my algorithm was written poorly but then I just tried to iterate over an array like this and it still took a long time. Does anyone know what's up?

I'm also aware that printing out statements in the console takes time but even after removing these statements the onAppear closure still took a while to finish.

Also sorry this is my first time ever posting on Stack Overflow so if please let me know if I should write my posts a certain way in the future.

    @State private var mat: [Double?] = Array(repeating: nil, count: 30000)
    
    var body: some View {
        Text("HELLo")
            .onAppear() {
                for i in 0..<matrix.count {
                    let start = CFAbsoluteTimeGetCurrent()
                    mat[i] = 0
                    let diff = CFAbsoluteTimeGetCurrent() - start
                    print("HAC_SINGLELINK.init TIME: \(diff), ROW \(i) of \(matrix.count)")
                  }
                    
                }
            }
  • 1
    `print` takes _quite_ a lot of time. Even if you remove `print`, Looping also takes a lot of time. Most of the time is not spent assigning the value, but looping. Related: https://stackoverflow.com/questions/67021110/why-is-for-in-slower-than-while-in-swift-debugging-mode Try running it in Release mode. – Sweeper Jun 24 '21 at 03:47
  • Even if you just calculate 0.0008949041366577148 * 30,000, it's about 27 seconds (but then I'm not sure if your benchmarking techniques are accurate). You can also try using the Accelerate framework for these calculations, if you are not satisfied with 27 seconds. Also note that this can always be done on a background queue. – Sweeper Jun 24 '21 at 03:50
  • Okay wow thank you for the fast reponse, one thing I'm still not understanding is that in the link you posted the for in loop is iterating over 10 Million elements and finishes in 3 seconds unoptimized but mine still takes much longer than that even though its only 30K – Sahil Srivastava Jun 24 '21 at 03:58

1 Answers1

3

I believe the time is caused by the number of modifications you do to your @State variable, which cause a lot of overhead.

With your initial code, on my machine, it took ~16 seconds. With my modified code, which does all of the modifications on a temporary non-state variable and then assigns to @State once, it takes 0.004 seconds:

.onAppear() {
    let start = CFAbsoluteTimeGetCurrent()
    
    var temp = matrix
    
    for i in 0..<temp.count {
        temp[i] = 0
    }
    let diff = CFAbsoluteTimeGetCurrent() - start

    matrix = temp
    
    print("HAC_SINGLELINK.init TIME: \(diff) \(matrix.count)")
                    
}
jnpdx
  • 45,847
  • 6
  • 64
  • 94
  • The only thing that still confuses me is that originally I was executing this behavior in a separate class that wasn't connected to a state object and getting similar results but I guess I need to look into that more – Sahil Srivastava Jun 24 '21 at 04:01
  • Also I'm not sure if this means anything to you but in my time profiler on my actual code for the HAC algorithm 'specialized Array._makeMutableAndUnique()' is taking 302.0 ms – Sahil Srivastava Jun 24 '21 at 04:03
  • @SahilSrivastava `Array._makeMutableAndUnique()` seems to be something to do with `Array`'s COW semantics. Every time you modify an `Array`, you create a new copy of it. – Sweeper Jun 24 '21 at 04:06
  • Okay, do you think if I posted my original code you could look at it, I'm still very confused as to what is causing me my issue – Sahil Srivastava Jun 24 '21 at 04:15
  • @SahilSrivastava are you talking about a GitHub link or something that can be easily copied and pasted here? I can take a look, although we're getting into funny territory here about if it makes sense to start a new question, edit this one, etc – jnpdx Jun 24 '21 at 05:02
  • Adding to this answer: any time you change the `@State` variable, the `View`'s body is invalidated and redrawn, that's why you are seeing such a bad performance and this snippet fixes it. – pgb Jun 24 '21 at 20:45
  • @pgb Are you sure that updates aren’t batched at all? – jnpdx Jun 24 '21 at 20:59
  • Can't tell 100% sure... but the idea is that the body is a function of the state. If the state changes, the view invalidates, and body is called again. A nice explanation can be found in [this WWDC talk](https://developer.apple.com/videos/play/wwdc2021/10022/) – pgb Jun 25 '21 at 22:02
  • Hi, I actually figured it out after some time working on it, my original code was just poorly optimized so I fixed that and everything is good now! – Sahil Srivastava Jul 01 '21 at 01:34