Swift, вычисли время выполнения алгоритма сортировки - PullRequest
0 голосов
/ 15 января 2019

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

Так что я немного искал и почти ничего не нашел. Мне удалось найти это:

func printTimeElapsedWhenRunningCode(title:String, operation:()->()) {
    let startTime = CFAbsoluteTimeGetCurrent()
    operation()
    let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
    print("Time elapsed for \(title): \(timeElapsed) s.")
}

Это то, что я на самом деле пытаюсь проверить:

printTimeElapsedWhenRunningCode(title: "Merge Sort") {
    let newSort = mergeSort(sortArray)
}

Теперь я не уверен, действительно ли это вычисляет то, что мне нужно. Тогда каждый раз, когда я запускаю это, я всегда получаю другое время. Я даже на правильном пути?

Ответы [ 2 ]

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

Если вы хотите протестировать производительность, используйте блок модульных тестов. measure { ... } - хорошая отправная точка, так как он запускает его несколько раз и рассчитывает прошедшее время, стандартное отклонение и т. Д.

Я бы также предложил:

  • тестирование с типом, где сортировка является относительно эффективной (например, [Int], а не [String]), так что вы сосредотачиваетесь на скорости сортировки, а не на скорости сравнения);
  • выполнить сортировку, которая имеет наблюдаемое время (например, большой массив) и повторять сортировку много раз; и
  • Сделайте что-нибудь простое с конечным результатом, чтобы при тестировании оптимизированной сборки вы не рисковали, если бы она оптимизировала некоторый код, который генерирует результат, который вы не используете.

Но для быстрого тестирования производительности модульные тесты Xcode довольно просты. Например:

class MyAppTests: XCTestCase {

    let iterationCount = 1_000

    // build large array

    var array = (0 ..< 1_000).map { _ in Int.random(in: 0 ..< 1_000_000) }

    // test performance

    func testSortPerformance() {
        measure {
            for _ in 0 ..< iterationCount {
                let results = array.sorted()
                XCTAssert(!results.isEmpty)
            }
        }
    }

    func testBubbleSortPerformance() {
        measure {
            for _ in 0 ..< iterationCount {
                let results = array.bubbleSorted()
                XCTAssert(!results.isEmpty)
            }
        }
    }
}

Это приведет к следующим результатам в Навигаторе отчетов:

enter image description here

Или в консоли вы увидите подробности:

/.../MyAppTests.swift:33: Test Case '-[MyAppTests.MyAppTests testBubbleSortPerformance]' measured [Time, seconds] average: 0.603, relative standard deviation: 3.284%, values: [0.613748, 0.580443, 0.590879, 0.586842, 0.626791, 0.610288, 0.595295, 0.588713, 0.594823, 0.647156], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100
/.../MyAppTests.swift:23: Test Case '-[MyAppTests.MyAppTests testSortPerformance]' measured [Time, seconds] average: 0.025, relative standard deviation: 13.393%, values: [0.033849, 0.026869, 0.022752, 0.023048, 0.023024, 0.022847, 0.023286, 0.023987, 0.023803, 0.022640], performanceMetricID:com.apple.XCTPerformanceMetric_WallClockTime, baselineName: "", baselineAverage: , maxPercentRegression: 10.000%, maxPercentRelativeStandardDeviation: 10.000%, maxRegression: 0.100, maxStandardDeviation: 0.100

И, пока я на этом, я, наверное, тоже протестировал бы сами алгоритмы сортировки, например, убедившись, что результаты увеличивают значения и что сумма всех элементов все еще складывается:

func testBubbleSort() {
    let results = array.bubbleSorted()

    var previous = results[0]
    for index in 1 ..< results.count {
        let current = results[index]
        XCTAssertLessThanOrEqual(previous, current)
        previous = current
    }

    XCTAssertEqual(results.sum(), array.sum())
}
0 голосов
/ 15 января 2019

Примечание: в реальной жизни лучше найти какую-то устоявшуюся среду тестирования производительности. Правильно провести тестирование сложно.

Вот неполный список вещей, которые вам лучше сделать, если вы проводите собственное тестирование:

  1. Среднее по многим итерациям, а не по одной. Есть много причин, по которым результаты будут немного шумными. Если общее время выполнения составляет менее 0,1 секунды, скорее всего, оно абсолютно ненадежно. Лучше, если это хотя бы 1 секунда. Также имеет смысл отслеживать не только среднее, но и некоторые другие показатели, такие как 95% процентиль.

  2. Не игнорируйте результаты проверенных вычислений внутри цикла. Умный компилятор может оптимизировать неиспользуемые результаты и заменить его чем-то вроде no-op. В идеале результат должен быть непредсказуемым для компилятора. Например: на i -й итерации добавьте i -й (или (i % array.length) -й) элемент отсортированного списка к общей сумме и в конце верните сумму или напечатайте ее (очевидно, за пределами измеренного значения). время)

  3. Не выполняйте никаких операций печати / регистрации / ввода-вывода в тестируемом алгоритме, если только вы не пытаетесь измерить производительность этой операции ввода-вывода. IO очень медленный.

  4. Выполните несколько итераций разминки перед основными итерациями «теста». Это сделано для того, чтобы убедиться, что все данные, которые могут находиться в разных кэшах ЦП, есть. Без прогрева первые запуски и последние запуски могут сильно отличаться Кроме того, если вы запускаете управляемый код (например, JavaScript, Java или .Net), многие запуски могут заставить ВМ перекомпилировать код с некоторыми лучшими оптимизациями. В таком случае вам может понадобиться выполнить несколько тысяч «итераций» для начала. Некоторые более эффективные среды тестирования запускают пакеты до тех пор, пока время между различными пакетами не станет стабильным.

  5. Сравните код с тем же уровнем оптимизации, который будет использоваться в производстве. Современные компиляторы могут быть очень умны в оптимизации, если вы разрешите их, и «отладочные» сборки могут быть в 10 раз медленнее, чем «выпускные» сборки.

Для алгоритмов сортировки есть некоторые конкретные вещи, которые следует запомнить, и главное: выполнить несколько измерений на разных тестовых массивах

  1. Попробуйте разные размеры массива от нескольких до миллионов элементов. Сегодняшний доступ к памяти - очень сложная вещь. Разные алгоритмы имеют разные схемы использования памяти, которые могут сильно повлиять на производительность при разных размерах

  2. Проверьте разные данные. Некоторые алгоритмы сортировки имеют патологически плохие случаи, а некоторые - нет. Некоторые могут работать особенно быстро на полусортированных данных, а некоторые не могут их использовать. По крайней мере, используйте случайные данные. Хотя лучше использовать не только случайно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...