web-dev-qa-db-ja.com

ベンチマークの方法Swift=コード実行?

次以外に、Swiftで記述されたコードブロックを実行するために必要な正確な時間を与える方法/ソフトウェアはありますか?

let date_start = NSDate()

// Code to be executed 

println("\(-date_start.timeIntervalSinceNow)")
58
ielyamani

コードのブロックにスタンドアロンのタイミング関数が必要な場合は、次のSwiftヘルパー関数を使用します。

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

func timeElapsedInSecondsWhenRunningCode(operation: ()->()) -> Double {
    let startTime = CFAbsoluteTimeGetCurrent()
    operation()
    let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
    return Double(timeElapsed)
}

前者はコードの特定のセクションに必要な時間をログアウトし、後者はそれをフロートとして返します。最初のバリアントの例として:

printTimeElapsedWhenRunningCode(title:"map()") {
    let resultArray1 = randoms.map { pow(sin(CGFloat($0)), 10.0) }
}

次のようなものをログアウトします。

Map()の経過時間:0.0617449879646301 s

Swiftベンチマークは、選択した最適化レベルに応じて大きく変化します であるため、これはSwift実行時間の相対的な比較にのみ役立つ場合があります。ベータ版ごとに変更します。

105
Brad Larson

特定のコードブロックのパフォーマンスに関する洞察を得て、編集時にパフォーマンスが損なわれないことを確認したい場合は、 XCTest'smeasure(_ block: () -> Void)

ベンチマークしたいメソッドを実行する単体テストを作成します。単体テストはそれを複数回実行し、必要な時間と結果の偏差を与えます

func testExample() {

    self.measure {
        //do something you want to measure
    }
}

詳しくは、Apple docs Xcodeでのテスト->パフォーマンステスト の下のドキュメントを参照してください。

33
kviksilver

この関数を使用して、非同期および同期コードを測定できます。

import Foundation

func measure(_ title: String, block: (@escaping () -> ()) -> ()) {

    let startTime = CFAbsoluteTimeGetCurrent()

    block {
        let timeElapsed = CFAbsoluteTimeGetCurrent() - startTime
        print("\(title):: Time: \(timeElapsed)")
    }
}

したがって、基本的には、関数をパラメーターとして受け入れるブロックを渡します。このブロックを使用して、いつ終了するかを測定に指示します。

たとえば、「myAsyncCall」と呼ばれる呼び出しにかかる時間を測定するには、次のように呼び出します。

measure("some title") { finish in
    myAsyncCall {
        finish()
    }
    // ...
}

同期コードの場合:

measure("some title") { finish in
     // code to benchmark
     finish()
     // ...
}

これはXCTestのmeasureBlockに似ているはずですが、どのように実装されているかはわかりません。

13
Ixx

ベンチマーク関数-Swift 4.2

これは、テストのラベリング、多くのテストの実行、実行時間の平均化、テスト間で呼び出されるセットアップブロック(つまり、ソートアルゴリズムの測定間で配列をシャッフル)、ベンチマークの明確な印刷を可能にする非常に汎用性の高いベンチマーク機能です。結果、平均実行時間もDoubleとして返します。

以下を試してください:

@_transparent @discardableResult public func measure(label: String? = nil, tests: Int = 1, printResults output: Bool = true, setup: @escaping () -> Void = { return }, _ block: @escaping () -> Void) -> Double {

    guard tests > 0 else { fatalError("Number of tests must be greater than 0") }

    var avgExecutionTime : CFAbsoluteTime = 0
    for _ in 1...tests {
        setup()
        let start = CFAbsoluteTimeGetCurrent()
        block()
        let end = CFAbsoluteTimeGetCurrent()
        avgExecutionTime += end - start
    }

    avgExecutionTime /= CFAbsoluteTime(tests)

    if output {
        let avgTimeStr = "\(avgExecutionTime)".replacingOccurrences(of: "e|E", with: " × 10^", options: .regularExpression, range: nil)

        if let label = label {
            print(label, "▿")
            print("\tExecution time: \(avgTimeStr)s")
            print("\tNumber of tests: \(tests)\n")
        } else {
            print("Execution time: \(avgTimeStr)s")
            print("Number of tests: \(tests)\n")
        }
    }

    return avgExecutionTime
}

使用法

var arr = Array(1...1000).shuffled()

measure(label: "Map to String") {
    let _ = arr.map { String($0) }
}

measure(label: "Apple Shuffle", tests: 1000, setup: { arr.shuffle() }) {
    arr.sort()
}

measure {
    let _ = Int.random(in: 1...10000)
}

let mathExecutionTime = measure(printResults: false) {
    let _ = 219 * 354
}

print("Math Execution Time: \(mathExecutionTime * 1000)ms")


// Prints:
//
// Map to String ▿
//     Execution time: 0.021643996238708496s
//     Number of tests: 1
//
// Apple's Sorting Method ▿
//     Execution time: 0.0010601345300674438s
//     Number of tests: 1000
//
// Execution time: 6.198883056640625 × 10^-05s
// Number of tests: 1
//
// Math Execution Time: 0.016927719116210938ms
//

注:measureは実行時間も返します。 labeltests、およびsetup引数はオプションです。 printResults引数は、デフォルトでtrueに設定されます。

5
Noah Wilder

プレイグラウンドでも実行できる簡単なテストに対するBrad Larsonの答えが気に入っています。私自身のニーズに合わせて、少し調整しました。

  1. テストする関数の呼び出しをテスト関数でラップしました。これにより、必要に応じてさまざまな引数をいじることができます。
  2. テスト関数はその名前を返すので、averageTimeTo()ベンチマーク関数に 'title'パラメーターを含める必要はありません。
  3. ベンチマーク機能を使用すると、オプションで実行する反復回数を指定できます(デフォルトは10)。次に、合計時間と平均時間の両方を報告します。
  4. ベンチマーク関数はコンソールに出力し、averageTime( 'averageTimeTo'と呼ばれる関数から期待されるかもしれません)を返すので、ほとんど同じ機能を持つ2つの別個の関数は必要ありません。

例えば:

func myFunction(args: Int...) {
    // Do something
}

func testMyFunction() -> String {
    // Wrap the call to myFunction here, and optionally test with different arguments
    myFunction(args: 1, 2, 3)
    return #function
}

// Measure average time to complete test
func averageTimeTo(_ testFunction: () -> String, repeated reps: UInt = 10) -> Double {
    let functionName = testFunction()
    var totalTime = 0.0
    for _ in 0..<reps {
        let startTime = CFAbsoluteTimeGetCurrent()
        testFunction()
        let elapsedTime = CFAbsoluteTimeGetCurrent() - startTime
        totalTime += elapsedTime
    }
    let averageTime = totalTime / Double(reps)
    print("Total time to \(functionName) \(reps) times: \(totalTime) seconds")
    print("Average time to \(functionName): \(averageTime) seconds\n")
    return averageTime
}

averageTimeTo(testMyFunction)

// Total time to testMyFunction() 10 times: 0.000253915786743164 seconds
// Average time to testMyFunction(): 2.53915786743164e-05 seconds

averageTimeTo(testMyFunction, repeated: 1000)

// Total time to testMyFunction() 1000 times: 0.027538537979126 seconds
// Average time to testMyFunction(): 2.7538537979126e-05 seconds
3
Kal