Quantcast

Pragmaticni Apple dodao u Swift 5.5 async/await :P

bmaxa

Poznat
Moderator
Poruka
8.200
Elem za ovu gimnastiku ce vam biti potreban XCode 13 beta i macOS 11+
Elem
Swift:
@main
struct Main{
  static func main()async{
      for _ in 0..<10 {
          print("starting")
          async let a = f()
          async let b = f()
          async let c = f()
          print(await a + b + c)
      }
  }
  static func f()->Int{
      i += 1
      sleep(1)
      print("async ",i)
      return i
  }
    static var i:Int = 0
}

Fora je da ovo bas i nije toliko korisno posto async ne pokrece thread momentalno nego na await.
Zbog toga sto async moze samo uz let pa da radi paralelno rezultati se ne mogu smestiti u niz.
I onda async let const1 itd.
Inace f-ja se pokrece na await, samo meni nije jasno koja svrha toga kada moras da cekas da
se f-ja zavrsi a ne mozes nista drugo da radis :P
Kod:
starting
async  3
async  3
async  3
9
starting
async  6
async  6
async  6
18
starting
async  9
async  9
async  9
27
starting
async  12
async  12
async  12
36
starting
async  15
async  15
async  15
45
starting
async  18
async  18
async  18
54
starting
async  21
async  21
async  21
63
starting
async  24
async  24
async  24
72
starting
async  27
async  27
async  27
81
starting
async  30
async  30
async  30
90
Kao sto vidite po 3 ove f-je se izvrsavaju paralelno i to je jedini nacin.
Inace ova async let fora je dobra jer ne morate da markirate f-je eksplicitno async...
 

bmaxa

Poznat
Moderator
Poruka
8.200
A sad za one koji su stavili Monterey 12 icu. Dodali su klasu Task koja prakticno zamenjuje Thread na convenient nacin.
Evo naseg progija sa Taskom, bez asynca:
Swift:
//
//  main.swift
//  firstRightAsyncProgram
//
//  Created by Branimir Maksimovic on 20.7.21..
//

import Foundation

@main
struct Main{
    struct A{
        static func set(_ index:Int,_ val:Int){
            future[index] = val
            finish(index)
        }
        static func get(_ index:Int)->Int{
            var counter = 0
            while(!finished[index]){ counter += 1 }
            print("waited ",counter)
            return future[index]
        }
        static var future:[Int] = Array(repeating: 0, count: 10)
        static func finish(_ index:Int){
            finished[index] = true
        }
        static var finished:[Bool] = Array(repeating: false, count: 10)
    }
    static func main(){
        print("entering main")
        for i in 0..<10 {
            Task(priority: .default){
                print("starting")
                A.set(i, f(i))
            }
        }
        Task(priority: .default){
            for i in 0..<10 {
                print("result ",A.get(i))
            }
            exit(0)
        }
        dispatchMain()
    }
    static func f(_ j:Int)->Int{
      i += 1
      sleep(1)
      print("async ",i)
      return i
    }
    static var i:Int = 0
}

I izvrsavanje:
Kod:
entering main
starting
starting
starting
starting
starting
starting
starting
starting
async  8
async  8
async  8
async  8
async  8
starting
starting
waited  114
async  8
result  8
waited  0
result  8
waited  0
result  8
waited  0
result  8
async  8
waited  0
result  8
async  8
waited  216
result  10
waited  0
result  10
waited  0
result  10
async  10
waited  83057997
result  10
async  10
waited  104244
result  10
Program ended with exit code: 0

Hm, ovo je zaista async. Da vidimo sada kada iskoristimo async za f sta ce biti...
Swift:
//
//  main.swift
//  firstRightAsyncProgram
//
//  Created by Branimir Maksimovic on 20.7.21..
//

import Foundation

@main
struct Main{
    struct A{
        static func set(_ index:Int,_ val:Int){
            future[index] = val
            finish(index)
        }
        static func get(_ index:Int)->Int{
            var counter = 0
            while(!finished[index]){ counter += 1 }
            print("waited ",counter)
            return future[index]
        }
        static var future:[Int] = Array(repeating: 0, count: 10)
        static func finish(_ index:Int){
            finished[index] = true
        }
        static var finished:[Bool] = Array(repeating: false, count: 10)
    }
    static func main(){
        print("entering main")
        for i in 0..<10 {
            Task(priority: .default){
                print("starting")
                await A.set(i, f(i))
            }
        }
        Task(priority: .default){
            for i in 0..<10 {
                print("result ",A.get(i))
            }
            exit(0)
        }
        dispatchMain()
    }
    static func f(_ j:Int)async->Int{
      i += 1
      sleep(1)
      print("async ",i)
      return i
    }
    static var i:Int = 0
}
Samo mala izmena
i izvrsavanje:
Kod:
entering main
starting
starting
starting
starting
starting
starting
starting
starting
async  8
async  8
async  8
async  8
starting
starting
async  8
async  8
async  8
async  8
waited  0
result  8
waited  0
result  8
waited  0
result  8
waited  0
result  10
waited  0
result  10
waited  0
result  10
waited  0
result  10
waited  0
result  8
async  10
waited  81376483
result  10
async  10
waited  328389
result  10
Program ended with exit code: 0
Jel primecuje neko razliku ;)
 

