0

我有一个在macOS上运行的Swift应用程序。它批量处理图像。我事先并不知道这些图像将会有多大,以及我的应用程序将运行哪些硬件 - 这些都是依赖于用户的。如何在不耗尽内存的情况下尽可能并行化我的macOS应用程序任务?

我使用GCD来并行处理图像,因为它可以真正加快吞吐量。

但是,在某些情况下,太多的并行化可能会造成伤害:如果用户处理高分辨率图像,并行化会造成太多的内存压力,系统的性能变得非常差。

所以我想找到一种方法来“提供我的并行任务处理器”的速度最大化并行化,同时保持工作负载在RAM(所以没有任何触发器分页&交换:我想避免磁盘IO)。

关于如何做到这一点的任何想法?

+0

你如何并行?我的意思是你是否在可用内核之间分割每个映像,或者是否同时处理多个映像? –

+0

我并行处理多个图像。如果我可以并行处理一个图像的工作,这个问题就不会那么重要。也许我需要在这条道路上投入更多的精力(当我们只处理一幅图像时,它也有较低的延迟),但问题依然存在。 – BearOverflow

+0

更新:我害怕的结果是真实的 - 可并行化的部分并没有从并行化中获益。创建并行任务的开销消除了CPU效率增益的优势。这部分最好的并行化因素是2(除此之外没有更多的收益),这很好,但它只是开始与大量图像(8192x8192)产生差异,这不是标称使用情况。对于名义用例,我认为小的改进并不能保证所产生的代码复杂性。所以这个问题依然存在。 – BearOverflow

回答

0

我最终实现了一个TokenBucket类型的单例,它根据内存需求来处理准入控制。它被初始化,以便我的应用程序可以使用80%的RAM。

let memoryGate = MemoryGate(maxBytes: ProcessInfo.processInfo.physicalMemory*8/10) 

当有人想执行内存密集型操作时,它必须从中请求()内存。如果没有足够的存储空间,则会阻止通话。完成后,线程必须释放()内存。

代码:

class MemoryGate { 

    private let maxBytes : UInt64 
    private var availableBytes : Int64 

    private let cv = NSCondition() 

    init(maxBytes: UInt64) { 
     self.maxBytes = maxBytes 
     self.availableBytes = Int64(maxBytes) 
     Log.debug?.message("maxBytes=\(maxBytes)") 
    } 

    public func request(amount: UInt64) { 
     Log.debug?.message("Resquesting \(amount) bytes") 
     cv.lock() 

     // If the amount is bigger than the max allowed, no amount of waiting is going 
     // to help, so we go through and let the other smaller jobs be held back until 
     // memory is freed 
     if (amount <= maxBytes) { 
      while (availableBytes < Int64(amount)) { 
       cv.wait() 
      } 
     } 

     availableBytes -= Int64(amount) 

     Log.debug?.message("Got \(amount) bytes. availableBytes=\(availableBytes)") 
     cv.unlock() 
    } 

    public func release(amount: UInt64) { 
     cv.lock() 
     availableBytes += Int64(amount) 
     Log.debug?.message("Released \(amount) bytes. availableBytes=\(availableBytes)") 
     cv.broadcast() 
     cv.unlock() 
    } 
} 
0

内存压力事件有一个GCD dispatch source。不知道你的代码是如何构建的,但是有可能创建并行任务,直到你得到一个事件,然后停止制作任务,甚至杀死一些?

+0

这有点棘手,因为在批处理中,我得到了用户想要处理的照片列表(比方说500),并且我需要将它们放入由GCD管理的DispatchQueue中。据说GCD最清楚它将如何使用可用的CPU资源,所以我应该把它们全部扔进GCD来完成它的工作。但是一旦他们进入了他们的生活,我认为我不能收回他们。那时他们都开始分配内存。当我收到DISPATCH_MEMORYPRESSURE_WARN时已经太晚了 - 这些任务已经发送给GCD进行处理。 – BearOverflow

相关问题