我的第一个问题!
我正在对视频Feed进行CPU密集型图像处理,我想使用OperationQueue。但是,结果绝对可怕。这是一个例子 - 让我说我有一个CPU密集型操作:
var data = [Int].init(repeating: 0, count: 1_000_000)
func run() {
let startTime = DispatchTime.now().uptimeNanoseconds
for i in data.indices { data[i] = data[i] &+ 1 }
NSLog("\(DispatchTime.now().uptimeNanoseconds - startTime)")
}
我的笔记本电脑需要大约40毫秒才能执行。我跑了一百次:
(1...100).forEach { i in run(i) }
他们平均每人约42毫秒,总共约4200毫秒。我有4个物理内核,所以我尝试在OperationQueue上运行它:
var q = OperationQueue()
(1...100).forEach { i in
q.addOperation {
run(i)
}
}
q.waitUntilAllOperationsAreFinished()
有趣的事情取决于q.maxConcurrentOperationCount:
concurrency single operation total
1 45ms 4500ms
2 100-250ms 8000ms
3 100-300ms 7200ms
4 250-450ms 9000ms
5 250-650ms 9800ms
6 600-800ms 11300ms
我使用.background
的默认QoS,可以看到线程优先级是默认值(0.5)。通过Instruments查看CPU利用率,我看到了很多浪费的周期(第一部分是在主线程上运行,第二部分是在OperationQueue下运行):
我在C中编写了一个简单的线程队列,并使用了Swift中的线程队列,它与内核线性扩展,因此我可以将速度提高4倍。但是我对Swift的错误是什么?
更新:我认为我们已经得出结论,这是DispatchQueue中的合法错误。那么问题实际上是在DispatchQueue代码中询问问题的正确渠道是什么?
答案 0 :(得分:0)
var xml = @"<COMPARABLE_SALE PropertySequenceIdentifier=""3"" ProjectName=""Villages of Devinshire"" ProjectPhaseIdentifier=""1"" PropertySalesAmount=""132500"" SalesPricePerGrossLivingAreaAmount=""109.32"" DataSourceDescription=""FMLS, 5559496;DOM 80"" DataSourceVerificationDescription=""Tax Recs/2ndGen/Deeds"" SalesPriceTotalAdjustmentPositiveIndicator=""N"" SalePriceTotalAdjustmentAmount=""-1500"" SalesPriceTotalAdjustmentGrossPercent=""1.1"" SalePriceTotalAdjustmentNetPercent=""1.1"" AdjustedSalesPriceAmount=""131000"">
<SALE_PRICE_ADJUSTMENT _Type=""GrossBuildingArea"" _Description=""1,254""/>
<SALE_PRICE_ADJUSTMENT _Type=""BasementArea"" _Description=""1,254 Sq.Ft.""/>
<SALE_PRICE_ADJUSTMENT _Type=""BasementFinish"" _Description=""1rr2ba4o""/>
</COMPARABLE_SALE>";
var xDoc = XDocument.Parse(xml);
var description = xDoc.Root.Elements("SALE_PRICE_ADJUSTMENT")
.First(e => e.Attribute("_Type").Value == "GrossBuildingArea")
.Attribute("_Description")
.Value;
将运行优先级最低的线程。如果您正在寻找快速执行,请考虑.background
并确保在开启编译器优化时测量性能。
还要考虑使用DispatchQueue而不是OperationQueue。它可能具有更少的开销和更好的性能。
根据您的评论更新:试试这个。它从我的笔记本电脑上的38秒到14左右。
值得注意的变化:
.userInitiated
永远运行var data = [Int] .init(重复:0,计数:1_000_000)
.background
但仍有问题 - 串行队列运行速度提高3倍,即使它不使用所有核心。
答案 1 :(得分:0)
您似乎衡量每个run
执行的挂钟时间。这似乎不是正确的指标。并行化问题并不意味着每次运行都会执行得更快...它只是意味着您可以一次执行多次运行。
无论如何,让我验证你的结果。
您的函数run
似乎只在某些时候使用参数。让我明确定义一个类似的功能:
func increment(_ offset : Int) {
for i in data.indices { data[i] = data[i] &+ offset }
}
在我的测试机器上,在释放模式下,此代码每次输入需要0.68 ns或每次添加需要大约2.3个周期(3.4 GHz)。禁用绑定检查会有所帮助(每个条目最多0.5 ns)。
总之。接下来让我们按照您的建议将问题并行化:
var q = OperationQueue()
for i in 1...queues {
q.addOperation {
increment(i)
}
}
q.waitUntilAllOperationsAreFinished()
这似乎并不特别安全,但速度快吗?
嗯,它更快......我每次进入0.3 ns。
源代码:https://github.com/lemire/Code-used-on-Daniel-Lemire-s-blog/tree/master/extra/swift/opqueue