性能:Array.removeAll vs `= []`

Noo*_*ath 7 memory collections performance swift

Swift Array/Dictionary原始函数removeAllinit? 基本上,我在问,在 Swift 中重置可变集合的优缺点是什么,是否被认为是清空Array/的推荐方法Dictionary

// Scenario A
var myArray = ["one", "two", "three"]
myArray.removeAll()

// Scenario B
myArray = ["one", "two", "three"]
myArray = []
Run Code Online (Sandbox Code Playgroud)

Rob*_*Rob 11

性能差异应该不大,因为它们在很大程度上做相同的事情。让我们看一下源代码

/// Removes all elements from the array.
///
/// - Parameter keepCapacity: Pass `true` to keep the existing capacity of
///   the array after removing its elements. The default value is
///   `false`.
///
/// - Complexity: O(*n*), where *n* is the length of the array.
@inlinable
public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) {
  if !keepCapacity {
    _buffer = _Buffer()
  }
  else {
    self.replaceSubrange(indices, with: EmptyCollection())
  }
}
Run Code Online (Sandbox Code Playgroud)

对于不同的类型,此方法还有其他几种实现,但模式始终相同。如果removeAll参数keepCapacityfalse,只需重新初始化它,大致相当于说myArray = []

因此,唯一的问题是您是否要在删除其元素后保留数组的容量(如果您正在清空一个大数组并准备用另一个相同大小的数组重新填充,您可能会这样做)。


如果您愿意,请对其进行基准测试。例如,将“单元测试”目标添加到您的项目中,将迭代次数提高到足够高以使持续时间可观察:

class MyAppTests: XCTestCase {

    func testPerformanceRemoveAll() {
        var countTotal = 0
        var myArray: [Int] = []

        self.measure {
            for _ in 0 ..< 1_000_000 {
                myArray = Array(repeating: 0, count: 1_000)
                myArray.removeAll(keepingCapacity: false)
                countTotal += myArray.count
            }
        }

        XCTAssertEqual(countTotal, 0)
    }

    func testPerformanceReinitialize() {
        var countTotal = 0
        var myArray: [Int] = []

        self.measure {
            for _ in 0 ..< 1_000_000 {
                myArray = Array(repeating: 0, count: 1_000)
                myArray = []
                countTotal += myArray.count
            }
        }

        XCTAssertEqual(countTotal, 0)
    }

}
Run Code Online (Sandbox Code Playgroud)

结果如下:

/// Removes all elements from the array.
///
/// - Parameter keepCapacity: Pass `true` to keep the existing capacity of
///   the array after removing its elements. The default value is
///   `false`.
///
/// - Complexity: O(*n*), where *n* is the length of the array.
@inlinable
public mutating func removeAll(keepingCapacity keepCapacity: Bool = false) {
  if !keepCapacity {
    _buffer = _Buffer()
  }
  else {
    self.replaceSubrange(indices, with: EmptyCollection())
  }
}
Run Code Online (Sandbox Code Playgroud)

在此处输入图片说明

顺便说一句,如果您想知道为什么我在清空数组后添加总数,我只是想确保在清空数组后实际使用数组以确保优化器不会优化代码做清空。在这种情况下没有必要,但要谨慎。

我还用Int而不是 进行了测试String,因为我对String开销并不好奇,而是试图专注于Array行为。

最重要的是,性能差异在很大程度上无法区分。