Swift 4:字符串引用计数以及如何计算

时间:2021-08-28 16:54:01

This performance optimization WWDC video suggests that strings are reference counted because they are on the heap. This has implications on the performance of structs with Strings and whether something has changed in Swift 4 (now that Strings are collections again - with copy on write). Curious how to prove this out and get an actual count. CFGetRetainCount - doesn't work on Strings.

此性能优化WWDC视频表明字符串是引用计数的,因为它们在堆上。这对使用Strings的结构的性能以及Swift 4中的某些内容是否已经改变有影响(现在Strings又是集合 - 带有写入时的副本)。好奇如何证明这一点并获得实际数量。 CFGetRetainCount - 不适用于字符串。

See https://developer.apple.com/videos/play/wwdc2016/416/

Swift 4:字符串引用计数以及如何计算

Using Swift 4.

使用Swift 4。

1 个解决方案

#1


3  

Swift Strings are value types which doesn't have reference counting. But the characters that string contains are kept in heap inside a reference type container storage and that has reference count.

Swift字符串是没有引用计数的值类型。但是字符串包含的字符保存在引用类型容器存储中的堆中,并且具有引用计数。

This is why Swift Strings has copy on write optimization -like other collections-

这就是Swift Strings复制写入优化的原因 - 就像其他集合一样 -

Using Strings -and also any other reference type- inside Structs is not a good idea for performance, because on each assignment of Struct itself, all other reference types and String storage is retained.

在Structs中使用字符串 - 以及任何其他引用类型 - 对于性能来说并不是一个好主意,因为在每个Struct本身的赋值中,所有其他引用类型和字符串存储都会被保留。

When you have a value type that contains N reference type, on each assignment/deinit you need N retain/release. And you will have copy overhead for value types.

当您具有包含N引用类型的值类型时,在每个赋值/ deinit上您需要N retain / release。并且您将获得值类型的复制开销。

But if you define a reference type that contains N reference types, on each assignment/deinit you will have just 1 retain/release operation.

但是,如果您定义包含N个引用类型的引用类型,则在每个赋值/ deinit上只有1个保留/释放操作。

For exp:

struct Label {
    var text: String
    var font: UIFont
    func draw() { }
}

let label1 = Label(text: "Hi", font: font)
let label2 = label1
retain(label2.text._storage)
retain(label2.font)
// finished using label1
release(label1.text._storage)
release(label1.font)
// finished using label2
release(label2.text._storage)
release(label2.font)

If the Label was implemented as a class it would be

如果Label被实现为类,那就是

class Label {
        var text: String
        var font: UIFont
        func draw() { }
}

let label1 = Label(text: "Hi", font: font)
let label2 = label1
retain(label2)
// finished using label1
release(label1)
// finished using label2
release(label2)

On the other hand, this approach conflicts with Struct Philosophy's thread safety offer. The same instances will be shared among all copies.

另一方面,这种方法与Struct Philosophy的线程安全提议相冲突。所有副本之间将共享相同的实例。

Since retain/release operations are on the heap and they have to be thread-safe, there is considerable cost for this operations.

由于保留/释放操作在堆上并且它们必须是线程安全的,因此这种操作需要相当大的成本。

So if you really need a great performance, including micro optimizations, and you want to use value types wisely, you should consider this approach.

因此,如果您真的需要一个出色的性能,包括微优化,并且您希望明智地使用值类型,那么您应该考虑这种方法。

PS: This approach is not changed by Swift 4, copy on write is another optimization. It creates a copy, only when value types that contains reference types with multiple references, are mutated.

PS:Swift 4没有改变这种方法,写入时的复制是另一种优化。只有当包含具有多个引用的引用类型的值类型发生变异时,它才会创建副本。

#1


3  

Swift Strings are value types which doesn't have reference counting. But the characters that string contains are kept in heap inside a reference type container storage and that has reference count.

Swift字符串是没有引用计数的值类型。但是字符串包含的字符保存在引用类型容器存储中的堆中,并且具有引用计数。

This is why Swift Strings has copy on write optimization -like other collections-

这就是Swift Strings复制写入优化的原因 - 就像其他集合一样 -

Using Strings -and also any other reference type- inside Structs is not a good idea for performance, because on each assignment of Struct itself, all other reference types and String storage is retained.

在Structs中使用字符串 - 以及任何其他引用类型 - 对于性能来说并不是一个好主意,因为在每个Struct本身的赋值中,所有其他引用类型和字符串存储都会被保留。

When you have a value type that contains N reference type, on each assignment/deinit you need N retain/release. And you will have copy overhead for value types.

当您具有包含N引用类型的值类型时,在每个赋值/ deinit上您需要N retain / release。并且您将获得值类型的复制开销。

But if you define a reference type that contains N reference types, on each assignment/deinit you will have just 1 retain/release operation.

但是,如果您定义包含N个引用类型的引用类型,则在每个赋值/ deinit上只有1个保留/释放操作。

For exp:

struct Label {
    var text: String
    var font: UIFont
    func draw() { }
}

let label1 = Label(text: "Hi", font: font)
let label2 = label1
retain(label2.text._storage)
retain(label2.font)
// finished using label1
release(label1.text._storage)
release(label1.font)
// finished using label2
release(label2.text._storage)
release(label2.font)

If the Label was implemented as a class it would be

如果Label被实现为类,那就是

class Label {
        var text: String
        var font: UIFont
        func draw() { }
}

let label1 = Label(text: "Hi", font: font)
let label2 = label1
retain(label2)
// finished using label1
release(label1)
// finished using label2
release(label2)

On the other hand, this approach conflicts with Struct Philosophy's thread safety offer. The same instances will be shared among all copies.

另一方面,这种方法与Struct Philosophy的线程安全提议相冲突。所有副本之间将共享相同的实例。

Since retain/release operations are on the heap and they have to be thread-safe, there is considerable cost for this operations.

由于保留/释放操作在堆上并且它们必须是线程安全的,因此这种操作需要相当大的成本。

So if you really need a great performance, including micro optimizations, and you want to use value types wisely, you should consider this approach.

因此,如果您真的需要一个出色的性能,包括微优化,并且您希望明智地使用值类型,那么您应该考虑这种方法。

PS: This approach is not changed by Swift 4, copy on write is another optimization. It creates a copy, only when value types that contains reference types with multiple references, are mutated.

PS:Swift 4没有改变这种方法,写入时的复制是另一种优化。只有当包含具有多个引用的引用类型的值类型发生变异时,它才会创建副本。