尽管我尽了最大努力使CoreML MLModel进程并行,但它的预测似乎是幕后苹果迫使它以串行/逐个的方式运行.

我制作了一个公共存储库,复制该问题的PoC: https://github.com/SocialKitLtd/coreml-concurrency-issue.

What I have tried:

  • 每次重新创建MLModel,而不是全局实例
  • 仅使用.cpuAndGpu个配置

What I'm trying to achieve:
I'm trying to utilize multithreading to process a bunch of video frames at the same time (assuming the CPU/RAM can take it) faster than the one-by-one strategy.

代码(也显示在存储库中):

class ViewController: UIViewController {

    override func viewDidLoad() {
        super.viewDidLoad()
        let parallelTaskCount = 3
        
        for i in 0...parallelTaskCount - 1 {
            DispatchQueue.global(qos: .userInteractive).async {
                let image = UIImage(named: "image.jpg")!
                self.runPrediction(index: i, image: image)
            }
        }
    }

    
    func runPrediction(index: Int, image: UIImage) {
        let conf = MLModelConfiguration()
        conf.computeUnits = .cpuAndGPU
        conf.allowLowPrecisionAccumulationOnGPU = true
        
        let myModel = try! MyModel(configuration: conf)
        let myModelInput = try! MyModelInput(LR_inputWith: image.cgImage!)
        // Prediction
        let predicition = try! myModel.prediction(input: myModelInput)
        print("finished proccessing \(index)")
    }
    
}

Any help will be highly appreciated.

推荐答案

当您在CPU上采用并行执行时,通常可以在CPU限制的计算上获得显著的性能提升.但.cpuAndGPU的CoreML也受到GPU的限制,这会降低您享受的并行性.在我的实验中,我看到了运行基于GPU的CoreML计算的较小的性能优势(在iPhone和M1 iPad上从串行操作分别提高了13%和18%),但在Mac Studio上获得了更多实质性的好处(速度是前者的两倍多).


使用工具(通过在Xcode中按命令-i或 Select "产品"»"配置文件")的评测可能很有启发性.请参见Recording Performance Data.

首先,让我们先比较一下.cpuOnly个方案中的computeUnits个.在这里,它按顺序运行20个CoreML prediction调用(1个调用中的maxConcurrentOperationCount个):

enter image description here

而且,如果我切换到CPU视图,我可以看到它在我的iPhone 12 Pro Max上的两个性能核心之间 skip :

enter image description here

这事儿可以理解.好的,现在让我们将maxConcurrentOperationCount改为3,总体处理时间(processingAll函数)从5分钟降至3.5分钟:

enter image description here

当我切换到CPU视图,以查看发生了什么情况时,它似乎开始在两个性能核心上并行运行,但切换到了一些效率核心(可能是因为设备的散热状态变得紧张,这解释了我们没有达到任何接近2倍的性能):

enter image description here

因此,在执行仅使用CPU的CoreML计算时,并行执行可以带来显著的好处.话虽如此,仅使用CPU的计算比使用GPU的计算要慢得多.


当我切换到.cpuAndGPU时,maxConcurrentOperationCount的1和3的差异要小得多,允许三个并发操作时需要45秒,而当连续执行时需要50秒.在这里,它并行运行三个:

enter image description here

和顺序:

enter image description here

但与.cpuOnly个场景相比,您可以在CPU跟踪中看到,CPU大部分处于空闲状态.下面是后者,其中的CPU视图显示了详细信息:

enter image description here

因此,人们可以看到,让它们在多个CPU上运行并不会获得太大的性能提升,因为这不是CPU限制的,而是明显受到GPU的限制.


以下是我的上述代码.注意,我使用了OperationQueue,因为它提供了一种简单的机制来控制并发度(maxConcurrentOperationCount:

import os.log

private let poi = OSLog(subsystem: "Test", category: .pointsOfInterest)

func processAll() {
    let parallelTaskCount = 20

    let queue = OperationQueue()
    queue.maxConcurrentOperationCount = 3          // or try `1`

    let id = OSSignpostID(log: poi)
    os_signpost(.begin, log: poi, name: #function, signpostID: id)

    for i in 0 ..< parallelTaskCount {
        queue.addOperation {
            let image = UIImage(named: "image.jpg")!
            self.runPrediction(index: i, image: image, shouldAddContuter: true)
        }
    }

    queue.addBarrierBlock {
        os_signpost(.end, log: poi, name: #function, signpostID: id)
    }
}

func runPrediction(index: Int, image: UIImage, shouldAddContuter: Bool = false) {
    let id = OSSignpostID(log: poi)
    os_signpost(.begin, log: poi, name: #function, signpostID: id, "%d", index)
    defer { os_signpost(.end, log: poi, name: #function, signpostID: id, "%d", index) }

    let conf = MLModelConfiguration()
    conf.computeUnits = .cpuAndGPU                 // contrast to `.cpuOnly`
    conf.allowLowPrecisionAccumulationOnGPU = true
    
    let myModel = try! MyModel(configuration: conf)
    let myModelInput = try! MyModelInput(LR_inputWith: image.cgImage!)
    // Prediction
    let prediction = try! myModel.prediction(input: myModelInput)
    os_signpost(.event, log: poi, name: "finished processing", "%d %@", index, prediction.featureNames)
}

Note, above I have focused on CPU usage. You can also use the “Core ML” template in Instruments. E.g. here are the Points of Interest 和 the CoreML tracks next to each other on my M1 iPad Pro (with maxConcurrencyOperationCount set to 2 to keep it simple):

enter image description here

乍一看,CoreML似乎在并行处理这些任务,但如果我以1maxConcurrencyOperationCount(即,串行)再次运行它,这些单独的计算任务的时间会更短,这表明在并行场景中,存在一些与GPU相关的争用.

Anyway, in short, you can use Instruments to observe what is going on. And one can achieve significant improvements in performance through parallel processing for CPU-bound tasks only, 和 anything requiring the GPU or neural engine will be further constrained by that hardware.

Ios相关问答推荐

将图案UI视图动画化以模拟进度条

有没有办法观察滚动的SwiftUI图表的内容偏移量?

由于代码签名时出错,无法在iOS设备上发布iOS Maui Build

无法在所有窗口上方显示图像

带有RadStudio 11.3/12的Mac Mini M2(Sonoma 14.2.1)上的PAServer-测试连接按钮有效,但部署不起作用

为什么AVSpeechSynthesizer复制的信息比我的文本多?

SwiftUI HStack 文本字段对齐

NavigationLink 只能在文本部分点击

一对多关系 Firebase 实时数据库(嵌入数百万条 comments )

iOS 16 堆栈视图中按钮的奇怪动画

小部件链接有效,但不执行任何操作

如何为视图的 colored颜色 和宽度设置动画?

SwiftUI - 使用切换switch 切换键盘类型(默认/数字)?

SwiftUI - 在ForEach的每个元素之间自动添加分隔符

在 Swift 中按下返回键时在文本字段之间切换

界面生成器 - 无法从路径加载设计对象(空)

在滚动视图中使用动态大小的控制器调整容器视图的大小

Swift - 获取设备的 WIFI IP 地址

如何为 UILabel 的背景 colored颜色 设置动画?

如何立即移动 CALayer(无动画)