Đo thời gian trôi qua trong Swift


132

Làm cách nào chúng ta có thể đo thời gian trôi qua để chạy một chức năng trong Swift? Tôi đang cố gắng hiển thị thời gian đã trôi qua như thế này: "Thời gian đã trôi qua là 0,05 giây". Tôi thấy rằng trong Java , chúng ta có thể sử dụng System.nanoTime (), có phương thức tương đương nào có sẵn trong Swift để thực hiện việc này không?

Xin hãy xem chương trình mẫu:

func isPrime(var number:Int) ->Bool {
    var i = 0;
    for i=2; i<number; i++ {
        if(number % i == 0 && i != 0) {
            return false;
        }
    }
    return true;
}

var number = 5915587277;

if(isPrime(number)) {
    println("Prime number");
} else {
    println("NOT a prime number");
}

1
nó không liên quan đến vấn đề đo thời gian của bạn, nhưng vòng lặp có thể dừng lại sqrt(number)thay vào đó numbervà bạn có thể tiết kiệm thêm một chút thời gian - nhưng có nhiều ý tưởng tối ưu hóa việc tìm kiếm các số nguyên tố.
holex

@holex Hãy bỏ qua thuật toán được sử dụng. Tôi đang cố gắng tìm hiểu làm thế nào chúng ta có thể đo thời gian trôi qua?
Vaquita

1
bạn có thể sử dụng NSDatecác đối tượng và bạn có thể đo sự khác biệt giữa chúng.
holex

4
Nếu bạn đang sử dụng XCode, tôi khuyên bạn nên sử dụng tính năng kiểm tra hiệu suất mới. Nó thực hiện tất cả các công việc nặng nhọc cho bạn và thậm chí chạy nó nhiều lần và cho bạn thời gian trung bình với độ lệch chuẩn của nó ...
Roshan

1
@dumbledad Bạn có thể đo hiệu suất của toàn bộ khối trực tiếp trong các bài kiểm tra đơn vị. Ví dụ, xem điều này . Nếu bạn muốn chia nhỏ hơn nữa (giả sử từng dòng mã), hãy xem Time Profiler , một phần của Công cụ. Điều này mạnh mẽ và toàn diện hơn nhiều.
Roshan

Câu trả lời:


228

Đây là một hàm Swift tôi đã viết để đo các sự cố Project Euler trong Swift

Kể từ Swift 3, giờ đây đã có phiên bản Grand Central Dispatch được "swiftified". Vì vậy, câu trả lời chính xác có lẽ là sử dụng API của DispatchTime .

Chức năng của tôi sẽ trông giống như:

// Swift 3
func evaluateProblem(problemNumber: Int, problemBlock: () -> Int) -> Answer
{
    print("Evaluating problem \(problemNumber)")

    let start = DispatchTime.now() // <<<<<<<<<< Start time
    let myGuess = problemBlock()
    let end = DispatchTime.now()   // <<<<<<<<<<   end time

    let theAnswer = self.checkAnswer(answerNum: "\(problemNumber)", guess: myGuess)

    let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds // <<<<< Difference in nano seconds (UInt64)
    let timeInterval = Double(nanoTime) / 1_000_000_000 // Technically could overflow for long running tests

    print("Time to evaluate problem \(problemNumber): \(timeInterval) seconds")
    return theAnswer
}

Câu trả lời cũ

Đối với Swift 1 và 2, chức năng của tôi sử dụng NSDate:

// Swift 1
func evaluateProblem(problemNumber: Int, problemBlock: () -> Int) -> Answer
{
    println("Evaluating problem \(problemNumber)")

    let start = NSDate() // <<<<<<<<<< Start time
    let myGuess = problemBlock()
    let end = NSDate()   // <<<<<<<<<<   end time

    let theAnswer = self.checkAnswer(answerNum: "\(problemNumber)", guess: myGuess)

    let timeInterval: Double = end.timeIntervalSinceDate(start) // <<<<< Difference in seconds (double)

    println("Time to evaluate problem \(problemNumber): \(timeInterval) seconds")
    return theAnswer
}

Lưu ý rằng việc sử dụng NSdate cho các chức năng định thời không được khuyến khích: " Thời gian hệ thống có thể giảm do đồng bộ hóa với các tham chiếu thời gian bên ngoài hoặc do sự thay đổi rõ ràng của người dùng đồng hồ. ".


