วัดเวลาที่ผ่านไปใน Swift


132

เราจะวัดเวลาที่ผ่านไปสำหรับการเรียกใช้ฟังก์ชันใน Swift ได้อย่างไร? ฉันพยายามแสดงเวลาที่ผ่านไปดังนี้: "เวลาที่ผ่านไปคือ. 05 วินาที" เห็นว่าใน Javaเราสามารถใช้ System.nanoTime () ได้มีวิธีการที่เทียบเท่าใน Swift เพื่อทำสิ่งนี้ให้สำเร็จหรือไม่?

โปรดดูโปรแกรมตัวอย่าง:

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
ไม่เกี่ยวข้องกับปัญหาการวัดเวลาของคุณ แต่คุณสามารถหยุดการวนซ้ำได้sqrt(number)แทนnumberและคุณสามารถประหยัดเวลาได้อีกเล็กน้อย แต่ยังมีแนวคิดอีกมากมายในการเพิ่มประสิทธิภาพการค้นหาช่วงเวลา
holex

@holex โปรดเพิกเฉยต่ออัลกอริทึมที่ใช้ ฉันพยายามคิดว่าเราจะวัดเวลาที่ผ่านไปได้อย่างไร?
Vaquita

1
คุณสามารถใช้NSDateวัตถุและคุณสามารถวัดความแตกต่างระหว่างวัตถุเหล่านั้นได้
holex

4
หากคุณใช้ XCode ฉันขอแนะนำให้คุณใช้คุณสมบัติการทดสอบประสิทธิภาพใหม่ มันช่วยยกของหนักทั้งหมดให้คุณและยังทำงานหลายครั้งและให้เวลาเฉลี่ยกับค่าเบี่ยงเบนมาตรฐานของมัน ...
Roshan

1
@dumbledad คุณสามารถวัดประสิทธิภาพของบล็อกทั้งหมดได้โดยตรงในการทดสอบหน่วย ยกตัวอย่างเช่นเห็นนี้ หากคุณต้องการแบ่งรายละเอียดเพิ่มเติม (พูดทีละบรรทัดโค้ด) ลองดูTime Profilerซึ่งเป็นส่วนหนึ่งของ Instruments สิ่งนี้มีประสิทธิภาพและครอบคลุมกว่ามาก
Roshan

คำตอบ:


229

นี่คือฟังก์ชั่น Swift ที่ฉันเขียนเพื่อวัดปัญหาProject Eulerใน Swift

สำหรับ Swift 3 ตอนนี้มีเวอร์ชันของ Grand Central Dispatch ที่ "รวดเร็ว" ดังนั้นคำตอบที่ถูกต้องคืออาจจะใช้DispatchTime API

ฟังก์ชันของฉันจะมีลักษณะดังนี้:

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

คำตอบเก่า

สำหรับ Swift 1 และ 2 ฟังก์ชันของฉันใช้ 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
}

โปรดทราบว่าการใช้ NSdate สำหรับฟังก์ชันจับเวลาเป็นสิ่งที่ไม่ควรทำ: " เวลาของระบบอาจลดลงเนื่องจากการซิงโครไนซ์กับการอ้างอิงเวลาภายนอกหรือเนื่องจากการเปลี่ยนนาฬิกาของผู้ใช้อย่างชัดเจน "


ดูเหมือนว่าจะให้ผลลัพธ์ที่ดีถึงประมาณ +/- 2microsec อย่างไรก็ตามอาจแตกต่างกันไปตามแพลตฟอร์ม
user1021430

6
swift3 ไม่ได้ผลสำหรับฉันมันส่งคืน 0.0114653027 ซึ่งฉันรู้ว่าข้อเท็จจริงที่ไม่เป็นความจริงอันที่มีวันที่ส่งคืน 4.77720803022385sec
Cristi Băluță

5
แต่น่าเสียดายที่ไม่ได้. เวลาทำงานจะหมดลงอย่างสมบูรณ์ด้วยเหตุผลบางประการ ฉันมีการทดสอบที่บอกว่าใช้เวลา 1004959766 นาโนวินาที (ประมาณหนึ่งวินาที) แต่แน่นอนว่าใช้เวลาประมาณ 40 วินาที ฉันใช้Dateควบคู่ไปด้วยและแน่นอนว่ารายงาน 41.87 วินาที ฉันไม่รู้ว่าuptimeNanosecondsกำลังทำอะไรอยู่ แต่ไม่ได้รายงานระยะเวลาที่ถูกต้อง
jowie

2
ขออภัยสำหรับการแก้ไขครั้งใหญ่จัดลำดับความสำคัญของโซลูชันที่ใหม่กว่าของคุณ แต่ฉันอยากจะแนะนำให้ลบรหัส NSDate ของคุณทั้งหมด: การใช้ NSDate ไม่น่าเชื่อถือโดยสิ้นเชิง: NSDate ขึ้นอยู่กับนาฬิกาของระบบซึ่งสามารถเปลี่ยนแปลงได้ตลอดเวลาเนื่องจากเหตุผลหลายประการเช่น เมื่อซิงค์เวลาเครือข่าย (NTP) จะอัปเดตนาฬิกา (มักเกิดขึ้นเพื่อปรับเวลาลอย) การปรับ DST วินาทีอธิกสุรทินและการปรับนาฬิกาด้วยตนเอง นอกจากนี้คุณไม่สามารถเผยแพร่อีกต่อไปบน AppStore แอปใด ๆ โดยใช้ Swift 1: Swift 3 เป็นขั้นต่ำที่เปลือยเปล่า ดังนั้นจึงไม่มีประเด็นใดที่จะต้องเก็บรหัส NSDate ของคุณนอกจากพูดว่า "อย่าทำ"
Cœur

1
@ Cœurคำวิจารณ์ของคุณเกี่ยวกับเวอร์ชัน NSDate นั้นถูกต้อง (ยกเว้นการเปลี่ยนแปลง DST จะไม่มีผลกับ NSDate ซึ่งอยู่ใน GMT เสมอ) และการแก้ไขของคุณเพื่อย้อนกลับลำดับนั้นเป็นการปรับปรุงอย่างแน่นอน
JeremyP

69

นี่คือคลาสจับเวลาที่สะดวกโดยอิงจาก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
        }
    }
}

คุณสามารถใช้งานได้ดังนี้:

let timer = ParkBenchTimer()

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

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

4
" โทรซ้ำฟังก์ชั่นนี้ไม่รับประกัน monotonically เพิ่มขึ้นผล. " ตามของเอกสาร
Franklin Yu

8
บริบทเพิ่มเติม: "การเรียกใช้ฟังก์ชันนี้ซ้ำ ๆ ไม่รับประกันผลลัพธ์ที่เพิ่มขึ้นอย่างจำเจเวลาของระบบอาจลดลงเนื่องจากการซิงโครไนซ์กับการอ้างอิงเวลาภายนอกหรือเนื่องจากการเปลี่ยนนาฬิกาของผู้ใช้อย่างชัดเจน "
Klaas

นักพัฒนาควรคำนึงถึง "การซิงโครไนซ์กับการอ้างอิงเวลาภายนอก" และ "การเปลี่ยนนาฬิกาของผู้ใช้อย่างชัดเจน" คุณหมายความว่ามันเป็นไปไม่ได้ที่ทั้งสองสถานการณ์จะเกิดขึ้นหรือ
Franklin Yu

@FranklinYu ไม่ฉันแค่อยากจะบอกว่านี่คือสองเหตุผลที่เป็นไปได้ หากคุณพึ่งพาการเพิ่มมูลค่าซ้ำซากจำเจอยู่เสมอมีตัวเลือกอื่น ๆ
Klaas

บางครั้งฉันต้องการวัดช่วงเวลาสั้น ๆ หากการซิงโครไนซ์เกิดขึ้นในระหว่างนั้นฉันอาจต้องใช้เวลาติดลบ มีจริงmach_absolute_timeที่ไม่ประสบปัญหานี้ดังนั้นฉันจึงสงสัยว่าเหตุใดจึงไม่เป็นที่รู้จักในวงกว้าง อาจเป็นเพียงเพราะเอกสารไม่ดี
Franklin Yu

