Як записати час виконання методу рівно в мілісекундах?


222

Чи є спосіб визначити, скільки часу потрібно виконати методу (в мілісекундах)?


2
Ви випадково запитуєте, бо хочете дізнатися, що можна оптимізувати, щоб зробити це швидше?
Майк Данлаве

1
Так, я використовую UIWebView, який завантажує деякі сторінки. Я хочу оптимізувати завантаження сторінок, перевіривши час, яким потрібен метод для завантаження сторінки 1 на сторінку 10.
дан

2
Здається, це дублікат цього питання: stackoverflow.com/questions/889380/…
Бред Ларсон

@BradLarson Хоча це видається дублікатом, на інше питання є кращі відповіді, тобто там видатні відповіді не пропонують використовувати (неправильний) NSDate, але натомість добре пояснює, чому NSDate - це неправильний спосіб зробити для цієї мети.
Томас Темпельман

Відповіді:


437
NSDate *methodStart = [NSDate date];

/* ... Do whatever you need to do ... */

NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);

Швидкий:

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

Простий у використанні та має субмілісекундну точність.


3
@PeterWarbo NSTimeInterval - це typedef з подвійним розміром і визначається як секунди - див. Developer.apple.com/library/mac/#documentation/Cocoa/Reference/…
Ben Lings

5
Ви можете записати це значення за допомогою% f - NSLog ("ExecuTTime =% f", ExecutionTime);
Тоні

1
@Тоні ти забув @,NSLog(@"executionTime = %f", executionTime);
Джон Різельвато

6
Я тільки порівняв NSDateі mach_absolute_time()приблизно на рівні 30 мс. 27 проти 29, 36 проти 39, 43 проти 45. NSDateДля мене було легше використовувати, і результати були досить схожі, щоб не турбувати mach_absolute_time().
король Неван

5
Все, що базується на NSDate, не є безпечним для вимірювання пройденого часу, оскільки час може стрибнути, навіть назад. Набагато безпечнішим способом є використання mach_absolute_time, як показано в багатьох інших відповідях тут. Цього слід сприймати як поганий приклад. Дивіться також відповідну відповідь, яка пояснює це все детальніше: stackoverflow.com/a/30363702/43615
Thomas Tempelmann

252

Ось два однорядкових макроси, які я використовую:

#define TICK   NSDate *startTime = [NSDate date]
#define TOCK   NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])

Використовуйте його так:

TICK;

/* ... Do Some Work Here ... */

TOCK;

13
Ха-ха. Мені це подобається!
Бобмофф

5
Що робить це настільки добре, це те, що тик-так - це така пам'ятна фраза, що ведення журналу майже не потребує думки.
Джон Різельвато

30
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])змушує цю відповідь також повертати, в якій функції використовувався таймер. Я вважаю це корисним, якщо я використовував кнопку TICK TOCK, щоб увімкнути кілька функцій.
golmschenk

3
Чудова ідея @golmschenk! Ви також можете переглянути __PRETTY_FUNCTION__та, __LINE__якщо хочете, більш детальну інформацію.
Рон

50

Для тонкозернистої синхронізації в OS X слід використовувати mach_absolute_time( )оголошені в <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;

Звичайно, застосовуються звичайні застереження щодо дрібнозернистих вимірювань; вам, мабуть, найкраще багато разів викликати тестувану процедуру та усереднювати / приймати мінімум / якусь іншу форму обробки.

Крім того, зауважте, що вам може бути корисніше профілювати роботу програми за допомогою такого інструменту, як Shark. Це не дасть точної інформації про терміни, але покаже, який відсоток часу в програмі витрачається куди, що часто корисніше (але не завжди).


1
Намагаєтесь, щоб це працювало у Свіфті ... будь-яка пропозиція?
zumzum

1
"Один не просто ... конвертувати в Свіфт" - Нед Старк
stonedauwg

@zumzum Дивіться мою відповідь для прикладу цього в Swift.
jbg

23

Є зручна обгортка для mach_absolute_time()- це CACurrentMediaTime()функція.

На відміну від NSDateабо CFAbsoluteTimeGetCurrent()компенсації, mach_absolute_time()і CACurrentMediaTime()базуються на внутрішньому хост-хості, точний, одноатомний захід і не підлягає змінам у зовнішньому довіднику часу, наприклад, спричиненому часовими поясами, економією денного світла або стрибковими секундами.


ObjC

CFTimeInterval startTime = CACurrentMediaTime();
// Do your stuff here
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(@"Total Runtime: %g s", endTime - startTime);

Швидкий

let startTime = CACurrentMediaTime()
// Do your stuff here
let endTime = CACurrentMediaTime()
print("Total Runtime: \(endTime - startTime) s")

3
Я думаю, що ця відповідь заслуговує на більшу кількість відгуків. Це набагато краще, ніж використовувати NSDate.
середній Джо

12

У Swift я використовую:

У своєму Macros.swift я щойно додав

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

тепер ви можете просто зателефонувати куди завгодно

TICK()

// your code to be tracked

TOCK()
  • цей код заснований на перекладі коду Рона на Swift, у нього є кредити
  • Я використовую дату початку на глобальному рівні, будь-які пропозиції щодо покращення вітаються

Це має бути \(-startTime.timeIntervalSinceNow)(зауважте негатив)
Сніговик

9

Я знаю, що це старе, але навіть я знову опинився, коли блукав повз нього, тому подумав, що подаю тут свій варіант.

Найкраще перевірити мою публікацію в блозі на цю тему : Визначення часу в «Objective-C: секундомір»

В основному я написав клас, який перестає дивитися дуже просто, але інкапсульований так, що вам потрібно зробити лише наступне:

[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];

І закінчуєте ви:

MyApp[4090:15203]  -> Stopwatch: [My Timer] runtime: [0.029]

у журналі ...

Ще раз ознайомтеся з моєю публікацією трохи більше або завантажте її тут: MMStopwatch.zip


7

Я використовую макроси на основі рішення Рона .

#define TICK(XXX) NSDate *XXX = [NSDate date]
#define TOCK(XXX) NSLog(@"%s: %f", #XXX, -[XXX timeIntervalSinceNow])

Для рядків коду:

TICK(TIME1);
/// do job here
TOCK(TIME1);

ми побачимо в консолі щось на кшталт: TIME1: 0.096618


Ваша відповідь насправді не так сильно відрізняється від відповіді Рона, а також я якось не бачу, яким чином це краще?
Триларіон

2
Ви не можете використовувати рішення @ Ron в одному контексті двічі. Це основна причина цього макросу.
Сергій Терьохін

4

Я використовую дуже мінімальну реалізацію класу на одній сторінці, натхненну кодом з цієї публікації в блозі :

#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

Використання його дуже просте:

  • просто зателефонуйте [DBGStopwatch start:@"slow-operation"];на початку
  • а потім [DBGStopwatch stop:@"slow-operation"];після фінішу, щоб отримати час

3

Ви можете отримати справді точний час (секунди. Частини секунд) за допомогою цього класу StopWatch. Він використовує високоточний таймер в iPhone. Використання NSDate отримає лише другу точність. Ця версія розроблена спеціально для автоматичного випуску та цілі-c. У мене є версія c ++, якщо потрібно. Ви можете знайти версію c ++ тут .

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

Клас має статику stopWatch метод, який повертає автоматично випущений об'єкт.

Після дзвінка startвикористовуйте secondsметод, щоб отримати минулий час. Зателефонуйте startще раз, щоб перезапустити його. Або stopзупинити це. Ви все ще можете прочитати час (дзвінок seconds) у будь-який час після дзвінка stop.

Приклад у функції (виклик виконання часу)

-(void)SomeFunc
{
   StopWatch* stopWatch = [StopWatch stopWatch];
   [stopWatch Start];

   ... do stuff

   [stopWatch StopWithContext:[NSString stringWithFormat:@"Created %d Records",[records count]]];
}

Ваша "лише секунда точність" невірна. Хоча вся частина NSTimeInterval - це секунди, це подвійна.
Стівен Фішер

3

Я використовую цей код:

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

2

Я використовую це:

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

Але я не впевнений у CLOCKS_PER_SEC на iPhone. Ви можете залишити його.


2
CLOCKS_PER_SEC на iPhone - дико неточне значення.
mxcl

1
Добре знати. Я б скористався відповіддю Метью, якби мені зараз це зробити.
Девід Канарек

2

Приклад тонкозернистої синхронізації з використанням mach_absolute_time()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")
}

2

Гаразд, якщо ваша мета - з'ясувати, що ви можете виправити, щоб зробити це швидше, це трохи інша мета. Вимірювання часу, який займають функції, - це хороший спосіб з’ясувати, чи змінилось те, що ви зробили, але щоб дізнатися, що робити, вам потрібна інша техніка. Це я рекомендую , і я знаю, що ви можете це зробити на iPhone.

Редагувати: Рецензенти запропонували розробити відповідь, тому я намагаюся продумати короткий спосіб сказати це.
Загальна програма займає достатньо годинного часу, щоб вас турбувати. Припустимо, це N секунд.
Ви припускаєте, що можете пришвидшити це. Єдиний спосіб зробити це - змусити його не робити те, що він робить у цей час, що становить м секунди.
Ви спочатку не знаєте, що це за річ. Ви можете здогадатися, як це роблять усі програмісти, але це легко може бути чимось іншим. Як би там не було, ось як його знайти:

Оскільки ця річ, що б вона не була, припадає на частку m / N часу, це означає, що якщо ви її випадково призупинили, то ймовірність m / N, що ви впізнаєте її в акті здійснення цієї речі. Звичайно, це може робити щось інше, але призупиніть це і подивіться, що він робить.
Тепер зробіть це ще раз. Якщо ви побачите, як знову роблять те саме, ви можете бути більш підозрілими.