Điều này dường như cho kết quả tốt lên tới khoảng +/- 2microsec. Có lẽ điều đó thay đổi theo nền tảng, tuy nhiên.
dùng1021430

6
swift3 không làm việc cho tôi, nó sẽ trả 0,0114653027 mà tôi biết một thực tế đó không phải là sự thật, một với ngày trở lại 4.77720803022385sec
Cristi Băluţă

5
Không may măn. Thời gian hoạt động là hoàn toàn ra vì một số lý do. Tôi có một bài kiểm tra cho biết mất 1004959766 nano giây (khoảng một giây), nhưng chắc chắn đã chạy trong khoảng 40 giây. Tôi đã sử dụng Datecùng với, và chắc chắn rằng báo cáo 41,87 giây. Tôi không biết những gì uptimeNanosecondsđang làm, nhưng nó không báo cáo thời lượng chính xác.
jowie

2
Xin lỗi vì đã chỉnh sửa lớn, ưu tiên giải pháp mới hơn của bạn, nhưng tôi thậm chí sẽ khuyên bạn nên xóa hoàn toàn mã NSDate của mình: hoàn toàn không đáng tin khi sử dụng NSDate: NSDate dựa trên đồng hồ hệ thống, có thể thay đổi bất cứ lúc nào vì nhiều lý do khác nhau, chẳng hạn như như đồng bộ hóa thời gian mạng (NTP) cập nhật đồng hồ (thường xảy ra để điều chỉnh độ trôi), điều chỉnh DST, giây nhuận và điều chỉnh hướng dẫn sử dụng của đồng hồ. Ngoài ra, bạn không thể xuất bản nữa trên AppStore bất kỳ ứng dụng nào sử dụng Swift 1: Swift 3 là mức tối thiểu. Vì vậy, không có lý do gì để giữ mã NSDate của bạn ngoài việc nói "đừng làm điều đó".
Cœur

1
@ Nếu bạn chỉ trích phiên bản NSDate là hợp lệ (ngoại trừ các thay đổi DST sẽ không ảnh hưởng đến NSDate luôn có trong GMT) và chỉnh sửa của bạn để đảo ngược thứ tự chắc chắn là một sự cải thiện.
JeremyP

69

Đây là lớp hẹn giờ tiện dụng dựa trên CoreFoundations CFAbsoluteTime:

import CoreFoundation

class ParkBenchTimer {

    let startTime:CFAbsoluteTime
    var endTime:CFAbsoluteTime?

    init() {
        startTime = CFAbsoluteTimeGetCurrent()
    }

    func stop() -> CFAbsoluteTime {
        endTime = CFAbsoluteTimeGetCurrent()

        return duration!
    }

    var duration:CFAbsoluteTime? {
        if let endTime = endTime {
            return endTime - startTime
        } else {
            return nil
        }
    }
}

Bạn có thể sử dụng nó như thế này:

let timer = ParkBenchTimer()

// ... a long runnig task ...

println("The task took \(timer.stop()) seconds.")

4
" Các cuộc gọi lặp đi lặp lại chức năng này không đảm bảo kết quả tăng đơn điệu. " Theo tài liệu của nó .
Franklin Yu

8
Thêm ngữ cảnh: "Các cuộc gọi lặp lại cho chức năng này không đảm bảo kết quả tăng đơn điệu. Thời gian hệ thống có thể giảm do đồng bộ hóa với các tham chiếu thời gian bên ngoài hoặc do sự thay đổi rõ ràng của người dùng đồng hồ ."
Klaas

Nhà phát triển nên đưa "đồng bộ hóa với các tham chiếu thời gian bên ngoài" và "thay đổi người dùng rõ ràng của đồng hồ" vào tài khoản. Ý bạn là không thể xảy ra hai tình huống?
Franklin Yu

@FranklinYu không, tôi chỉ muốn nói đây là hai lý do có thể. Nếu bạn dựa vào việc luôn luôn nhận được các giá trị tăng đơn điệu, có những lựa chọn khác.
Klaas

đôi khi tôi muốn đo một khoảng thời gian ngắn; nếu đồng bộ hóa xảy ra trong thời gian đó, tôi có thể kết thúc với thời gian tiêu cực. Thực sự mach_absolute_timekhông có vấn đề gì với vấn đề này, vì vậy tôi tự hỏi tại sao nó không được biết đến rộng rãi. Có lẽ chỉ vì nó không được ghi chép tốt.
Franklin Yu