54

การใช้งานclock, ProcessInfo.systemUptimeหรือDispatchTimeเวลาเริ่มต้นขึ้นง่าย


เท่าที่ฉันรู้มีอย่างน้อยสิบวิธีในการวัดเวลาที่ผ่านไป:

Monotonic Clock ตาม:

  1. ProcessInfo.systemUptime.
  2. mach_absolute_timeกับmach_timebase_infoที่กล่าวไว้ในคำตอบนี้
  3. clock()ในมาตรฐาน POSIX
  4. times()ในมาตรฐาน POSIX (ซับซ้อนเกินไปเนื่องจากเราต้องพิจารณาเวลาของผู้ใช้เทียบกับเวลาของระบบและกระบวนการย่อยมีส่วนเกี่ยวข้อง)
  5. DispatchTime (เครื่องห่อรอบ Mach time API) ตามที่ JeremyP กล่าวไว้ในคำตอบที่ยอมรับ
  6. CACurrentMediaTime().

นาฬิกาแขวนตาม:

(อย่าใช้สิ่งเหล่านี้สำหรับเมตริก: ดูเหตุผลด้านล่าง)

  1. NSDate/ Dateตามที่ผู้อื่นกล่าวถึง
  2. CFAbsoluteTime ตามที่ผู้อื่นกล่าวถึง
  3. DispatchWallTime.
  4. gettimeofday()ในมาตรฐาน POSIX

ตัวเลือกที่ 1, 2 และ 3 มีรายละเอียดด้านล่าง

ตัวเลือกที่ 1: Process Info API ใน Foundation

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

ซึ่งdiff:NSTimeIntervalเป็นเวลาที่ผ่านไปโดยวินาที

ตัวเลือกที่ 2: Mach C API

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

ซึ่งdiff:Doubleเป็นเวลาที่ผ่านไปโดยนาโนวินาที

ตัวเลือกที่ 3: POSIX clock API

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

ซึ่งdiff:Doubleเป็นเวลาที่ผ่านไปโดยวินาที

ทำไมไม่ใช้นาฬิกาแขวนเป็นเวลาที่ผ่านไป?

ในเอกสารของCFAbsoluteTimeGetCurrent:

การเรียกใช้ฟังก์ชันนี้ซ้ำ ๆ ไม่ได้รับประกันผลลัพธ์ที่เพิ่มขึ้นอย่างจำเจ

เหตุผลคล้ายcurrentTimeMillisกับnanoTimeใน Java :

คุณไม่สามารถใช้เพื่อวัตถุประสงค์อื่นได้ เหตุผลก็คือไม่มีนาฬิกาของคอมพิวเตอร์ที่สมบูรณ์แบบ มันลอยอยู่เสมอและบางครั้งต้องได้รับการแก้ไข การแก้ไขนี้อาจเกิดขึ้นด้วยตนเองหรือในกรณีของเครื่องส่วนใหญ่มีกระบวนการที่ทำงานและออกการแก้ไขเล็กน้อยอย่างต่อเนื่องกับนาฬิการะบบ ("นาฬิกาแขวน") สิ่งเหล่านี้มักจะเกิดขึ้นบ่อยครั้ง การแก้ไขเช่นนี้เกิดขึ้นเมื่อใดก็ตามที่มีวินาทีอธิกสุรทิน

ที่นี่CFAbsoluteTimeให้เวลานาฬิกาแขวนแทนเวลาเริ่มต้น NSDateเป็นเวลานาฬิกาแขวนเช่นกัน


คำตอบที่ดีที่สุด - แต่อะไรคือแนวทางที่ดีที่สุดที่จะทำ?!
Fattie

