Производительность: Array.removeAll vs `= []` - PullRequest
0 голосов
/ 10 января 2019

Есть ли разница в производительности в вычислительном отношении и / или в памяти между простыми функциями Swift Array / Dictionary removeAll и init? По сути, я спрашиваю, каковы плюсы и минусы сброса изменяемой коллекции в Swift, и считается ли это рекомендуемым способом очистки Array / Dictionary?

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

// Scenario B
myArray = ["one", "two", "three"]
myArray = []

Ответы [ 2 ]

0 голосов
/ 10 января 2019

Как указано в документации removeAll()

- Сложность: O (n), где n - длина массива.

Таким образом, скорость будет зависеть от размера массива. Как уже упоминали другие, было бы преждевременной оптимизацией выбирать одно поверх другого за счет читабельности.

0 голосов
/ 10 января 2019

Разница в производительности не должна быть значительной, поскольку они делают в основном одно и то же. Давайте посмотрим на исходный код :

/// 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())
  }
}

Есть несколько других реализаций этого метода для разных типов, но шаблон всегда один и тот же. Если removeAll параметр keepCapacity равен false, просто повторно инициализируйте его, в значительной степени эквивалентно высказыванию 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)
    }

}

Со следующими результатами:

Test Case '-[MyAppTests.MyAppTests testPerformanceReinitialize]' started.
/.../MyApp/MyAppTests/MyAppTests.swift:41: Test Case '-[MyAppTests.MyAppTests testPerformanceReinitialize]' measured [Time, seconds] average: 0.221, relative standard deviation: 6.559%, values: [0.264467, 0.216076, 0.216146, 0.216040, 0.216014, 0.216426, 0.216374, 0.215876, 0.216272, 0.216152], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[MyAppTests.MyAppTests testPerformanceReinitialize]' passed (2.646 seconds).

Test Case '-[MyAppTests.MyAppTests testPerformanceRemoveAll]' started.
/.../MyApp/MyAppTests/MyAppTests.swift:26: Test Case '-[MyAppTests.MyAppTests testPerformanceRemoveAll]' measured [Time, seconds] average: 0.235, relative standard deviation: 6.712%, values: [0.282223, 0.229732, 0.229601, 0.229624, 0.229584, 0.229652, 0.229695, 0.229729, 0.229702, 0.229659], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
Test Case '-[MyAppTests.MyAppTests testPerformanceRemoveAll]' passed (2.602 seconds).

enter image description here

Кстати, если вам интересно, почему я добавляю итоги после очистки массива, я просто пытаюсь убедиться, что я фактически использую массив после его очистки, чтобы оптимизатор не оптимизировать код, выполняя очистку. В этом нет необходимости, но разумно.

Я также провел тест с Int вместо String, так как мне не интересно, какие служебные данные String, а скорее я хочу сосредоточиться на поведении Array.

Итог, разница в производительности была в значительной степени неразличимой.

...