54

Sử dụng clock, ProcessInfo.systemUptimehoặc DispatchTimecho thời gian khởi động đơn giản.


Theo như tôi biết, có ít nhất mười cách để đo thời gian trôi qua:

Đồng hồ đơn điệu dựa trên:

  1. ProcessInfo.systemUptime.
  2. mach_absolute_timevới mach_timebase_infonhư đã đề cập trong câu trả lời này .
  3. clock()trong tiêu chuẩn POSIX .
  4. times()trong tiêu chuẩn POSIX . (Quá phức tạp vì chúng ta cần xem xét thời gian của người dùng so với thời gian hệ thống và các quy trình con có liên quan.)
  5. DispatchTime (một trình bao bọc xung quanh API thời gian Mach) như được JeremyP đề cập trong câu trả lời được chấp nhận.
  6. CACurrentMediaTime().

Đồng hồ treo tường dựa trên:

(không bao giờ sử dụng chúng cho số liệu: xem bên dưới tại sao)

  1. NSDate/ Datenhư được đề cập bởi những người khác.
  2. CFAbsoluteTime như đã đề cập bởi những người khác.
  3. DispatchWallTime.
  4. gettimeofday()trong tiêu chuẩn POSIX .

Tùy chọn 1, 2 và 3 được xây dựng dưới đây.

Tùy chọn 1: API thông tin quy trình trong Foundation

do {
    let info = ProcessInfo.processInfo
    let begin = info.systemUptime
    // do something
    let diff = (info.systemUptime - begin)
}

nơi diff:NSTimeIntervallà thời gian trôi qua bằng giây.

Tùy chọn 2: API Mach C

do {
    var info = mach_timebase_info(numer: 0, denom: 0)
    mach_timebase_info(&info)
    let begin = mach_absolute_time()
    // do something
    let diff = Double(mach_absolute_time() - begin) * Double(info.numer) / Double(info.denom)
}

nơi diff:Doublelà thời gian trôi qua bằng nano giây.

Tùy chọn 3: API đồng hồ POSIX

do {
    let begin = clock()
    // do something
    let diff = Double(clock() - begin) / Double(CLOCKS_PER_SEC)
}

nơi diff:Doublelà thời gian trôi qua bằng giây.

Tại sao không phải là đồng hồ treo tường cho thời gian đã trôi qua?

Trong tài liệu của CFAbsoluteTimeGetCurrent:

Các cuộc gọi liên tục đến chức năng này không đảm bảo kết quả tăng đơn điệu.

Lý do là tương tự như currentTimeMillisvs nanoTimetrong Java :

Bạn không thể sử dụng cái này cho mục đích khác. Lý do là không có đồng hồ máy tính nào là hoàn hảo; nó luôn luôn trôi và đôi khi cần phải được sửa chữa. Việc hiệu chỉnh này có thể xảy ra bằng tay hoặc trong trường hợp của hầu hết các máy, có một quá trình chạy và liên tục đưa ra các hiệu chỉnh nhỏ cho đồng hồ hệ thống ("đồng hồ treo tường"). Những xu hướng này thường xảy ra. Một điều chỉnh như vậy xảy ra bất cứ khi nào có một bước nhảy vọt thứ hai.

Ở đây CFAbsoluteTimecung cấp thời gian đồng hồ treo tường thay vì thời gian khởi động. NSDatelà thời gian đồng hồ treo tường là tốt.


câu trả lời tốt nhất - nhưng cái quái gì, là cách tiếp cận tốt nhất để thực hiện?!
Fattie

1
Lựa chọn 5 phù hợp nhất với tôi. Nếu bạn xem xét mã đa nền tảng hãy thử cái này. Cả thư viện Darwin và Glibc đều có clock()chức năng.
Misha Svyatoshenko

Đối với các giải pháp hệ thống đồng hồ dựa: Tôi đã thành công với ProcessInfo.processInfo.systemUptime, mach_absolute_time(), DispatchTime.now()CACurrentMediaTime(). Nhưng giải pháp với việc clock()không chuyển đổi thành giây chính xác. Vì vậy, tôi sẽ tư vấn để cập nhật câu đầu tiên của bạn đến: " Sử dụng ProcessInfo.systemUptime hoặc DispatchTime cho thời gian khởi động chính xác. "
Cœur