1
ตัวเลือกที่ 5 เหมาะกับฉันที่สุด หากคุณกำลังพิจารณารหัสหลายแพลตฟอร์มลองใช้รหัสนี้ ทั้งห้องสมุด Darwin และ Glibc มีclock()ฟังก์ชัน
Misha Svyatoshenko

สำหรับการแก้ระบบนาฬิกาตาม: ผมประสบความสำเร็จกับProcessInfo.processInfo.systemUptime, mach_absolute_time(), และDispatchTime.now() CACurrentMediaTime()แต่วิธีแก้ปัญหาclock()ไม่สามารถแปลงเป็นวินาทีได้อย่างถูกต้อง ดังนั้นฉันขอแนะนำให้อัปเดตประโยคแรกของคุณเป็น: " ใช้ ProcessInfo.systemUptime หรือ DispatchTime เพื่อเวลาเริ่มต้นที่ถูกต้อง "
Cœur

36

คำตอบที่สั้นที่สุดของ Swift 4:

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

มันจะพิมพ์ให้คุณบางอย่างเช่นเวลาผ่านไป 1.02107906341553 วินาที (เวลาแน่นอนจะแตกต่างกันไปขึ้นอยู่กับงานฉันแค่แสดงสิ่งนี้ให้พวกคุณเห็นระดับความแม่นยำทศนิยมสำหรับการวัดนี้)

หวังว่ามันจะช่วยใครบางคนใน Swift 4 ต่อจากนี้!

ปรับปรุง

หากคุณต้องการทดสอบโค้ดส่วนต่างๆโดยทั่วไปเราขอแนะนำตัวอย่างถัดไป:

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

ใครช่วยอธิบายว่าทำไม * -1
Maksim Kniazev

1
@MaksimKniazev เพราะมันเป็นค่าลบคนจึงต้องการบวก!
thachnb

@thachnb ฉันไม่รู้ว่า "startPoint.timeIntervalSinceNow" สร้างมูลค่าเชิงลบ ...
Maksim Kniazev

1
@MaksimKniazev Apple กล่าวว่า: หากวัตถุวันที่อยู่ก่อนหน้าวันที่และเวลาปัจจุบันค่าของคุณสมบัตินี้จะเป็นลบ
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) // <- ค่าบวก
eonist

วิธีแก้ปัญหาที่ตรงไปตรงมาที่สุดที่ฉันเคยเห็นมาขอบคุณ
MiladiuM

10

เพียงแค่คัดลอกและวางฟังก์ชันนี้ เขียนด้วยความรวดเร็ว 5. คัดลอก JeremyP ที่นี่

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")
    }

ใช้แล้วชอบ

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

6

คุณสามารถสร้างtimeฟังก์ชันสำหรับวัดการโทรของคุณ ฉันได้รับแรงบันดาลใจจากคำตอบของ 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.")
}

ฟังก์ชันนี้จะช่วยให้คุณสามารถเรียกมันแบบนี้time (isPrime(7))ซึ่งจะส่งคืนทูเพิลที่มีผลลัพธ์และคำอธิบายสตริงของเวลาที่ผ่านไป

หากคุณต้องการเพียงเวลาที่ผ่านไปคุณสามารถทำได้ time (isPrime(7)).duration


3
"การเรียกใช้ฟังก์ชันนี้ซ้ำ ๆ ไม่ได้รับประกันผลลัพธ์ที่เพิ่มขึ้นอย่างจำเจ" ตามเอกสาร .
Franklin Yu

3

ฟังก์ชันตัวช่วยที่เรียบง่ายสำหรับการวัดเวลาดำเนินการด้วยการปิด

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

การใช้งาน:

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

ผลลัพธ์: #Init - execution took 1.00104497105349 seconds


2

คุณสามารถวัดนาโนวินาทีเช่นนี้:

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)")

ฉันเพิ่งเรียกใช้สิ่งนี้โดยไม่ได้ใส่รหัสไว้ใน "// ขั้นตอนยาวของคุณ" และให้ผลลัพธ์เป็น 240900993 ซึ่งเป็นเวลา 0.24 วินาที
user1021430

การสร้างอินสแตนซ์NSCalendarขั้นตอนมีราคาแพง แต่คุณสามารถทำได้ก่อนที่จะเริ่มเรียกใช้โพรซีเดอร์ของคุณดังนั้นจะไม่ถูกเพิ่มลงในรันไทม์ของโพรซีเดอร์ที่ยาวนานของคุณ ... โปรดทราบว่าการเรียกสร้างNSDateอินสแตนซ์และ–components(_:, fromDate:)วิธีการโทรยังคงต้องใช้เวลาดังนั้นอาจ คุณจะไม่ได้รับ0.0นาโนวินาที
holex

จากรูปลักษณ์ของตัวสร้าง NSCalendar ดูเหมือนว่าอาจไม่สามารถสร้างได้ล่วงหน้า (startDate เป็นอินพุตที่จำเป็น) เป็นเรื่องที่น่าแปลกใจที่ดูเหมือนจะเป็นหมู
user1021430

คุณสามารถทำสิ่งนี้ได้ (คำตอบที่อัปเดตของฉัน) เวลาที่วัดได้โดยไม่ต้องมีขั้นตอนเพิ่มเติมคือประมาณ6.9ไมโครวินาทีบนอุปกรณ์ของฉัน
holex

ขอบคุณครับ ขั้นตอนพื้นฐานตอนนี้คล้ายกับคำตอบของ JeremyP แต่การรายงานแตกต่างกัน
user1021430

2

ฉันใช้สิ่งนี้:

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_;
    }
}

ฉันไม่รู้ว่ามันแม่นยำมากหรือน้อยกว่าที่เคยโพสต์ไว้ แต่วินาทีนั้นมีทศนิยมจำนวนมากและดูเหมือนว่าจะตรวจจับการเปลี่ยนแปลงโค้ดเล็กน้อยในอัลกอริทึมเช่น QuickSort โดยใช้ swap () เทียบกับการใช้ swap urself เป็นต้น

อย่าลืมเพิ่มประสิทธิภาพการสร้างของคุณเมื่อทดสอบประสิทธิภาพ:

การเพิ่มประสิทธิภาพคอมไพเลอร์อย่างรวดเร็ว


2

นี่คือความพยายามของฉันสำหรับคำตอบที่ง่ายที่สุด:

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

หมายเหตุ

  • startTimeและendTimeเป็นประเภทTimeIntervalซึ่งเป็นเพียงtypealiasสำหรับDoubleดังนั้นจึงเป็นเรื่องง่ายที่จะแปลงเป็น an Intหรืออะไรก็ได้ เวลาถูกวัดเป็นวินาทีโดยมีความแม่นยำย่อยมิลลิวินาที
  • ดูเพิ่มเติมDateIntervalซึ่งรวมถึงเวลาเริ่มต้นและเวลาสิ้นสุดจริง
  • ใช้เวลาตั้งแต่ปี 1970 มีความคล้ายคลึงกับการประทับเวลา Java

วิธีที่ง่ายที่สุดต้องการให้เป็นสองสตริงวินาทีคือlet elapsedTime = Date (). timeIntervalSince1970 - startTime
FreeGor

1

ฉันได้ยืมแนวคิดจาก Klaas เพื่อสร้างโครงสร้างที่มีน้ำหนักเบาเพื่อวัดการวิ่งและช่วงเวลา:

การใช้รหัส:

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

ไม่จำเป็นต้องเรียกใช้ฟังก์ชันหยุดเนื่องจากฟังก์ชันการพิมพ์จะทำให้เวลาหมดไป อาจเรียกซ้ำ ๆ กันเพื่อให้เวลาล่วงเลยไป แต่หากต้องการหยุดตัวจับเวลา ณ จุดหนึ่งในรหัสให้ใช้timer.stop()มันอาจใช้เพื่อคืนเวลาเป็นวินาทีด้วยเช่นกัน: let seconds = timer.stop() หลังจากหยุดจับเวลาแล้วตัวจับเวลาช่วงจะไม่ทำงานดังนั้นprint("Running: \(timer) ")จะให้เวลาที่ถูกต้องแม้จะใส่รหัสไปไม่กี่บรรทัด