bmaxa

Poznat
Moderator
Poruka
8.200
I sad, na kraju pravo resenje. Swift je uveo `actor` nesto kao synchronized klasa. Potom umesto task koristiti
async f-ju `withTaskGroup`. f-je koje se pozivaju nad aktor instancom moraju biti markirane sa `await`.
Sada nema potrebe da vrtimo da cekamo finished, posto ima await na `run` pa ce collect doci kada
se svi taskovi zavrse.
Swift:
//
//  main.swift
//  firstRightAsyncProgram
//
//  Created by Branimir Maksimovic on 20.7.21..
//

import Foundation

@main
struct Main{
    static var i:Int = 0
    static func f()->Int{
        i += 1
        let j = i
        print("async ",j)
        return j
    }
    actor A{
        var future:[Int] = Array(repeating: 0, count: 10)
        func set(_ index:Int,_ val:Int){
            future[index] = val
        }
        func get(_ index:Int)->Int{
            return future[index]
        }
        func run()async{
            await withTaskGroup(of: Void.self) { group in
                for i in 0..<10 {
                    group.async{
                        print("starting")
                        await self.set(i, f())
                    }
                }
            }
        }
        func collect(){
            for i in 0..<10 {
                print("result ",get(i))
            }
        }
    }
    static func main()async{
        print("entering main")
        let a = A()
        await a.run()
        await a.collect()
    }
}
I izvrsavanje:
Kod:
bmaxa@Branimirs-Air swiftasync % ./bin/firstRightAsyncProgram
entering main
starting
async  1
starting
async  2
starting
async  3
starting
starting
async  5
starting
starting
async  6
async  7
starting
async  8
starting
async  9
starting
async  10
async  4
result  5
result  1
result  3
result  7
result  2
result  8
result  10
result  4
result  9
result  6

I to bi bilo to, ko hoce da instalira XCode 13 pa da proba :P
 

bmaxa

Poznat
Moderator
Poruka
8.200
A sada da rekapituiramo sa konkretnim taskom. Uzeo sam za primer moje resenje problema 512 sa project euler.
Swift:
//  problem512.swift
//  problem512Euler
//
//  Created by Branimir Maksimovic on 21.7.21..
//

import Foundation

@main
struct Main{
    static var factor : Int = 8;
    actor A{
        static func phi(_ nn : Int)->Int {
            var n = nn
            var m = n;
            if n % 2 == 0 {
                m /= 2;
                while n % 2 == 0 {
                    n /= 2;
                }
            }
            var i = 3;
            while (i*i <= n) {
                if n % i == 0 {
                    m /= i;
                    m *= (i-1);
                    while n % i == 0 {
                        n /= i
                    }
                }
                i += 2
            }
            if n > 1 {
                m /= n;
                m *= (n-1)
            }
            return m
        }
        static func f(_ n:Int)->Int {
            if n & 1 == 0 { return 0 }

            return phi(n)
        }
        func g(_ n : Int)async->Int{
            var sum = 0
            await withTaskGroup(of: Int.self) { group in
                for j in 0..<factor {
                    group.async{
                        var i = j + 1
                        var sum = 0
                        print("starting")
                        while i <= n {
                            sum += A.f(i)
                            i += factor
                        }
                        return sum
                    }
                }
                for await result in group {
                    sum+=result
                }
            }
            return sum
        }
    }
    static func main()async{
        let task = Process()
        task.executableURL = URL(fileURLWithPath: "/usr/sbin/sysctl")
        task.arguments = ["hw.ncpu"]
        let out = Pipe()
        task.standardOutput = out
        let res = try? task.run()
        if let _ = res {
            let outputData = out.fileHandleForReading.readDataToEndOfFile()
            let s = String(decoding: outputData, as: UTF8.self)
            print("got from sysctl ",s)
            let sep = CharacterSet(charactersIn: " \n")
            let components = s.components(separatedBy: sep)
            factor = Int(components[1]) ?? 4
        }
        print("factor ",factor)
        print("entering main")
        let a = A()
        await print(a.g(100))
        await print(a.g(Int(5e8)))
    }
}
i izvrsavanje:
Kod:
bmaxa@Branimirs-Air project_euler % time ./Problem512Euler/bin/Problem512Euler
got from sysctl  hw.ncpu: 8

factor  8
entering main
starting
starting
starting
starting
starting
starting
starting
starting
2007
starting
starting
starting
starting
starting
starting
starting
starting
50660591862310323
./Problem512Euler/bin/Problem512Euler  352.35s user 0.51s system 399% cpu 1:28.26 total

Dakle na M1 sa 4+4 za minut i po, a na 2700x sa 8/16 treba 5 minuta :P
Cudo je ovaj M1 :P
 

Top
  Blokirali ste reklame
Dragi prijatelju, nemojte da blokirate reklame - isključite Ad Blocker na Forumu, jer će tako mesto vaših susreta na Krstarici ostati besplatno za korišćenje.