Swift,计算排序算法的执行时间

时间:2019-01-15 00:59:34

标签: swift algorithm performance-testing

我需要计算2种不同算法的执行时间,然后根据执行时间确定推荐的算法。因此,对于一般的算法和数据结构,我还是很陌生,但是在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 :(得分:4)

注意:在现实生活中,最好找到一些已建立的性能测试框架。正确地进行测试很困难。

如果您自己进行测试,则最好不要做以下事情:

  1. 平均多次迭代,而不是一次。有很多原因导致结果有点嘈杂。如果总运行时间少于0.1秒,则很可能完全不可靠。最好是至少1秒。不仅要跟踪平均值,还要跟踪其他指标(如95%百分位数)也很有意义。

  2. 不要忽略循环内经过测试的计算结果。一个聪明的编译器可以优化未使用的结果,并用no-op之类的东西代替它。理想情况下,结果对于编译器应该不可预测。例如:在第i次迭代中,将已排序列表的第i个元素(或(i % array.length)个元素)添加到总和中,最后返回总和或打印它(显然不在所计算的时间范围内)

  3. 除非要尝试测量IO操作的性能,否则请不要在经过测试的算法内进行任何打印/记录/ IO。 IO非常慢。

  4. 在主要的“测试”迭代之前进行一些热身迭代。这是为了确保可以在各种CPU缓存中存储所有数据。如果没有预热,则第一次运行和随后的运行可能会非常不同。同样,如果您正在运行托管代码(如JavaScript或Java或.Net),则许多运行可能会迫使VM重新进行代码编译,并进行一些更好的优化。在这种情况下,您可能需要先运行数千次“热身”迭代来强制执行。一些更好的测试框架会批量运行,直到不同批次之间的时间稳定为止。

  5. 将代码的优化级别与生产中使用的代码进行比较。如果允许的话,当今的编译器可以非常聪明地进行优化,并且“调试”构建的速度很容易比“发行”构建的速度慢10倍。

对于排序算法,需要记住一些特定的东西,主要是:在不同的测试阵列上进行多次测量

  1. 尝试从数个元素到数百万个元素的不同数组大小。今天的内存访问是一件非常复杂的事情。不同的算法具有不同的内存使用模式,这可能会在不同大小下极大地影响性能

  2. 检查其他数据。某些排序算法在病理上是坏情况,有些则没有。有些可能对半分类数据特别有效,而有些则无法利用它。至少使用一些随机数据。最好不仅使用随机数。

答案 1 :(得分:1)

如果您想对性能进行基准测试,则使用单元测试的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())
}