Czy istnieje sposób na określenie, ile czasu musi wykonać metoda (w milisekundach)?
Czy istnieje sposób na określenie, ile czasu musi wykonać metoda (w milisekundach)?
Odpowiedzi:
NSDate *methodStart = [NSDate date];
/* ... Do whatever you need to do ... */
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);
Szybki:
let methodStart = NSDate()
/* ... Do whatever you need to do ... */
let methodFinish = NSDate()
let executionTime = methodFinish.timeIntervalSinceDate(methodStart)
print("Execution time: \(executionTime)")
Swift3:
let methodStart = Date()
/* ... Do whatever you need to do ... */
let methodFinish = Date()
let executionTime = methodFinish.timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")
Łatwy w użyciu i ma precyzję poniżej milisekundy.
NSLog(@"executionTime = %f", executionTime);
NSDate
i mach_absolute_time()
na poziomie około 30 ms. 27 vs. 29, 36 vs. 39, 43 vs. 45. NSDate
było dla mnie łatwiejsze w użyciu, a wyniki były na tyle podobne, że nie zawracałem sobie tym głowy mach_absolute_time()
.
Oto dwa makra jednowierszowe, których używam:
#define TICK NSDate *startTime = [NSDate date]
#define TOCK NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])
Użyj tego w ten sposób:
TICK;
/* ... Do Some Work Here ... */
TOCK;
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])
sprawia, że ta odpowiedź również zwraca, w której funkcji użyto timera. Uznałem to za użyteczne, jeśli użyłem TICK TOCK do pomiaru wielu funkcji.
__PRETTY_FUNCTION__
i __LINE__
uzyskać bardziej szczegółowe informacje.
W celu uzyskania precyzyjnego pomiaru czasu w systemie OS X należy użyć mach_absolute_time( )
deklarowanego w <mach/mach_time.h>
:
#include <mach/mach_time.h>
#include <stdint.h>
// Do some stuff to setup for timing
const uint64_t startTime = mach_absolute_time();
// Do some stuff that you want to time
const uint64_t endTime = mach_absolute_time();
// Time elapsed in Mach time units.
const uint64_t elapsedMTU = endTime - startTime;
// Get information for converting from MTU to nanoseconds
mach_timebase_info_data_t info;
if (mach_timebase_info(&info))
handleErrorConditionIfYoureBeingCareful();
// Get elapsed time in nanoseconds:
const double elapsedNS = (double)elapsedMTU * (double)info.numer / (double)info.denom;
Oczywiście obowiązują zwykłe zastrzeżenia dotyczące drobnoziarnistych pomiarów; prawdopodobnie najlepiej jest przywoływać testowaną procedurę wiele razy i uśredniać / przyjmować minimum / inną formę przetwarzania.
Ponadto pamiętaj, że bardziej przydatne może być profilowanie aplikacji przy użyciu narzędzia takiego jak Shark. Nie da ci to dokładnych informacji o czasie, ale powie ci, jaki procent czasu aplikacji spędza gdzie, co jest często bardziej przydatne (ale nie zawsze).
Jest wygodne opakowanie mach_absolute_time()
- to CACurrentMediaTime()
funkcja.
W przeciwieństwie
NSDate
lubCFAbsoluteTimeGetCurrent()
offsety,mach_absolute_time()
aCACurrentMediaTime()
oparte są na wewnętrznym zegarze gospodarza, precyzyjny, monoatomowego środka, i nie podlega zmianom w czasie zewnętrznych punktów odniesienia, takich jak te spowodowane przez strefy czasowe, czas letni, lub sekundy przestępne.
ObjC
CFTimeInterval startTime = CACurrentMediaTime();
// Do your stuff here
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(@"Total Runtime: %g s", endTime - startTime);
Szybki
let startTime = CACurrentMediaTime()
// Do your stuff here
let endTime = CACurrentMediaTime()
print("Total Runtime: \(endTime - startTime) s")
NSDate
.
W Swift używam:
W moim Macros.swift właśnie dodałem
var startTime = NSDate()
func TICK(){ startTime = NSDate() }
func TOCK(function: String = __FUNCTION__, file: String = __FILE__, line: Int = __LINE__){
println("\(function) Time: \(startTime.timeIntervalSinceNow)\nLine:\(line) File: \(file)")
}
możesz teraz dzwonić w dowolnym miejscu
TICK()
// your code to be tracked
TOCK()
\(-startTime.timeIntervalSinceNow)
(zauważ negatywne)
Wiem, że to jest stara, ale nawet ja znów się po niej wędrowałem, więc pomyślałem, że przedstawię tutaj swoją własną opcję.
Najlepiej jest sprawdzić mój post na blogu na ten temat: Mierzenie czasu w Objective-C: Stoper
Zasadniczo napisałem klasę, która przestaje oglądać w bardzo prosty sposób, ale jest hermetyzowana, więc wystarczy wykonać następujące czynności:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
I kończysz na:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
w dzienniku ...
Ponownie sprawdź mój post, aby uzyskać więcej informacji lub pobierz go tutaj: MMStopwatch.zip
Używam makr opartych na rozwiązaniu Rona .
#define TICK(XXX) NSDate *XXX = [NSDate date]
#define TOCK(XXX) NSLog(@"%s: %f", #XXX, -[XXX timeIntervalSinceNow])
W przypadku wierszy kodu:
TICK(TIME1);
/// do job here
TOCK(TIME1);
zobaczymy w konsoli coś takiego: TIME1: 0.096618
Używam bardzo minimalnej, jednostronicowej implementacji klasy strony zainspirowanej kodem z tego postu na blogu :
#import <mach/mach_time.h>
@interface DBGStopwatch : NSObject
+ (void)start:(NSString *)name;
+ (void)stop:(NSString *)name;
@end
@implementation DBGStopwatch
+ (NSMutableDictionary *)watches {
static NSMutableDictionary *Watches = nil;
static dispatch_once_t OnceToken;
dispatch_once(&OnceToken, ^{
Watches = @{}.mutableCopy;
});
return Watches;
}
+ (double)secondsFromMachTime:(uint64_t)time {
mach_timebase_info_data_t timebase;
mach_timebase_info(&timebase);
return (double)time * (double)timebase.numer /
(double)timebase.denom / 1e9;
}
+ (void)start:(NSString *)name {
uint64_t begin = mach_absolute_time();
self.watches[name] = @(begin);
}
+ (void)stop:(NSString *)name {
uint64_t end = mach_absolute_time();
uint64_t begin = [self.watches[name] unsignedLongLongValue];
DDLogInfo(@"Time taken for %@ %g s",
name, [self secondsFromMachTime:(end - begin)]);
[self.watches removeObjectForKey:name];
}
@end
Korzystanie z niego jest bardzo proste:
[DBGStopwatch start:@"slow-operation"];
na początku[DBGStopwatch stop:@"slow-operation"];
po zakończeniu, aby uzyskać czasMożesz uzyskać naprawdę dobry czas (sekundy. Kilka sekund) za pomocą tej klasy StopWatch. Korzysta z precyzyjnego timera w iPhonie. Korzystanie z NSDate zapewni jedynie dokładność sekundową. Ta wersja jest zaprojektowana specjalnie dla autorelease i celu-c. W razie potrzeby mam również wersję c ++. Możesz znaleźć wersję c ++ tutaj .
StopWatch.h
#import <Foundation/Foundation.h>
@interface StopWatch : NSObject
{
uint64_t _start;
uint64_t _stop;
uint64_t _elapsed;
}
-(void) Start;
-(void) Stop;
-(void) StopWithContext:(NSString*) context;
-(double) seconds;
-(NSString*) description;
+(StopWatch*) stopWatch;
-(StopWatch*) init;
@end
StopWatch.m
#import "StopWatch.h"
#include <mach/mach_time.h>
@implementation StopWatch
-(void) Start
{
_stop = 0;
_elapsed = 0;
_start = mach_absolute_time();
}
-(void) Stop
{
_stop = mach_absolute_time();
if(_stop > _start)
{
_elapsed = _stop - _start;
}
else
{
_elapsed = 0;
}
_start = mach_absolute_time();
}
-(void) StopWithContext:(NSString*) context
{
_stop = mach_absolute_time();
if(_stop > _start)
{
_elapsed = _stop - _start;
}
else
{
_elapsed = 0;
}
NSLog([NSString stringWithFormat:@"[%@] Stopped at %f",context,[self seconds]]);
_start = mach_absolute_time();
}
-(double) seconds
{
if(_elapsed > 0)
{
uint64_t elapsedTimeNano = 0;
mach_timebase_info_data_t timeBaseInfo;
mach_timebase_info(&timeBaseInfo);
elapsedTimeNano = _elapsed * timeBaseInfo.numer / timeBaseInfo.denom;
double elapsedSeconds = elapsedTimeNano * 1.0E-9;
return elapsedSeconds;
}
return 0.0;
}
-(NSString*) description
{
return [NSString stringWithFormat:@"%f secs.",[self seconds]];
}
+(StopWatch*) stopWatch
{
StopWatch* obj = [[[StopWatch alloc] init] autorelease];
return obj;
}
-(StopWatch*) init
{
[super init];
return self;
}
@end
Klasa ma statyczny stopWatch
metodę , która zwraca automatycznie wydany obiekt.
Po wywołaniu start
użyj seconds
metody, aby uzyskać upływ czasu. Zadzwoń start
ponownie, aby go ponownie uruchomić. Lub stop
to zatrzymać. Nadal możesz odczytać czas (połączenie seconds
) w dowolnym momencie po połączeniu stop
.
Przykład w funkcji A (wywołanie czasowe wykonania)
-(void)SomeFunc
{
StopWatch* stopWatch = [StopWatch stopWatch];
[stopWatch Start];
... do stuff
[stopWatch StopWithContext:[NSString stringWithFormat:@"Created %d Records",[records count]]];
}
Używam tego kodu:
#import <mach/mach_time.h>
float TIME_BLOCK(NSString *key, void (^block)(void)) {
mach_timebase_info_data_t info;
if (mach_timebase_info(&info) != KERN_SUCCESS)
{
return -1.0;
}
uint64_t start = mach_absolute_time();
block();
uint64_t end = mach_absolute_time();
uint64_t elapsed = end - start;
uint64_t nanos = elapsed * info.numer / info.denom;
float cost = (float)nanos / NSEC_PER_SEC;
NSLog(@"key: %@ (%f ms)\n", key, cost * 1000);
return cost;
}
Używam tego:
clock_t start, end;
double elapsed;
start = clock();
//Start code to time
//End code to time
end = clock();
elapsed = ((double) (end - start)) / CLOCKS_PER_SEC;
NSLog(@"Time: %f",elapsed);
Ale nie jestem pewien CLOCKS_PER_SEC na iPhonie. Może chcesz to wyłączyć.
Przykład precyzyjnego pomiaru czasu mach_absolute_time()
w Swift 4:
let start = mach_absolute_time()
// do something
let elapsedMTU = mach_absolute_time() - start
var timebase = mach_timebase_info()
if mach_timebase_info(&timebase) == 0 {
let elapsed = Double(elapsedMTU) * Double(timebase.numer) / Double(timebase.denom)
print("render took \(elapsed)")
}
else {
print("timebase error")
}
OK, jeśli Twoim celem jest ustalenie, co możesz naprawić, aby przyspieszyć, to trochę inny cel. Mierzenie czasu potrzebnego na działanie funkcji jest dobrym sposobem, aby dowiedzieć się, czy to, co zrobiłeś, miało znaczenie, ale aby dowiedzieć się, co zrobić , potrzebujesz innej techniki. Oto co polecam i wiem, że możesz to zrobić na iPhone'ach.
Edycja: recenzenci sugerowali, że opracowałem odpowiedź, więc staram się wymyślić krótki sposób, aby to powiedzieć.
Twój ogólny program zajmuje Ci wystarczająco dużo czasu. Załóżmy, że to N sekund.
Zakładasz, że możesz to przyspieszyć. Jedynym sposobem, aby to zrobić, jest sprawienie, aby w tym czasie nie robiła czegoś, co odpowiada m sekundom.
Początkowo nie wiesz, co to jest. Możesz zgadywać, jak wszyscy programiści, ale może to być coś innego. Cokolwiek to jest, oto jak to znaleźć:
Ponieważ ta rzecz, cokolwiek to jest, stanowi ułamek m / N czasu, oznacza to, że jeśli losowo ją zatrzymasz, prawdopodobieństwo wynosi m / N , że złapiesz ją podczas robienia tego. Oczywiście może robić coś innego, ale zatrzymaj to i zobacz, co robi.
Teraz zrób to jeszcze raz. Jeśli zobaczysz, że robi to samo, możesz być bardziej podejrzliwy.
Zrób to 10 razy lub 20. Teraz, gdy zobaczysz, że robi coś szczególnego (bez względu na to, jak to opisujesz) po wielu przerwach, których możesz się pozbyć, wiesz dwie rzeczy. Wiesz z grubsza, jaki ułamek czasu zajmuje, ale bardzo dokładnie wiesz , co naprawić.
Jeśli chcesz również bardzo dokładnie wiedzieć , ile czasu zaoszczędzisz, to proste. Zmierz to wcześniej, napraw i zmierz później. Jeśli jesteś naprawdę rozczarowany, wycofaj się.
Czy widzisz, czym różni się to od pomiaru? Znajduje , nie mierzy . Większość profilowania opiera się na jak najdokładniejszym pomiarze ilości czasu, co jest ważne, i macha ręką w celu ustalenia, co należy naprawić. Profilowanie nie powoduje znalezienia każdego problemu, ale ta metoda odnajduje każdy problem i bolą Cię problemy, których nie znajdziesz.
Oto inny sposób, w Swift, aby to zrobić za pomocą słowa kluczowego odroczenia
func methodName() {
let methodStart = Date()
defer {
let executionTime = Date().timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")
}
// do your stuff here
}
Z dokumentacji Apple'a : Instrukcja odroczenia służy do wykonania kodu tuż przed przeniesieniem kontroli programu poza zakres, w którym pojawia się instrukcja odroczenia.
Jest to podobne do bloku try / wreszcie z zaletą zgrupowania powiązanego kodu.
Używam tego w mojej bibliotece utils ( Swift 4.2 ):
public class PrintTimer {
let start = Date()
let name: String
public init(file: String=#file, line: Int=#line, function: String=#function, name: String?=nil) {
let file = file.split(separator: "/").last!
self.name = name ?? "\(file):\(line) - \(function)"
}
public func done() {
let end = Date()
print("\(self.name) took \((end.timeIntervalSinceReferenceDate - self.start.timeIntervalSinceReferenceDate).roundToSigFigs(5)) s.")
}
}
... następnie wywołaj metodę taką jak:
func myFunctionCall() {
let timer = PrintTimer()
// ...
timer.done()
}
... który z kolei wygląda tak w konsoli po uruchomieniu:
MyFile.swift:225 - myFunctionCall() took 1.8623 s.
Nie tak zwięzłe jak TICK / TOCK powyżej, ale jest wystarczająco jasne, aby zobaczyć, co robi i automatycznie uwzględnia to, co jest mierzone (według pliku, linii na początku metody i nazwy funkcji). Oczywiście, jeśli chciałbym uzyskać więcej szczegółów (np. Jeśli nie tylko odmierzam czas wywołania metody, jak to zwykle bywa, ale zamiast tego mierzę czas w bloku w tej metodzie), mogę dodać parametr „name =" Foo ”do parametru PrintTimer init nazwać to coś oprócz wartości domyślnych.
Skoro chcesz zoptymalizować czas przejścia z jednej strony na drugą w UIWebView, czy to nie znaczy, że naprawdę chcesz zoptymalizować JavaScript używany do ładowania tych stron?
W tym celu spojrzałbym na profilera WebKit, o którym tu mówiono:
http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/
Innym podejściem byłoby rozpoczęcie od wysokiego poziomu i zastanowienie się, jak zaprojektować dane strony internetowe, aby zminimalizować czas ładowania przy użyciu ładowania strony w stylu AJAX zamiast odświeżania całego widoku strony za każdym razem.
struct TIME {
static var ti = mach_timebase_info()
static var k: Double = 1
static var mach_stamp: Double {
if ti.denom == 0 {
mach_timebase_info(&ti)
k = Double(ti.numer) / Double(ti.denom) * 1e-6
}
return Double(mach_absolute_time()) * k
}
static var stamp: Double { return NSDate.timeIntervalSinceReferenceDate() * 1000 }
}
do {
let mach_start = TIME.mach_stamp
usleep(200000)
let mach_diff = TIME.mach_stamp - mach_start
let start = TIME.stamp
usleep(200000)
let diff = TIME.stamp - start
print(mach_diff, diff)
}
Oto rozwiązanie Swift 3 do dzielenia kodu w dowolnym miejscu, aby znaleźć długi proces.
var increment: Int = 0
var incrementTime = NSDate()
struct Instrumentation {
var title: String
var point: Int
var elapsedTime: Double
init(_ title: String, _ point: Int, _ elapsedTime: Double) {
self.title = title
self.point = point
self.elapsedTime = elapsedTime
}
}
var elapsedTimes = [Instrumentation]()
func instrument(_ title: String) {
increment += 1
let incrementedTime = -incrementTime.timeIntervalSinceNow
let newPoint = Instrumentation(title, increment, incrementedTime)
elapsedTimes.append(newPoint)
incrementTime = NSDate()
}
Stosowanie: -
instrument("View Did Appear")
print("ELAPSED TIMES \(elapsedTimes)")
Próbka wyjściowa: -
ELAPSED TIMES [MyApp.SomeViewController.Instrumentation (tytuł: „Start View Did Load”, punkt: 1, elapsedTime: 0.040504038333892822), MyApp.SomeViewController.Instrumentation (tytuł: „Zakończono dodawanie SubViews”, punkt: 2, upłynął: 0,02758501005 MyApp.SomeViewController.Instrumentation (tytuł: „Wyświetl pojawił się”, punkt: 3, upłynął Czas: 0,56564098596572876)]
wiele odpowiedzi jest dziwnych i tak naprawdę nie daje wyniku w milisekundach (ale w sekundach lub cokolwiek innego):
tutaj, czego używam, aby uzyskać MS (MILLISECONDS):
Szybki:
let startTime = NSDate().timeIntervalSince1970 * 1000
// your Swift code
let endTimeMinusStartTime = NSDate().timeIntervalSince1970 * 1000 - startTime
print("time code execution \(endTimeMinStartTime) ms")
Cel C:
double startTime = [[NSDate date] timeIntervalSince1970] * 1000.0;
// your Objective-C code
double endTimeMinusStartTime = [[NSDate date] timeIntervalSince1970] * 1000.0 - startTime;
printf("time code execution %f ms\n", endTimeMinusStartTime );
W przypadku Swift 4 dodaj jako delegata do swojej klasy:
public protocol TimingDelegate: class {
var _TICK: Date?{ get set }
}
extension TimingDelegate {
var TICK: Date {
_TICK = Date()
return(_TICK)!
}
func TOCK(message: String) {
if (_TICK == nil){
print("Call 'TICK' first!")
}
if (message == ""){
print("\(Date().timeIntervalSince(_TICK!))")
}
else{
print("\(message): \(Date().timeIntervalSince(_TICK!))")
}
}
}
Dodaj do naszej klasy:
class MyViewcontroller: UIViewController, TimingDelegate
Następnie dodaj do swojej klasy:
var _TICK: Date?
Jeśli chcesz coś zrobić, zacznij od:
TICK
I zakończ z:
TOCK("Timing the XXX routine")