-
-
Save jstn/d93c86f7bd2b6f22f0bf to your computer and use it in GitHub Desktop.
/* | |
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 | |
} | |
} |
On my iPhone 5 I got a "exc_arm_breakpoint" on line 35, simulator did not give this error.
Adding brackets like @tumatauenga mentioned fixes the problem.
Haven't researched the problem but seems like this is a 32 bit vs 64 bit issue?
Thanks @tumatauenga & @ryanholden8, that bit me too.
Is there a way to use this so that I can fire a selector after a certain time period?
@DragonMeistah you probably need NSTimer
.
@tumatauenga @acadet fixed this; wish that @jstn would pull from him.
This will be subtly wrong if timebase does not reduce to an integer. Line 22 will convert what should be a fractional scale factor to an integer -- info.numer
should be multiplied by the elapsed time and then the result of that should be divided by info.denom
.
I'm not an expert at Swift but this gist contains what I believe to be correct based on my use of mach_timebase_info
from C.
thank you all, I've updated this based on your comments
There are some more descriptive defines available that this code could use. Wouldn't improve on code correctness, of course, but gives those constants a name.
#define NSEC_PER_SEC 1000000000ull
#define NSEC_PER_MSEC 1000000ull
#define USEC_PER_SEC 1000000ull
#define NSEC_PER_USEC 1000ull
They're available in Swift, too.
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
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
}
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.
You need brackets around
stopTime - startTime
on line 35