36

Swift 4 câu trả lời ngắn nhất:

let startingPoint = Date()
//  ... intensive task
print("\(startingPoint.timeIntervalSinceNow * -1) seconds elapsed")

Nó sẽ in cho bạn một cái gì đó như 1.02107906341553 giây trôi qua (thời gian tất nhiên sẽ thay đổi tùy theo nhiệm vụ, tôi chỉ hiển thị điều này cho các bạn để xem mức độ chính xác thập phân cho phép đo này).

Hy vọng nó sẽ giúp được ai đó trong Swift 4 kể từ bây giờ!

Cập nhật

Nếu bạn muốn có một cách chung để kiểm tra các phần mã, tôi sẽ đề xuất đoạn mã tiếp theo:

func measureTime(for closure: @autoclosure () -> Any) {
    let start = CFAbsoluteTimeGetCurrent()
    closure()
    let diff = CFAbsoluteTimeGetCurrent() - start
    print("Took \(diff) seconds")
}

*Usage*

measureTime(for: <insert method signature here>)

**Console log**
Took xx.xxxxx seconds

Ai đó có thể giải thích tại sao * -1?
Maksim Kniazev

1
@MaksimKniazev Vì đó là giá trị âm, mọi người muốn tích cực!
thachnb

@thachnb Tôi không có ý tưởng "startedPoint.timeIntervalSinceNow" tạo ra giá trị âm ...
Maksim Kniazev

1
@MaksimKniazev Apple nói rằng: Nếu đối tượng ngày sớm hơn ngày và giờ hiện tại, giá trị của tài sản này là âm.
Peter Guan

13
let start = NSDate()

for index in 1...10000 {
    // do nothing
}

let elapsed = start.timeIntervalSinceNow

// elapsed is a negative value.

2
abs (start.timeIntervalSinceNow) // <- giá trị dương
eonist

khá nhiều giải pháp đơn giản nhất mà tôi từng thấy, cảm ơn bạn
MiladiuM

10

Chỉ cần Sao chép và Dán chức năng này. Viết bằng swift 5. Sao chép JeremyP tại đây.

func calculateTime(block : (() -> Void)) {
        let start = DispatchTime.now()
        block()
        let end = DispatchTime.now()
        let nanoTime = end.uptimeNanoseconds - start.uptimeNanoseconds
        let timeInterval = Double(nanoTime) / 1_000_000_000
        print("Time: \(timeInterval) seconds")
    }

Sử dụng nó như

calculateTime {
     exampleFunc()// function whose execution time to be calculated
}

6

Bạn có thể tạo một timechức năng để đo các cuộc gọi của bạn. Tôi lấy cảm hứng từ câu trả lời của Klaas .

func time <A> (f: @autoclosure () -> A) -> (result:A, duration: String) {
    let startTime = CFAbsoluteTimeGetCurrent()
    let result = f()
    let endTime = CFAbsoluteTimeGetCurrent()
    return (result, "Elapsed time is \(endTime - startTime) seconds.")
}

Hàm này sẽ cho phép bạn gọi nó như thế này time (isPrime(7))sẽ trả về một tuple chứa kết quả và mô tả chuỗi về thời gian đã trôi qua.

Nếu bạn chỉ muốn thời gian trôi qua, bạn có thể làm điều này time (isPrime(7)).duration


3
"Các cuộc gọi lặp lại chức năng này không đảm bảo kết quả tăng đơn điệu." theo tài liệu .
Franklin Yu

3

Chức năng trợ giúp đơn giản để đo thời gian thực hiện với đóng cửa.

func printExecutionTime(withTag tag: String, of closure: () -> ()) {
    let start = CACurrentMediaTime()
    closure()
    print("#\(tag) - execution took \(CACurrentMediaTime() - start) seconds")
}

Sử dụng:

printExecutionTime(withTag: "Init") {
    // Do your work here
}

Kết quả: #Init - execution took 1.00104497105349 seconds


2

bạn có thể đo các nano giây như ví dụ:

let startDate: NSDate = NSDate()

// your long procedure

let endDate: NSDate = NSDate()
let dateComponents: NSDateComponents = NSCalendar(calendarIdentifier: NSCalendarIdentifierGregorian).components(NSCalendarUnit.CalendarUnitNanosecond, fromDate: startDate, toDate: endDate, options: NSCalendarOptions(0))
println("runtime is nanosecs : \(dateComponents.nanosecond)")

