如何在Swift中以并行方式缩放随机模拟?
我正在玩Swift 5.1(在Mac上)的游戏,模拟网球比赛。当然,模拟的一部分是随机选择谁赢得每一分 下面是我进行并行处理的代码的相关部分如何在Swift中以并行方式缩放随机模拟?,swift,random,parallel-processing,Swift,Random,Parallel Processing,我正在玩Swift 5.1(在Mac上)的游戏,模拟网球比赛。当然,模拟的一部分是随机选择谁赢得每一分 下面是我进行并行处理的代码的相关部分 func combine(result: MatchTally) { overallTally.add(result: result) } DispatchQueue.concurrentPerform(iterations: cycleCount){iterationNumber in var counter = MatchTally(
func combine(result: MatchTally)
{
overallTally.add(result: result)
}
DispatchQueue.concurrentPerform(iterations: cycleCount){iterationNumber in
var counter = MatchTally()
for _ in 1...numberOfSimulations
{
let result = playMatch(between: playerOne, and: playerTwo)
counter[result.0, result.1] += 1
}
combiningQueue.sync {combine(result: counter)}
}
选择适当的模拟运行计数后,单个队列大约需要5秒。如果我将并发队列设置为2,那么模拟现在每个队列需要3.8秒(即7.2秒)。再次加倍到4个队列将导致4.8s/队列。最后是6个队列(机器是6核Intel i7),每队列需要5.6s
对于那些需要更令人信服地证明这与随机数生成有关的人(我使用的是Double.random(0…1)
),我用一个固定的结果替换了大多数随机结果生成的代码(我不能替换第二位,因为我仍然需要平局),并适当地调整了模拟的数量,结果如下:
- 1队:5秒/队
- 2个队列:2.7秒/队列
- 4个队列:1.9秒/队列
- 6个队列:1.7s/队列
import Foundation
func formatTime(_ date: Date) -> String
{
let df = DateFormatter()
df.dateFormat = "h:mm:ss.SSS"
return df.string(from: date)
}
func something(_ iteration: Int)
{
var tally = 0.0
let startTime = Date()
print("Start #\(iteration) - \(formatTime(startTime))")
for _ in 1...1_000_000
{
tally += Double.random(in: 0...100)
// tally += 3.5
}
let endTime = Date()
print("End #\(iteration) - \(formatTime(endTime)) - elapsed: \(endTime.timeIntervalSince(startTime))")
}
print("Single task performed on main thread")
something(0) // Used to get a baseline for single run
print("\nMultiple tasks performed concurrently")
DispatchQueue.concurrentPerform(iterations: 5, execute: something)
将循环中的随机加法替换为固定加法可以演示代码在一种场景中的伸缩性,而在另一种场景中则不行。看起来解决方案是使用一个不太“时髦”的生成器,如
drand48()
。我相信我已经测试了这个选项,但似乎我错了。这似乎不是同一个问题,所以我想这是arc4random()
的固有特性,我相信Double.random()
就是基于这个特性
另一个积极因素是,返回值的速度大约快4倍。所以我的模拟不会是加密安全的,但是网球比赛是什么呢 看起来解决方案是使用不太“时髦”的生成器,如
drand48()
。我相信我已经测试了这个选项,但似乎我错了。这似乎不是同一个问题,所以我想这是arc4random()
的固有特性,我相信Double.random()
就是基于这个特性
另一个积极因素是,返回值的速度大约快4倍。所以我的模拟不会是加密安全的,但是网球比赛是什么呢?“每个队列3.8秒(即7.2秒)。”并行性并不是这样工作的。据你所知,这两项任务都花了7.2秒。深入分析开始/结束时间,看看线程在哪里花费时间也许我的描述不清楚。完成一个单位的工作需要5秒钟。完成两个单元需要7.2秒。这两项任务都花了那么多时间(我确实测量了任务完成时间),这就是问题所在。如果没有共享资源,当另一个任务并行运行时,任务不应该减慢速度。是的,我理解这个问题,我只是查看您提供的度量的细节。“两个任务都占用了那么多时间”,所以两个任务各自占用了7.2秒的CPU时间,而在同时运行时,占用了7.2秒的“挂钟时间”?六羟甲基三聚氰胺六甲醚。。。你能试着编一个最小的可验证的例子来重现这个问题吗?别忘了,通过改变一行代码,去掉随机函数并用固定结果替换它,情况在可伸缩性方面得到了极大的改善。因此,有人会认为这就是问题所在。我希望我有足够的能力来查看线程切换等的探查器结果并收集更多信息,但我不在那个级别。如果更改工作负载,问题是否会重现?例如,使用一个简单的忙循环来占用CPU时间,而不需要调用您的实际工作负载?“每个队列3.8秒(即花费7.2秒)。”这并不是真正的并行工作方式。据你所知,这两项任务都花了7.2秒。深入分析开始/结束时间,看看线程在哪里花费时间也许我的描述不清楚。完成一个单位的工作需要5秒钟。完成两个单元需要7.2秒。这两项任务都花了那么多时间(我确实测量了任务完成时间),这就是问题所在。如果没有共享资源,当另一个任务并行运行时,任务不应该减慢速度。是的,我理解这个问题,我只是查看您提供的度量的细节。“两个任务都占用了那么多时间”,所以两个任务各自占用了7.2秒的CPU时间,而在同时运行时,占用了7.2秒的“挂钟时间”?六羟甲基三聚氰胺六甲醚。。。你能试着编一个最小的可验证的例子来重现这个问题吗?别忘了,通过改变一行代码,去掉随机函数并用固定结果替换它,情况在可伸缩性方面得到了极大的改善。因此,有人会认为这就是问题所在。我希望我有足够的能力来查看线程切换等的探查器结果并收集更多信息,但我不在那个级别。如果更改工作负载,问题是否会重现?例如,使用一个简单的忙循环来占用CPU时间,而不需要调用您的实际工作负载?