C'è un modo semplice per ottenere un tempo molto preciso?
Devo calcolare alcuni ritardi tra le chiamate al metodo. Più specificamente, voglio calcolare la velocità di scorrimento in un UIScrollView.
C'è un modo semplice per ottenere un tempo molto preciso?
Devo calcolare alcuni ritardi tra le chiamate al metodo. Più specificamente, voglio calcolare la velocità di scorrimento in un UIScrollView.
Risposte:
NSDate
e i timeIntervalSince*
metodi restituiranno a NSTimeInterval
che è un doppio con una precisione inferiore al millisecondo. NSTimeInterval
è in pochi secondi, ma usa il doppio per darti una maggiore precisione.
Per calcolare la precisione temporale in millisecondi, puoi fare:
// Get a current time for where you want to start measuring from
NSDate *date = [NSDate date];
// do work...
// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
double timePassed_ms = [date timeIntervalSinceNow] * -1000.0;
Documentazione su timeIntervalSinceNow .
Esistono molti altri modi per calcolare questo intervallo utilizzando NSDate
e consiglierei di consultare la documentazione della classe per la NSDate
quale si trova in NSDate Class Reference .
NSDate
e mach_absolute_time()
al livello di circa 30 ms. 27 contro 29, 36 contro 39, 43 contro 45. NSDate
era più facile da usare per me ei risultati erano abbastanza simili da non preoccuparmene.
mach_absolute_time()
può essere utilizzato per ottenere misurazioni precise.
Vedi http://developer.apple.com/qa/qa2004/qa1398.html
È disponibile anche CACurrentMediaTime()
, che è essenzialmente la stessa cosa ma con un'interfaccia più facile da usare.
(Nota: questa risposta è stata scritta nel 2009. Vedi la risposta di Pavel Alexeev per le clock_gettime()
interfacce POSIX più semplici disponibili nelle versioni più recenti di macOS e iOS.)
CACurrentMediaTime()
che converte mach_absolute_time()
direttamente in un file double
.
elapsedNano
è di tipo Nanoseconds
, che non è un tipo intero normale. È un alias di UnsignedWide
, che è una struttura con due campi interi a 32 bit. Puoi usare al UnsignedWideToUInt64()
posto del cast se preferisci.
Si prega di non utilizzare NSDate
, CFAbsoluteTimeGetCurrent
o gettimeofday
per misurare il tempo trascorso. Questi dipendono tutti dall'orologio di sistema, che può cambiare in qualsiasi momento a causa di molti motivi diversi, come la sincronizzazione dell'ora di rete (NTP) che aggiorna l'orologio (accade spesso per adattarsi alla deriva), le regolazioni dell'ora legale, i secondi intercalari e così via.
Ciò significa che se stai misurando la tua velocità di download o upload, otterrai picchi o cali improvvisi nei tuoi numeri che non sono correlati a ciò che è realmente accaduto; i tuoi test delle prestazioni avranno strani valori anomali errati; e i timer manuali si attiveranno dopo durate errate. Il tempo potrebbe anche tornare indietro e si finisce con delta negativi e si può finire con la ricorsione infinita o il codice morto (sì, ho fatto entrambi).
Usa mach_absolute_time
. Misura i secondi reali da quando il kernel è stato avviato. Aumenta in modo monotono (non tornerà mai indietro) e non è influenzato dalle impostazioni di data e ora. Dal momento che è un problema con cui lavorare, ecco un semplice involucro che ti dà NSTimeInterval
:
// LBClock.h
@interface LBClock : NSObject
+ (instancetype)sharedClock;
// since device boot or something. Monotonically increasing, unaffected by date and time settings
- (NSTimeInterval)absoluteTime;
- (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute;
@end
// LBClock.m
#include <mach/mach.h>
#include <mach/mach_time.h>
@implementation LBClock
{
mach_timebase_info_data_t _clock_timebase;
}
+ (instancetype)sharedClock
{
static LBClock *g;
static dispatch_once_t onceToken;
dispatch_once(&onceToken, ^{
g = [LBClock new];
});
return g;
}
- (id)init
{
if(!(self = [super init]))
return nil;
mach_timebase_info(&_clock_timebase);
return self;
}
- (NSTimeInterval)machAbsoluteToTimeInterval:(uint64_t)machAbsolute
{
uint64_t nanos = (machAbsolute * _clock_timebase.numer) / _clock_timebase.denom;
return nanos/1.0e9;
}
- (NSTimeInterval)absoluteTime
{
uint64_t machtime = mach_absolute_time();
return [self machAbsoluteToTimeInterval:machtime];
}
@end
CACurrentMediaTime()
di QuartzCore?
@import Darwin;
posto di#include <mach/mach_time.h>
CFAbsoluteTimeGetCurrent()
restituisce il tempo assoluto come double
valore, ma non so quale sia la sua precisione: potrebbe aggiornare solo ogni dozzina di millisecondi o potrebbe aggiornare ogni microsecondo, non lo so.
72.89674947369
secondi sarebbe piuttosto raro, considerando tutti gli altri valori che potrebbe essere. ;)
NON mach_absolute_time()
lo userei perché interroga una combinazione del kernel e del processore per un tempo assoluto usando i tick (probabilmente un uptime).
Cosa userei:
CFAbsoluteTimeGetCurrent();
Questa funzione è ottimizzata per correggere la differenza nel software e hardware iOS e OSX.
Qualcosa di più geek
Il quoziente di una differenza in mach_absolute_time()
ed AFAbsoluteTimeGetCurrent()
è sempre intorno a 24000011.154871
Ecco un registro della mia app:
Si prega di notare che il tempo risultato finale è una differenza di CFAbsoluteTimeGetCurrent()
s'
2012-03-19 21:46:35.609 Rest Counter[3776:707] First Time: 353900795.609040
2012-03-19 21:46:36.360 Rest Counter[3776:707] Second Time: 353900796.360177
2012-03-19 21:46:36.361 Rest Counter[3776:707] Final Result Time (difference): 0.751137
2012-03-19 21:46:36.363 Rest Counter[3776:707] Mach absolute time: 18027372
2012-03-19 21:46:36.365 Rest Counter[3776:707] Mach absolute time/final time: 24000113.153295
2012-03-19 21:46:36.367 Rest Counter[3776:707] -----------------------------------------------------
2012-03-19 21:46:43.074 Rest Counter[3776:707] First Time: 353900803.074637
2012-03-19 21:46:43.170 Rest Counter[3776:707] Second Time: 353900803.170256
2012-03-19 21:46:43.172 Rest Counter[3776:707] Final Result Time (difference): 0.095619
2012-03-19 21:46:43.173 Rest Counter[3776:707] Mach absolute time: 2294833
2012-03-19 21:46:43.175 Rest Counter[3776:707] Mach absolute time/final time: 23999753.727777
2012-03-19 21:46:43.177 Rest Counter[3776:707] -----------------------------------------------------
2012-03-19 21:46:46.499 Rest Counter[3776:707] First Time: 353900806.499199
2012-03-19 21:46:55.017 Rest Counter[3776:707] Second Time: 353900815.016985
2012-03-19 21:46:55.018 Rest Counter[3776:707] Final Result Time (difference): 8.517786
2012-03-19 21:46:55.020 Rest Counter[3776:707] Mach absolute time: 204426836
2012-03-19 21:46:55.022 Rest Counter[3776:707] Mach absolute time/final time: 23999996.639500
2012-03-19 21:46:55.024 Rest Counter[3776:707] -----------------------------------------------------
mach_absolute_time()
con a mach_timebase_info_data
e poi l'ho fatto (long double)((mach_absolute_time()*time_base.numer)/((1000*1000)*time_base.denom));
. Per ottenere la base dei tempi di mach, puoi fare(void)mach_timebase_info(&your_timebase);
#define CTTimeStart() NSDate * __date = [NSDate date]
#define CTTimeEnd(MSG) NSLog(MSG " %g",[__date timeIntervalSinceNow]*-1)
Utilizzo:
CTTimeStart();
...
CTTimeEnd(@"that was a long time:");
Produzione:
2013-08-23 15:34:39.558 App-Dev[21229:907] that was a long time: .0023
NSDate
dipende dall'orologio di sistema che può essere modificato in qualsiasi momento, risultando potenzialmente in intervalli di tempo errati o addirittura negativi. Utilizzare mach_absolute_time
invece per ottenere il tempo trascorso corretto.
mach_absolute_time
può essere influenzato dai riavvii del dispositivo. Usa invece l'ora del server.
Inoltre, ecco come calcolare un 64 bit NSNumber
inizializzato con l'epoca Unix in millisecondi, nel caso in cui sia così che si desidera memorizzarlo in CoreData. Ne avevo bisogno per la mia app che interagisce con un sistema che memorizza le date in questo modo.
+ (NSNumber*) longUnixEpoch {
return [NSNumber numberWithLongLong:[[NSDate date] timeIntervalSince1970] * 1000];
}
Le funzioni basate su mach_absolute_time
sono utili per misurazioni brevi.
Ma per misurazioni lunghe, un avvertimento importante è che smettono di ticchettare mentre il dispositivo è addormentato.
C'è una funzione per ottenere tempo dall'avvio. Non si ferma durante il sonno. Inoltre, gettimeofday
non è monotono, ma nei miei esperimenti ho sempre visto che l'ora di avvio cambia quando cambia l'ora di sistema, quindi penso che dovrebbe funzionare bene.
func timeSinceBoot() -> TimeInterval
{
var bootTime = timeval()
var currentTime = timeval()
var timeZone = timezone()
let mib = UnsafeMutablePointer<Int32>.allocate(capacity: 2)
mib[0] = CTL_KERN
mib[1] = KERN_BOOTTIME
var size = MemoryLayout.size(ofValue: bootTime)
var timeSinceBoot = 0.0
gettimeofday(¤tTime, &timeZone)
if sysctl(mib, 2, &bootTime, &size, nil, 0) != -1 && bootTime.tv_sec != 0 {
timeSinceBoot = Double(currentTime.tv_sec - bootTime.tv_sec)
timeSinceBoot += Double(currentTime.tv_usec - bootTime.tv_usec) / 1000000.0
}
return timeSinceBoot
}
E da iOS 10 e macOS 10.12 possiamo usare CLOCK_MONOTONIC:
if #available(OSX 10.12, *) {
var uptime = timespec()
if clock_gettime(CLOCK_MONOTONIC_RAW, &uptime) == 0 {
return Double(uptime.tv_sec) + Double(uptime.tv_nsec) / 1000000000.0
}
}
Riassumendo:
Date.timeIntervalSinceReferenceDate
- cambia quando cambia l'ora del sistema, non monotonoCFAbsoluteTimeGetCurrent()
- non monotono, può tornare indietroCACurrentMediaTime()
- smette di ticchettare quando il dispositivo è addormentatotimeSinceBoot()
- non dorme, ma potrebbe non essere monotonoCLOCK_MONOTONIC
- non dorme, monotono, supportato da iOS 10So che questo è vecchio ma anche io mi sono ritrovato a vagabondare di nuovo, quindi ho pensato di presentare la mia opzione qui.
La cosa migliore è dare un'occhiata al mio post sul blog su questo: Cronometrare le cose in Objective-C: un cronometro
Fondamentalmente, ho scritto una lezione che smette di guardare in un modo molto semplice ma è incapsulata in modo che tu debba fare solo quanto segue:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
E ti ritroverai con:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
nel registro ...
Ancora una volta, controlla il mio post per ulteriori informazioni o scaricalo qui: MMStopwatch.zip
NSDate
dipende dall'orologio di sistema che può essere modificato in qualsiasi momento, risultando potenzialmente in intervalli di tempo errati o addirittura negativi. Utilizzare mach_absolute_time
invece per ottenere il tempo trascorso corretto.
Puoi ottenere l'ora corrente in millisecondi dal 1 ° gennaio 1970 utilizzando NSDate:
- (double)currentTimeInMilliseconds {
NSDate *date = [NSDate date];
return [date timeIntervalSince1970]*1000;
}
Per quelli abbiamo bisogno della versione Swift della risposta di @Jeff Thompson:
// Get a current time for where you want to start measuring from
var date = NSDate()
// do work...
// Find elapsed time and convert to milliseconds
// Use (-) modifier to conversion since receiver is earlier than now
var timePassed_ms: Double = date.timeIntervalSinceNow * -1000.0
Spero che questo ti sia d'aiuto.
NSDate
dipende dall'orologio di sistema che può essere modificato in qualsiasi momento, risultando potenzialmente in intervalli di tempo errati o addirittura negativi. Utilizzare mach_absolute_time
invece per ottenere il tempo trascorso corretto.