Зробіть це 10 разів або 20. Тепер, якщо ви бачите, що це робить якусь конкретну річ (незалежно від того, як ви це описуєте) під час декількох пауз, від яких можна позбутися, ви знаєте дві речі. Ви дуже грубо знаєте, яка частина часу займає, але ви точно знаєте , що потрібно виправити.
Якщо ви також хочете точно знати , скільки часу буде заощаджено, це просто. Виміряйте її раніше, зафіксуйте та виміряйте після. Якщо ви справді розчаровані, відмовтеся від виправлення.

Ви бачите, чим це відрізняється від вимірювання? Це знаходження, а не вимірювання . Більшість профілювань ґрунтується на тому, щоб якомога точніше виміряти, скільки часу потрібно, як ніби це важливо, і подати рукою проблему визначення того, що потрібно виправити. Профілювання не знаходить кожної проблеми, але цей метод виявляє кожну проблему, і саме проблеми, які ви не знаєте, шкодять вам.


0

Ось ще один спосіб у Swift зробити це за допомогою ключового слова defer

func methodName() {
  let methodStart = Date()
  defer {
    let executionTime = Date().timeIntervalSince(methodStart)
    print("Execution time: \(executionTime)")
  }
  // do your stuff here
}

З Документів Apple : Оператор відкладання використовується для виконання коду безпосередньо перед перенесенням програмного керування за межі області, у якій з'являється заява відстрочки.

Це схоже на блок "спробувати / остаточно" з перевагою групування відповідного коду.


0

Я використовую це у своїй бібліотеці утиліт ( 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.")
    }
}

... тоді зателефонуйте у такий спосіб, як:

func myFunctionCall() {
    let timer = PrintTimer()
    // ...
    timer.done()
}

... що в свою чергу виглядає так у консолі після запуску:

MyFile.swift:225 - myFunctionCall() took 1.8623 s.

Не настільки стислий, як TICK / TOCK вище, але це досить зрозуміло, щоб побачити, що він робить, і автоматично включає те, що відбувається вчасно (по файлу, рядку на початку методу та назви функції). Очевидно, що якщо я хотів отримати більше деталей (наприклад, якщо я не просто призначаю виклик методу, як це звичайний випадок, але натомість я призначаю блок у межах цього методу), я можу додати параметр "name =" Foo "" у програмі PrintTimer init назвати це чимось, крім за замовчуванням.


-1

Оскільки ви хочете оптимізувати час переміщення з однієї сторінки на іншу в UIWebView, чи не означає це, що ви дійсно шукаєте оптимізувати Javascript, що використовується для завантаження цих сторінок?

З цією метою я б подивився на профілера WebKit, про який говорили тут:

http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/

Іншим підходом було б почати на високому рівні і подумати, як можна розробити відповідні веб-сторінки, щоб мінімізувати час завантаження, використовуючи завантаження сторінки в стилі AJAX, а не оновлюючи весь веб-перегляд щоразу.


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

-1

Ось рішення Swift 3 для розбиття коду де завгодно, щоб знайти тривалий процес.

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

Використання: -

instrument("View Did Appear")

print("ELAPSED TIMES \(elapsedTimes)")

Вибірка зразка: -

РОЗШИРЕННІ ЧАСИ [MyApp.SomeViewController.Instrumentation (назва: "Почати перегляд завантажено", точка: 1, минуло Час: 0.040504038333892822), MyApp.SomeViewController.Інструментація (назва: "Закінчено додаванням субпрозорів", точка: 2, прошло 505, 575, 515, 515, 515, 515, 275) MyApp.SomeViewController.Instrumentation (назва: "Перегляд з'явився", точка: 3, минуло Час: 0.56564098596572876)]


-1

багато відповідей дивні і насправді не дають результату в мілісекундах (але в секундах або що-небудь ще):

ось що я використовую для отримання MS (MILLISECONDS):

Швидкий:

let startTime = NSDate().timeIntervalSince1970 * 1000

// your Swift code

let endTimeMinusStartTime = NSDate().timeIntervalSince1970 * 1000 - startTime
print("time code execution \(endTimeMinStartTime) ms")

Завдання-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 );

-1

Для Swift 4 додайте в якості делегата до свого класу:

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

Додати до нашого класу:

class MyViewcontroller: UIViewController, TimingDelegate

Потім додайте до свого класу:

var _TICK: Date?

Коли ви хочете щось вчасно, почніть з:

TICK

І закінчуємо:

TOCK("Timing the XXX routine")

Чи читали ви відповіді та коментарі? Не використовуйте для цього Дата!
мат
Використовуючи наш веб-сайт, ви визнаєте, що прочитали та зрозуміли наші Політику щодо файлів cookie та Політику конфіденційності.
Licensed under cc by-sa 3.0 with attribution required.