设计Swift算法在mac os X上并行执行

时间:2017-01-09 15:22:13

标签: ios arrays swift macos parallel-processing

- 短版 -

并行化一种算法的最佳方法是什么,该算法在Swift中对大型数组的元素进行成对比较,以便利用多个内核?

- 长版 -

我正在开发一种算法,该算法在(大)数组的元素之间执行成对比较,并从数组中删除不满足给定条件的元素。一旦元素被丢弃,我们就不需要考虑它进行进一步的比较。 这是一个简单的二次算法,其标准实现可能如下所示:

func nonParallelComputation(dataSet: [Point]) -> [Point] {
    var discarded = [Bool](repeating: false, count: dataSet.count)
    for i in 0..<dataSet.count {
        if discarded[i] { continue }
        let p1 = dataSet[i] // first point of the comparison

        // try all possible second points
        for j in 0..<dataSet.count {
            if i == j || discarded[j] { continue }
            let p2 = dataSet[j] // second point of the comparison
            discarded[j] = p1.betterThan(point: p2)
        }
    }
    // filtering the dataset
    return dataSet.enumerated().filter { !discarded[$0.offset] }.map { $0.element }
}

不要过分关注实施的细节,无论如何都要大大简化。核心是discarded的{​​{1}}数组,用于标记要删除的数组元素。

我以为我可能会利用我的Mac的8个内核并尝试尽可能地并行化执行。例如,我可以将数组拆分为8个块,执行块的元素与数组中所有其他元素之间的比较,并且并行运行这些比较(例如,通过使用Bool),关心同步对共享DispatchQueue.concurrentPerform数组的访问(这里我使用了锁定队列)。

以上代码的天真并行版本如下所示:

discarded

这种方法不能很好地工作,因为显然,锁定阵列需要花费太多时间,因此在并行版本中性能会急剧恶化。

然后我想到不仅要在块中拆分数组,而且还要限制与给定块的元素与另一个给定块的元素的比较,这样在比较元素对时几乎不需要锁。基本上,我做了8次迭代。在第一次迭代中,我将块1的元素与块1的元素进行比较,并且并行地将块2的元素与块2的元素进行比较,并且并行地,...直到块8的元素与在第二次迭代中,我将块1的元素与块2的元素进行比较,并且并行地将块2的元素与块3的元素进行比较,等等。在每次迭代结束时,我更新了func parallelComputation(dataSet: [Point]) -> [Point] { var discarded = [Bool](repeating: false, count: dataSet.count) let numberOfParallelThreads = 8 let lockQueue = DispatchQueue(label: "myQueue") DispatchQueue.concurrentPerform(iterations: numberOfParallelThreads) { iteration in let stride = Int(ceil(Double(dataSet.count) / Double(numberOfParallelThreads))) let firstIndex = iteration * stride let lastIndex = min((iteration + 1) * stride, dataSet.count) guard firstIndex < lastIndex else { return } for i in firstIndex..<lastIndex { var firstPointDiscardedCheck = false lockQueue.sync() { firstPointDiscardedCheck = discarded[i] } if firstPointDiscardedCheck { continue } let p1 = dataSet[i] // first point of the comparison // try all possible second points for j in 0..<dataSet.count { let p2 = dataSet[j] // second point of the comparison var secondPointDiscardedCheck = false lockQueue.sync() { secondPointDiscardedCheck = discarded[j] } if i == j || secondPointDiscardedCheck { continue } if p1.betterThan(point: p2) { lockQueue.sync() { discarded[j] = true } } } } } // filtering the dataset return dataSet.enumerated().filter { !discarded[$0.offset] }.map { $0.element } } 数组。 这在性能方面具有预期的好处(仅适用于较大的阵列),但似乎过于复杂(参见下面的代码)。

编辑:我在此处添加了代码,但我对此解决方案不满意

discarded

虽然对于足够大的阵列而言比非并行解决方案快3-4倍,但该解决方案至少存在两个问题:1)它看起来过于复杂,2)它硬编码所需数量的并行线程。 / p>

必须有更好(更简单)的方式。

有什么想法吗?

0 个答案:

没有答案