Tuesday, February 14, 2023
HomeiOS DevelopmentWorking duties in parallel - The.Swift.Dev.

Working duties in parallel – The.Swift.Dev.



Having the ability to run duties in parallel is good, it will probably velocity up issues for positive when you may make the most of a number of CPU cores, however how can we really implement these sort of operations in Swift? 🤔


There are a number of methods of working parallel operations, I had an extended article in regards to the Grand Central Dispatch (GCD) framework, there I defined the variations between parallelism and concurrency. I additionally demonstrated learn how to arrange serial and concurrent dispatch queues, however this time I would prefer to focus a bit extra on duties, staff and jobs.


Think about that you’ve got an image which is 50000 pixel huge and 20000 pixel lengthy, that is precisely one billion pixels. How would you alter the colour of every pixel? Effectively, we may do that by iterating by every pixel and let one core do the job, or we may run duties in parallel.


The Dispatch framework affords a number of methods to resolve this problem. The primary resolution is to make use of the concurrentPerform operate and specify some variety of staff. For the sake of simplicity, I’ll add up the numbers from zero to 1 billion utilizing 8 staff. 💪


import Dispatch

let staff: Int = 8
let numbers: [Int] = Array(repeating: 1, rely: 1_000_000_000)

var sum = 0
DispatchQueue.concurrentPerform(iterations: staff) { index in
    let begin = index * numbers.rely / staff
    let finish = (index + 1) * numbers.rely / staff
    print("Employee #(index), gadgets: (numbers[start..<end].rely)")

    sum += numbers[start..<end].scale back(0, +)
}

print("Sum: (sum)")


Cool, however nonetheless every employee has to work on various numbers, possibly we should not begin all the employees without delay, however use a pool and run solely a subset of them at a time. That is fairly a simple process with operation queues, let me present you a primary instance. 😎


import Basis

let staff: Int = 8
let numbers: [Int] = Array(repeating: 1, rely: 1_000_000_000)

let operationQueue = OperationQueue()
operationQueue.maxConcurrentOperationCount = 4

var sum = 0
for index in 0..<staff {
    let operation = BlockOperation {
        let begin = index * numbers.rely / staff
        let finish = (index + 1) * numbers.rely / staff
        print("Employee #(index), gadgets: (numbers[start..<end].rely)")
        
        sum += numbers[start..<end].scale back(0, +)
    }
    operationQueue.addOperation(operation)
}

operationQueue.waitUntilAllOperationsAreFinished()

print("Sum: (sum)")


Each of the examples are above are extra ore much less good to go (if we glance by at doable information race & synchronization), however they depend upon extra frameworks. In different phrases they’re non-native Swift options. What if we may do one thing higher utilizing structured concurrency?


let staff: Int = 8
let numbers: [Int] = Array(repeating: 1, rely: 1_000_000_000)

let sum = await withTaskGroup(of: Int.self) { group in
    for i in 0..<staff {
        group.addTask {
            let begin = i * numbers.rely / staff
            let finish = (i + 1) * numbers.rely / staff
            return numbers[start..<end].scale back(0, +)
        }
    }

    var abstract = 0
    for await end result in group {
        abstract += end result
    }
    return abstract
}

print("Sum: (sum)")


Through the use of process teams you may simply setup the employees and run them in parallel by including a process to the group. Then you may anticipate the partial sum outcomes to reach and sum all the pieces up utilizing a thread-safe resolution. This method is nice, however is it doable to restrict the utmost variety of concurrent operations, similar to we did with operation queues? 🤷‍♂️


func parallelTasks<T>(
    iterations: Int,
    concurrency: Int,
    block: @escaping ((Int) async throws -> T)
) async throws -> [T] {
    attempt await withThrowingTaskGroup(of: T.self) { group in
        var end result: [T] = []

        for i in 0..<iterations {
            if i >= concurrency {
                if let res = attempt await group.subsequent() {
                    end result.append(res)
                }
            }
            group.addTask {
                attempt await block(i)
            }
        }

        for attempt await res in group {
            end result.append(res)
        }
        return end result
    }
}


let staff: Int = 8
let numbers: [Int] = Array(repeating: 1, rely: 1_000_000_000)

let res = attempt await parallelTasks(
    iterations: staff,
    concurrency: 4
) { i in
    print(i)
    let begin = i * numbers.rely / staff
    let finish = (i + 1) * numbers.rely / staff
    return numbers[start..<end].scale back(0, +)
}

print("Sum: (res.scale back(0, +))")


It’s doable, I made just a little helper operate much like the concurrentPerform technique, this manner you may execute quite a few duties and restrict the extent of concurrency. The principle concept is to run quite a few iterations and when the index reaches the utmost variety of concurrent gadgets you wait till a piece merchandise finishes and then you definately add a brand new process to the group. Earlier than you end the duty you additionally must await all of the remaining outcomes and append these outcomes to the grouped end result array. 😊


That is it for now, I hope this little article will make it easier to to handle concurrent operations a bit higher.




Supply hyperlink

RELATED ARTICLES

LEAVE A REPLY

Please enter your comment!
Please enter your name here

- Advertisment -
Google search engine

Most Popular

Recent Comments