Tôi chỉ chạy cái này mà không có mã nào được đặt trong "// thủ tục dài của bạn" và nó cho kết quả là 240900993, tức là 0,24 giây.
dùng1021430

khởi tạo một NSCalendarthủ tục là tốn kém, nhưng bạn có thể thực hiện trước khi bắt đầu chạy thủ tục, vì vậy nó sẽ không được thêm vào thời gian chạy thủ tục dài của bạn ... hãy nhớ rằng việc tạo một NSDatethể hiện và –components(_:, fromDate:)phương thức gọi vẫn mất thời gian, vì vậy có lẽ bạn sẽ không bao giờ có được 0.0nano giây.
holex

Từ vẻ ngoài của hàm tạo NSCalWiki, có vẻ như không thể tạo ra nó trước thời hạn (startDate là một đầu vào bắt buộc). Thật đáng ngạc nhiên khi nó có vẻ là một con heo như vậy.
dùng1021430

bạn có thể làm điều này (câu trả lời được cập nhật của tôi), thời gian đo mà không cần bất kỳ thủ tục bổ sung nào là khoảng 6.9micro giây trên thiết bị của tôi.
holex

Tuyệt thật, cảm ơn nhé. Quy trình cơ bản bây giờ tương tự như câu trả lời của JeremyP, nhưng báo cáo thì khác.
dùng1021430

2

Tôi sử dụng cái này:

public class Stopwatch {
    public init() { }
    private var start_: NSTimeInterval = 0.0;
    private var end_: NSTimeInterval = 0.0;

    public func start() {
        start_ = NSDate().timeIntervalSince1970;
    }

    public func stop() {
        end_ = NSDate().timeIntervalSince1970;
    }

    public func durationSeconds() -> NSTimeInterval {
        return end_ - start_;
    }
}

Tôi không biết nếu nó chính xác hơn hoặc ít hơn so với đăng trước đó. Nhưng các giây có rất nhiều số thập phân và dường như bắt được các thay đổi mã nhỏ trong các thuật toán như QuickSort bằng cách sử dụng hoán đổi () so với thực hiện hoán đổi v.v.

Hãy nhớ để tối ưu hóa bản dựng của bạn khi kiểm tra hiệu suất:

Tối ưu hóa trình biên dịch Swift


2

Đây là thử của tôi cho câu trả lời đơn giản nhất:

let startTime = Date().timeIntervalSince1970  // 1512538946.5705 seconds

// time passes (about 10 seconds)

let endTime = Date().timeIntervalSince1970    // 1512538956.57195 seconds
let elapsedTime = endTime - startTime         // 10.0014500617981 seconds

Ghi chú

  • startTimeendTimelà loại TimeInterval, chỉ là một typealiascho Double, vì vậy thật dễ dàng để chuyển đổi nó thành một Inthoặc bất cứ điều gì. Thời gian được đo bằng giây với độ chính xác dưới một phần nghìn giây.
  • Xem thêm DateInterval, bao gồm thời gian bắt đầu và kết thúc thực tế.
  • Sử dụng thời gian từ năm 1970 tương tự như dấu thời gian Java .

Cách dễ nhất, muốn có hai chuỗi, thứ hai là để elapsedTime = Date (). timeIntervalSince1970 - startTime
FreeGor 19/2/18

1

Tôi đã mượn ý tưởng từ Klaas để tạo ra một cấu trúc nhẹ để đo thời gian chạy và khoảng thời gian:

Sử dụng mã:

var timer = RunningTimer.init()
// Code to be timed
print("Running: \(timer) ") // Gives time interval
// Second code to be timed
print("Running: \(timer) ") // Gives final time

Chức năng dừng không phải được gọi, vì chức năng in sẽ cho thời gian mất hiệu lực. Nó có thể được gọi nhiều lần để hết thời gian. Nhưng để dừng bộ hẹn giờ tại một số điểm nhất định trong mã sử dụng, timer.stop()nó cũng có thể được sử dụng để trả về thời gian tính bằng giây: let seconds = timer.stop() Sau khi bộ định thời bị dừng, bộ hẹn giờ sẽ không có print("Running: \(timer) ")thời gian chính xác ngay cả sau một vài dòng mã.

Sau đây là mã cho RunningTimer. Nó được thử nghiệm cho Swift 2.1:

import CoreFoundation
// Usage:    var timer = RunningTimer.init()
// Start:    timer.start() to restart the timer
// Stop:     timer.stop() returns the time and stops the timer
// Duration: timer.duration returns the time
// May also be used with print(" \(timer) ")

struct RunningTimer: CustomStringConvertible {
    var begin:CFAbsoluteTime
    var end:CFAbsoluteTime

    init() {
        begin = CFAbsoluteTimeGetCurrent()
        end = 0
    }
    mutating func start() {
        begin = CFAbsoluteTimeGetCurrent()
        end = 0
    }
    mutating func stop() -> Double {
        if (end == 0) { end = CFAbsoluteTimeGetCurrent() }
        return Double(end - begin)
    }
    var duration:CFAbsoluteTime {
        get {
            if (end == 0) { return CFAbsoluteTimeGetCurrent() - begin } 
            else { return end - begin }
        }
    }
    var description:String {
    let time = duration
    if (time > 100) {return " \(time/60) min"}
    else if (time < 1e-6) {return " \(time*1e9) ns"}
    else if (time < 1e-3) {return " \(time*1e6) µs"}
    else if (time < 1) {return " \(time*1000) ms"}
    else {return " \(time) s"}
    }
}

1

Gói nó trong một khối hoàn thành để dễ sử dụng.

public class func secElapsed(completion: () -> Void) {
    let startDate: NSDate = NSDate()
    completion()
    let endDate: NSDate = NSDate()
    let timeInterval: Double = endDate.timeIntervalSinceDate(startDate)
    println("seconds: \(timeInterval)")
}

0

Đây là đoạn trích tôi nghĩ ra và nó dường như hoạt động với tôi trên Macbook với Swift 4.

Chưa bao giờ thử nghiệm trên các hệ thống khác, nhưng tôi nghĩ dù sao nó cũng đáng để chia sẻ.

typealias MonotonicTS = UInt64
let monotonic_now: () -> MonotonicTS = mach_absolute_time

let time_numer: UInt64
let time_denom: UInt64
do {
    var time_info = mach_timebase_info(numer: 0, denom: 0)
    mach_timebase_info(&time_info)
    time_numer = UInt64(time_info.numer)
    time_denom = UInt64(time_info.denom)
}

// returns time interval in seconds
func monotonic_diff(from: MonotonicTS, to: MonotonicTS) -> TimeInterval {
    let diff = (to - from)
    let nanos = Double(diff * time_numer / time_denom)
    return nanos / 1_000_000_000
}

func seconds_elapsed(since: MonotonicTS) -> TimeInterval {
    return monotonic_diff(from: since, to:monotonic_now())
}

Đây là một ví dụ về cách sử dụng nó:

let t1 = monotonic_now()
// .. some code to run ..
let elapsed = seconds_elapsed(since: t1)
print("Time elapsed: \(elapsed*1000)ms")

Một cách khác là làm điều đó rõ ràng hơn:

let t1 = monotonic_now()
// .. some code to run ..
let t2 = monotonic_now()
let elapsed = monotonic_diff(from: t1, to: t2)
print("Time elapsed: \(elapsed*1000)ms")

0

Đây là cách tôi viết nó.

 func measure<T>(task: () -> T) -> Double {
        let startTime = CFAbsoluteTimeGetCurrent()
        task()
        let endTime = CFAbsoluteTimeGetCurrent()
        let result = endTime - startTime
        return result
    }

Để đo lường một thuật toán sử dụng nó như thế.

let time = measure {
    var array = [2,4,5,2,5,7,3,123,213,12]
    array.sorted()
}

print("Block is running \(time) seconds.")

Làm thế nào chính xác là thời gian đó? Bởi vì tôi đã sử dụng bộ đếm thời gian trước để cập nhật cứ sau 0,1 giây. Tôi đã so sánh câu trả lời này và một câu trả lời với bộ đếm thời gian và kết quả từ câu trả lời này là 0,1 giây thì nhiều hơn với bộ đếm thời gian ..
Maksim Kniazev

0

Lớp Swift3 tĩnh cho thời gian chức năng cơ bản. Nó sẽ theo dõi từng bộ đếm thời gian theo tên. Gọi nó như thế này tại điểm bạn muốn bắt đầu đo:

Stopwatch.start(name: "PhotoCapture")