ต่อไปนี้เป็นรหัสสำหรับ RunningTimer ได้รับการทดสอบสำหรับ 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

ห่อไว้ในบล็อกที่สมบูรณ์เพื่อให้ใช้งานได้ง่าย

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

นี่คือตัวอย่างข้อมูลที่ฉันคิดขึ้นมาและดูเหมือนว่าจะใช้ได้กับฉันใน Macbook ที่มี Swift 4

ไม่เคยทดสอบกับระบบอื่น แต่ฉันคิดว่ามันคุ้มค่าที่จะแบ่งปัน

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

นี่คือตัวอย่างวิธีการใช้งาน:

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

อีกวิธีหนึ่งคือทำอย่างชัดเจนมากขึ้น:

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

นี่คือวิธีที่ฉันเขียนมัน

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

ในการวัดอัลกอริทึมให้ใช้เช่นนั้น

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

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

เวลานั้นแม่นยำแค่ไหน? เพราะฉันใช้ตัวจับเวลามาก่อนเพื่ออัปเดตทุกๆ 0.1 วินาที ฉันเปรียบเทียบคำตอบนี้กับคำตอบหนึ่งกับตัวจับเวลาและผลลัพธ์จากคำตอบนี้คือ 0.1 วินาทีนั้นมากกว่าด้วยตัวจับเวลา ..
Maksim Kniazev

0

คลาส Swift3 แบบคงที่สำหรับการจับเวลาฟังก์ชันพื้นฐาน มันจะติดตามตัวจับเวลาแต่ละครั้งตามชื่อ เรียกเช่นนี้ในจุดที่คุณต้องการเริ่มวัด:

Stopwatch.start(name: "PhotoCapture")

เรียกสิ่งนี้เพื่อจับภาพและพิมพ์เวลาที่ผ่านไป:

Stopwatch.timeElapsed(name: "PhotoCapture")

นี่คือผลลัพธ์: *** PhotoCapture ที่ผ่านไป ms: 1402.415125 มีพารามิเตอร์ "useNanos" หากคุณต้องการใช้นาโน โปรดอย่าลังเลที่จะเปลี่ยนแปลงตามความจำเป็น

   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))")
     }
  }

}


งานทั้งหมดของคุณmach_timebase_infoใช้งานได้ดีกว่าที่คุณทำในซอร์สโค้ดของProcessInfo.processInfo.systemUptime. watches[name] = ProcessInfo.processInfo.systemUptimeดังนั้นก็ทำ และ* TimeInterval(NSEC_PER_MSEC)ถ้าคุณต้องการนาโน
Cœur

0

จากคำตอบของFranklin Yuและความคิดเห็นของCœur

รายละเอียด

  • Xcode 10.1 (10B61)
  • Swift 4.2

โซลูชันที่ 1

วัด(_:)

โซลูชันที่ 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)
    }
}

การใช้โซลูชัน 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)")

ไม่แน่ใจว่าเหตุใดเราจึงต้องใช้การใช้งานตัวแปร การใช้Dateเพื่อวัดสิ่งใด ๆ เป็นสิ่งที่ไม่พึงปรารถนาอย่างยิ่ง (แต่systemUptimeหรือclock()ไม่เป็นไร) สำหรับการทดสอบเรามีmeasure(_:)แล้ว
Cœur

1
หมายเหตุอีกประการหนึ่ง: ทำไมต้องปิดตัวเลือก?
Cœur

@ C rightur คุณถูกต้อง! ขอบคุณสำหรับความคิดเห็นของคุณ ฉันจะอัปเดตโพสต์ในภายหลัง
Vasily Bodnarchuk
โดยการใช้ไซต์ของเรา หมายความว่าคุณได้อ่านและทำความเข้าใจนโยบายคุกกี้และนโยบายความเป็นส่วนตัวของเราแล้ว
Licensed under cc by-sa 3.0 with attribution required.