Skip to content

Instantly share code, notes, and snippets.

@jstn
Last active June 19, 2022 15:14
Show Gist options
  • Star 22 You must be signed in to star a gist
  • Fork 7 You must be signed in to fork a gist
  • Save jstn/d93c86f7bd2b6f22f0bf to your computer and use it in GitHub Desktop.
Save jstn/d93c86f7bd2b6f22f0bf to your computer and use it in GitHub Desktop.
simple nanosecond timer using mach_absolute_time
/*
var t = Timer()
t.start()
// do something
t.stop()
print("took \(t.seconds)")
*/
import Darwin
struct Timer {
var startTime: UInt64 = 0
var stopTime: UInt64 = 0
let numer: UInt64
let denom: UInt64
init() {
var info = mach_timebase_info(numer: 0, denom: 0)
mach_timebase_info(&info)
numer = UInt64(info.numer)
denom = UInt64(info.denom)
}
mutating func start() {
startTime = mach_absolute_time()
}
mutating func stop() {
stopTime = mach_absolute_time()
}
var nanoseconds: UInt64 {
return ((stopTime - startTime) * numer) / denom
}
var milliseconds: Double {
return Double(nanoseconds) / 1_000_000
}
var seconds: Double {
return Double(nanoseconds) / 1_000_000_000
}
}
@v57
Copy link

v57 commented Apr 7, 2020

You should add this code. Because for some reason in release mode on both ios and macos mach_timebase_info(&info) will set it to 0,0

if info.denom == 0 {
  info.denom = 1
}
if info.numer == 0 {
  info.numer = 1
}

You can test it in swift pm project:

import Darwin.C

var info = mach_timebase_info()
assert(mach_timebase_info(&info) == 0)
print(info)

Then run swift run -c release

@LK-Simon
Copy link

LK-Simon commented Jun 18, 2022

You aren't retrieving the Timebase Info object correctly, and you'll get the wrong values this way.

var info = mach_timebase_info(numer: 0, denom: 0)
let status = mach_timebase_info(&info)
if status != KERN_SUCCESS {
    print("Couldn't get Timebase Info!") // Obviously replace with your appropriate failure condition here
}

@LK-Simon
Copy link

Don't want to tread on anyone's toes here, but I've made my own implementation of the timer with a few advantages here: https://gist.github.com/LK-Simon/d1f5979c54a064871e4eea7ff2e4abf8

It provides the ability to take point-in-time readings from an "Active" timer... oh, and it includes the concept of "State" (Running and NotRunning) which are used to determine whether the code needs the point-in-time reading, or a "final" reading (when the timer is stopped).

Additionally, mine returns a struct containing the nanosecond value and property-decorated functions to return it in milliseconds and seconds. This means you can take multiple readings, retain them in the struct, use them as you wish, then dispose them as necessary (garbage collection will do this when they are dereferenced)

Finally, my implementation enables your code to feed in the mach time when calling start, stop, and result... so that you can take the reference time immediately at a point of execution, rather than whatever delay may occur when passing along a call stack.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment