memory race on deep looper

Below is some simple code that does nothing, saves nothing. Yet the memory usage keeps rising. Am I missing something or is this a bug? The code requires 6 core CPU minimum. ContentView.swift

import SwiftUI
import Combine

struct ContentView: View {
    
    @EnvironmentObject var loopClass:LoopClass
    
    var body: some View {
        
        ProgressView(value: Float(loopClass.loop_count), total: Float(loopClass.max_loops) ).progressViewStyle(.circular).padding()
        
        Button("Run looper", action: {
            loopClass.loopFunc()
        }
        )
    }
}

LoopClass.swift

import Combine

class LoopClass: ObservableObject {
    @Published var loop_count = 0
    @Published var max_loops  = 0
    
    func loopFunc () {
        let loopSet = [ 1, 3, 5, 7, 11, 13 ]
        
        max_loops = Int(pow(Double(loopSet.count), 13.0))
        print("max_loops = \(max_loops)")
        
        for loop13 in loopSet {
            
            DispatchQueue.global().async {
                
                for loop12 in loopSet {
                    for loop11 in loopSet {
                        for loop10 in loopSet {
                            for loop9 in loopSet {
                                for loop8 in loopSet {
                                    for loop17 in loopSet {
                                        for loop16 in loopSet {
                                            for loop5 in loopSet {
                                                for loop4 in loopSet {
                                                    for loop3 in loopSet {
                                                        for loop2 in loopSet {
                                                            for loop1 in loopSet {
                                                                DispatchQueue.main.async{ self.loop_count += 1 }
                                                            }
                                                        }}}}}}}}}}}
            }   //  DQ
        }       //  for loop13
    }
}

Accepted Reply

It’s hard to say exactly what’s going on here but I’m not surprised you’re seeing unbounded memory growth. You have multiple CPU cores rapidly dispatching work items asynchronously to the main queue. The main queue is serviced by the main thread, so only a single CPU core can pull work items off the queue to run them. That means you’re producing work items faster than you can consume them, so the queue will just keep growing.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Replies

It’s hard to say exactly what’s going on here but I’m not surprised you’re seeing unbounded memory growth. You have multiple CPU cores rapidly dispatching work items asynchronously to the main queue. The main queue is serviced by the main thread, so only a single CPU core can pull work items off the queue to run them. That means you’re producing work items faster than you can consume them, so the queue will just keep growing.

Share and Enjoy

Quinn “The Eskimo!” @ Developer Technical Support @ Apple
let myEmail = "eskimo" + "1" + "@" + "apple.com"

Verified by commenting-out "DispatchQueue.main.async{ self.loop_count += 1 }"

A simple solution was to move the DQ between loop17 and loop16, the ProgressView works and no memory race.

Thanks Eskimo.

  • just to be clear, the DQ morph's to: DispatchQueue.main.async{ self.loop_count += Int(pow(Double(loopSet.count), 6.0)) } It is not continuous but it need not be.

  • Turns out, I was declaring new instances of a structure within each loop. Rookie mistake. Replaced most of my DQ's with Task(), sendable func(). Works great!

Add a Comment