说
struct Teste: Codable {
let a: Int
lazy var x: Int = {
print("I just changed this Teste item")
return a + 1000
}()
}
你呢
var hugeData: [Teste] // 750 million of these
所以它有点像来自云端的数据,保存在单例或类似物中。
那么你碰巧
let see: [Teste] = hugeData.filter{ .. }
// 4 of these. we'll say one of them happens to be hugeData[109333072]
也许在视图或类似的东西中显示。下一步!虽然see
存在,但你碰巧出于某种原因
let width = hugeData[109333072].x
那一刻,它必须复制-写入所有内容hugeData
,只是因为see
那时存在一点点愚蠢的内容。
- 或者可能是 Swift 的写时复制机制足够智能,在这种情况下,可以真正在微小的块上执行写时复制?有人知道是否是这种情况吗?
我刚刚才意识到,如果处理大量数据的话,如果原始大数组中发生了一些变化,而恰好存在一些微不足道的写时复制数组,那么这将带来巨大的性能损失。
(并且/或者,正如要点中提到的那样,也许 Swift 通过对小对象进行实际的复制来解决这个问题??)
简而言之,如果对数组进行了更改而恰好存在副本,那么将写时复制应用于(所有)“原始”数组是否正确?
这是一个简短的、不言自明的演示...
struct Teste: Codable {
let a: Int
lazy var x: Int = {
print("I just calculated it", a + 1000)
return a + 1000
}()
}
print("yo")
tt = [Teste(a:7), Teste(a:8), Teste(a:9)]
let tt2 = tt.filter{ $0.a > 8 }
print(tt)
print(tt2)
print("yes, still nil in tt2")
print("go..", tt[2].x)
print("go again..", tt[2].x)
print(tt)
print(tt2)
print("yes, copy on write was? applied to the original huge array")
print("ie YES, still nil in tt2")
let tt3 = tt.filter{ $0.a > 8 }
print(tt)
print(tt3)
print("yes, the set value seems to carry through to tt3")
结果
yo
[.Teste(a: 7, $__lazy_storage_$_x: nil), .Teste(a: 8, $__lazy_storage_$_x: nil), .Teste(a: 9, $__lazy_storage_$_x: nil)]
[.Teste(a: 9, $__lazy_storage_$_x: nil)]
yes, still nil in tt2
I just calculated it 1009
go.. 1009
go again.. 1009
[.Teste(a: 7, $__lazy_storage_$_x: nil), .Teste(a: 8, $__lazy_storage_$_x: nil), .Teste(a: 9, $__lazy_storage_$_x: Optional(1009))]
[.Teste(a: 9, $__lazy_storage_$_x: nil)]
yes, copy on write was? applied to the original huge array
ie YES, still nil in tt2
[.Teste(a: 7, $__lazy_storage_$_x: nil), .Teste(a: 8, $__lazy_storage_$_x: nil), .Teste(a: 9, $__lazy_storage_$_x: Optional(1009))]
[.Teste(a: 9, $__lazy_storage_$_x: Optional(1009))]
yes, the set value seems to carry through to tt3
那么在这种情况下它会复制 7.5 亿个项目数组吗?
数组 CoW 是一种机制,当您修改一个数组并且该数组与另一个数组共享其存储时,该存储的内容将被复制,并且这两个数组将不再共享它们的存储,因为它们各自都有自己的副本。
您描述的情况不涉及共享存储的数组。
filter
返回一个包含已筛选元素的新数组。这个新数组不可能与旧数组共享相同的存储,因为它们具有不同的元素。在这种情况下,see
将具有包含 4 个元素的存储空间。因此,CoW 语义在这里实际上无关紧要。没有共享存储的数组。您只有两个数组,每个数组都有自己独立的存储。
因此
只会改变
hugeData
而不会造成任何复制关于“共享存储”含义的澄清:
结构体包含对堆中
Array
某个类型的引用。这是数组元素的存储位置。当您复制结构体值(如 )时,此引用也会被复制。即。这就是我所说的“共享相同存储”的意思。class
Array
var y = x
x._storage === y._storage
当你
y
以某种方式修改时,y._storage
将会被分配给某个其他类实例(也就是存储中的元素的一个副本),而 不再是 的情况x._storage === y._storage
。