编辑:我保留第一个答案以保留历史记录,但是 OOPer 的暗示导致了完全不同的观点(请参阅下一个答案)。
首先:让我印象深刻的是答案的速度和受过良好教育的速度(我们在周末!)
所以Itai Ferber的建议非常好,按照他的要求,我做了一些性能测试,只是为了给他一些回报;-)
我在操场上使用附加代码运行测试。正如您所看到的,这远远不是一个设计良好的性能测试,它只是一个简单的测试,可以了解性能影响的要点。我做了几次迭代(见下表)。
再次重申:我是在 Playground 中进行的,因此在“真实”应用中绝对时间会好得多,但测试之间的差异会非常相似。
主要发现:
交互显示线性行为(如预期)
“我的”解决方案 (test1) 比“未排队”的全局变量 (test0) 慢约 15 倍
我做了一个测试,是否使用了一个额外的全局变量作为辅助变量(test2),这稍微快了一点,但不是真正的突破
-
Itai Ferber (test3) 建议的解决方案比纯全局变量 (test0) 慢大约 6 到 7 倍...所以它是“我的”解决方案的两倍
李>
所以替代方案 3 不仅看起来更好,因为它不需要辅助变量的开销,而且速度也更快。
// the queue to synchronze data access, it's a concurrent one
fileprivate let globalDataQueue = DispatchQueue(
label: "com.ACME.globalDataQueue",
attributes: .concurrent)
// ------------------------------------------------------------------------------------------------
// Base Version: Just a global variable
// this is the global "variable" we worked with
var globalVariable : Int = 0
// ------------------------------------------------------------------------------------------------
// Alternative 1: with concurrent queue, helper variable insider getter
// As I used a calculated variable, to overcome the compiler errors, we need a helper variable
// to store the actual value.
var globalVariable1_Value : Int = 0
// this is the global "variable" we worked with
var globalVariable1 : Int {
set (newValue) {
globalDataQueue.async(flags: .barrier) {
globalVariable1_Value = newValue
}
}
get {
// we need a helper variable to store the result.
// inside a void closure you are not allow to "return"
var globalVariable1_Helper : Int = 0
globalDataQueue.sync{
globalVariable1_Helper = globalVariable1_Value
}
return globalVariable1_Helper
}
}
// ------------------------------------------------------------------------------------------------
// Alternative 2: with concurrent queue, helper variable as additional global variable
// As I used a calculated variable, to overcome the compiler errors, we need a helper variable
// to store the actual value.
var globalVariable2_Value : Int = 0
var globalVariable2_Helper : Int = 0
// this is the global "variable" we worked with
var globalVariable2 : Int {
// the setter
set (newValue) {
globalDataQueue.async(flags: .barrier) {
globalVariable2_Value = newValue
}
}
// the getter
get {
globalDataQueue.sync{
globalVariable2_Helper = globalVariable2_Value
}
return globalVariable2_Helper
}
}
// ------------------------------------------------------------------------------------------------
// Alternative 3: with concurrent queue, no helper variable as Itai Ferber suggested
// "compact" design
var globalVariable3_Value : Int = 0
var globalVariable3 : Int {
set (newValue) {
globalDataQueue.async(flags: .barrier) { globalVariable3_Value = newValue }
}
get {
return globalDataQueue.sync { globalVariable3_Value }
}
}
// ------------------------------------------------------------------------------------------------
// -- Testing
// variable for read test
var testVar = 0
let numberOfInterations = 2
// Test 0
print ("\nStart test0: simple global variable, not thread safe")
let startTime = CFAbsoluteTimeGetCurrent()
for _ in 0 ..< numberOfInterations {
testVar = globalVariable
globalVariable += 1
}
let endTime = CFAbsoluteTimeGetCurrent()
let timeDiff = endTime - startTime
print("globalVariable == \(globalVariable), test0 time needed \(timeDiff) seconds")
// Test 1
testVar = 0
print ("\nStart test1: concurrent queue, helper variable inside getter")
let startTime1 = CFAbsoluteTimeGetCurrent()
for _ in 0 ..< numberOfInterations {
testVar = globalVariable1
globalVariable1 += 1
}
let endTime1 = CFAbsoluteTimeGetCurrent()
let timeDiff1 = endTime1 - startTime1
print("globalVariable == \(globalVariable1), test1 time needed \(timeDiff1) seconds")
// Test 2
testVar = 0
print ("\nStart test2: with concurrent queue, helper variable as an additional global variable")
let startTime2 = CFAbsoluteTimeGetCurrent()
for _ in 0 ..< numberOfInterations {
testVar = globalVariable2
globalVariable2 += 1
}
let endTime2 = CFAbsoluteTimeGetCurrent()
let timeDiff2 = endTime2 - startTime2
print("globalVariable == \(globalVariable2), test2 time needed \(timeDiff2) seconds")
// Test 3
testVar = 0
print ("\nStart test3: with concurrent queue, no helper variable as Itai Ferber suggested")
let startTime3 = CFAbsoluteTimeGetCurrent()
for _ in 0 ..< numberOfInterations {
testVar = globalVariable3
globalVariable3 += 1
}
let endTime3 = CFAbsoluteTimeGetCurrent()
let timeDiff3 = endTime3 - startTime3
print("globalVariable == \(globalVariable3), test3 time needed \(timeDiff3) seconds")