在Swift中优化循环嵌套

问题描述 投票:0回答:2

我得到了一种计算UIImage中白色像素的方法,我需要遍历所有像素以增加找到的每个白色像素的计数。我正在尝试改善它的性能,但是找不到更好的方法。有什么想法吗?

    func whitePixelCount() -> Int {
        let width = Int(image.size.width)
        let height = Int(image.size.height)
        var counter = 0
        for x in 0..<(width*scale) {
            for y in 0..<(height*scale) {
                // We multiply per 4 because of the 4 channels, RGBA, but later we just use the Alpha
                let pixelIndex = (width * y + x) * 4

                if pointer[pixelIndex + Component.alpha.rawValue] == 255 {
                    counter += 1
                }
            }
        }
        return counter
    }
  • Component.alpha.rawValue等于3
  • scale是Int(image.scale)
  • 指针来自:

    guard let cfdata = self.image.cgImage?.dataProvider?.data,
        let pointer = CFDataGetBytePtr(cfdata) else {
            return nil
    }
    
ios swift for-loop uiimage
2个回答
0
投票

通常,可以通过用while循环替换for循环来提高big(o)的性能,这就是说while x

另一种方法是将映像拆分为单独的组件,然后将它们发送到不同的线程,然后重新组合结果数组。您将要使用gcd * workitems async来执行此操作。


0
投票

一种非常简单的方法是使用concurrentPerform并行化例程:

例如,这是一个非并行例程:

var total = 0

for x in 0..<maxX {
    for y in 0..<maxY {
        if ... {
            total += 1
        }
    }
}

print(total)

您可以通过将forx循环替换为concurrentPerform来并行化它:

var total = 0

let syncQueue = DispatchQueue(label: "...")

DispatchQueue.concurrentPerform(iterations: maxX) { x in
    var subTotal = 0
    for y in 0..<maxY {
        if ... {
            subTotal += 1
        }
    }
    syncQueue.sync {
        total += subTotal
    }
}

print(total)

所以,想法是:

  • for替换外部concurrentPerform循环;
  • 而不是尝试对total的每个迭代尝试更新y,对于每个线程都具有一个subTotal变量,并且仅在最后更新count(以最小化total的竞争);和
  • 使用某种同步机制(我在这里使用了串行队列,但是任何同步机制都可以)更新total以确保线程安全。

我试图使示例尽可能简单,但是甚至可以进行其他优化:

  • 如果每个线程上的工作量不足(例如maxX不太大),则并行化例程的开销会开始抵消让多个内核参与计算的好处。因此,您可以在每次迭代中“跨越” y的多行。

  • 不同的同步技术提供不同的性能。例如。您可以通过在协议扩展中定义NSLock方法来使用sync(传统观点认为该速度较慢,但​​我最近的基准测试表明,在许多情况下,性能可能比GCD更好)(以提供一种不错的,安全的方法这样使用锁):

    // Adapted from Apple’s `withCriticalSection` code sample
    
    extension NSLocking {
        func sync<T>(_ closure: () throws -> T) rethrows -> T {
            lock()
            defer { unlock() }
            return try closure()
        }
    }
    

    然后您可以执行以下操作:

    let lock = NSLock()
    
    DispatchQueue.concurrentPerform(iterations: maxX) { x in
        var subTotal = 0
        for y in 0..<maxY {
            if ... {
                subTotal += 1
            }
        }
        lock.sync {
            total += subTotal
        }
    }
    
    print(total)
    

    随意尝试所需的任何同步机制。但是,这样做的想法是,如果要从多个线程访问total,请确保以线程安全的方式进行访问。如果您要检查线程安全性,请暂时打开“ Thread Sanitizer”。

© www.soinside.com 2019 - 2024. All rights reserved.