Gọi đây để chụp và in thời gian đã trôi qua:

Stopwatch.timeElapsed(name: "PhotoCapture")

Đây là đầu ra: *** PhotoCapture đã trôi qua ms: 1402.415125 Có một tham số "useNanos" nếu bạn muốn sử dụng nano. Xin vui lòng thay đổi khi cần thiết.

   class Stopwatch: NSObject {

  private static var watches = [String:TimeInterval]()

  private static func intervalFromMachTime(time: TimeInterval, useNanos: Bool) -> TimeInterval {
     var info = mach_timebase_info()
     guard mach_timebase_info(&info) == KERN_SUCCESS else { return -1 }
     let currentTime = mach_absolute_time()
     let nanos = currentTime * UInt64(info.numer) / UInt64(info.denom)
     if useNanos {
        return (TimeInterval(nanos) - time)
     }
     else {
        return (TimeInterval(nanos) - time) / TimeInterval(NSEC_PER_MSEC)
     }
  }

  static func start(name: String) {
     var info = mach_timebase_info()
     guard mach_timebase_info(&info) == KERN_SUCCESS else { return }
     let currentTime = mach_absolute_time()
     let nanos = currentTime * UInt64(info.numer) / UInt64(info.denom)
     watches[name] = TimeInterval(nanos)
  }

  static func timeElapsed(name: String) {
     return timeElapsed(name: name, useNanos: false)
  }

  static func timeElapsed(name: String, useNanos: Bool) {
     if let start = watches[name] {
        let unit = useNanos ? "nanos" : "ms"
        print("*** \(name) elapsed \(unit): \(intervalFromMachTime(time: start, useNanos: useNanos))")
     }
  }

}


Toàn bộ công việc của bạn mach_timebase_infođã được triển khai tốt hơn bạn đã làm trong mã nguồn của ProcessInfo.processInfo.systemUptime. Vì vậy, chỉ cần làm watches[name] = ProcessInfo.processInfo.systemUptime. Và * TimeInterval(NSEC_PER_MSEC)nếu bạn muốn nanos.
Cœur

0

Dựa trên câu trả lời của Franklin Yu và bình luận của Cur

Chi tiết

  • Xcode 10.1 (10B61)
  • Swift 4.2

Giải pháp 1

đo lường(_:)

Giải pháp 2

import Foundation

class Measurer<T: Numeric> {

    private let startClosure: ()->(T)
    private let endClosure: (_ beginningTime: T)->(T)

    init (startClosure: @escaping ()->(T), endClosure: @escaping (_ beginningTime: T)->(T)) {
        self.startClosure = startClosure
        self.endClosure = endClosure
    }

    init (getCurrentTimeClosure: @escaping ()->(T)) {
        startClosure = getCurrentTimeClosure
        endClosure = { beginningTime in
            return getCurrentTimeClosure() - beginningTime
        }
    }

    func measure(closure: ()->()) -> T {
        let value = startClosure()
        closure()
        return endClosure(value)
    }
}

Sử dụng giải pháp 2

// Sample with ProcessInfo class

m = Measurer { ProcessInfo.processInfo.systemUptime }
time = m.measure {
    _ = (1...1000).map{_ in Int(arc4random()%100)}
}
print("ProcessInfo: \(time)")

// Sample with Posix clock API

m = Measurer(startClosure: {Double(clock())}) { (Double(clock()) - $0 ) / Double(CLOCKS_PER_SEC) }
time = m.measure {
    _ = (1...1000).map{_ in Int(arc4random()%100)}
}
print("POSIX: \(time)")

Không chắc chắn tại sao chúng ta sẽ cần phải sử dụng các triển khai biến. Sử dụng Dateđể đo lường bất cứ điều gì là vô cùng nản lòng (nhưng systemUptimehoặc clock()là OK). Đối với các bài kiểm tra, chúng tôi đã có measure(_:).
Cœur

1
Một lưu ý khác: tại sao làm cho việc đóng cửa tùy chọn?
Cœur

@ Cur bạn đúng! Cảm ơn ý kiến ​​của bạn. Tôi sẽ cập nhật bài sau.
Vasily Bodnarchuk
Khi sử dụng trang web của chúng tôi, bạn xác nhận rằng bạn đã đọc và hiểu Chính sách cookieChính sách bảo mật của chúng tôi.
Licensed under cc by-sa 3.0 with attribution required.