Как протоколировать время выполнения метода точно в миллисекундах?

Есть ли способ определить, сколько времени нужно выполнить методу (в миллисекундах)?

Ответ 1

NSDate *methodStart = [NSDate date];

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

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

Swift:

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

Прост в использовании и имеет субмиллисекундную точность.

Ответ 2

Вот два однострочных макроса, которые я использую:

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

Используйте его следующим образом:

TICK;

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

TOCK;

Ответ 3

Для мелкозернистой синхронизации в 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. Это не даст вам точной информации о времени, но она сообщит вам, какой процент времени приложения тратится там, где часто бывает полезно (но не всегда).

Ответ 4

В 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()
  • этот код основан на переводе кода Ron на Swift, у него есть кредиты
  • Я использую дату начала на глобальном уровне, любые предложения по улучшению приветствуются

Ответ 5

Я знаю, что это старый, но даже я обнаружил, что снова блуждаю по нему, поэтому я подумал, что я напишу свой вариант здесь.

Лучше всего проверить мой пост в блоге по этому поводу: Сроки в Objective-C: секундомер

В принципе, я написал класс, который перестает смотреть очень простым способом, но инкапсулирован так, что вам нужно только сделать следующее:

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

И вы получите:

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

в журнале...

Снова, просмотрите мой пост или немного загрузите его здесь: MMStopwatch.zip

Ответ 6

Я использую макросы на основе решения Рона.

#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

Ответ 7

Я использую очень минимальную, одну реализацию класса страницы, вдохновленную кодом из этого сообщения в блоге:

#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"]; после финиша, чтобы получить время

Ответ 8

Вы можете получить действительно точное время (секунды. секунд), используя этот класс StopWatch. Он использует высокоточный таймер в iPhone. Использование NSDate даст вам только вторую точность. Эта версия предназначена специально для авторекламы и objective-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]]];
}

Ответ 9

Хорошо, если ваша цель - выяснить, что вы можете исправить, чтобы сделать это быстрее, это немного другая цель. Измерение времени, которое занимают функции, - хороший способ выяснить, изменилось ли то, что вы сделали, но чтобы узнать, что делать, вам нужна другая техника. Это то, что я рекомендую, и я знаю, что вы можете сделать это на iPhone.

Изменение: рецензенты предложили мне разработать ответ, поэтому я пытаюсь придумать краткий способ сказать это.
Ваша общая программа занимает достаточно времени, чтобы беспокоить вас. Предположим, что N секунд.
Вы предполагаете, что можете ускорить это. Единственный способ сделать это - заставить его не делать то, что он делал за это время, что составляет m секунд.
Вы изначально не знаете, что это за вещь. Вы можете догадаться, как и все программисты, но это может быть что-то другое. Что бы это ни было, вот как это можно найти:

Поскольку эта вещь, какой бы она ни была, учитывает долю m/N времени, это означает, что если вы остановите ее наугад, вероятность того, что вы это поймете, будет равна m/N. Конечно, он может делать что-то еще, но сделайте паузу и посмотрите, что он делает.
Теперь сделай это снова. Если вы видите, что он делает то же самое снова, вы можете быть более подозрительным.

Сделайте это 10 раз или 20. Теперь, если вы видите, что он делает какую-то конкретную вещь (независимо от того, как вы ее описываете) в нескольких паузах, от которой вы можете избавиться, вы знаете две вещи. Вы очень точно знаете, сколько времени это займет, но вы точно знаете, что нужно исправить.
Если вы также хотите точно знать, сколько времени будет сэкономлено, это легко. Измерьте это прежде, исправьте это, и измерьте это после. Если вы действительно разочарованы, отмените исправление.

Вы видите, как это отличается от измерения? Это нахождение, а не измерение. Большая часть профилирования основана на том, чтобы как можно точнее измерить, сколько времени уходит, как если бы это было важно, и решает проблему определения того, что необходимо исправить. Профилирование не находит всех проблем, но этот метод находит все проблемы, и те проблемы, которые вы не находите, причиняют вам боль.

Ответ 10

Я использую это:

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. Возможно, вы захотите оставить его.

Ответ 11

Я использую этот код:

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

Ответ 12

Пример точной синхронизации с использованием 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")
}

Ответ 13

Для mach_absolute_time() есть удобная оболочка - это CACurrentMediaTime().

В отличие от NSDate или CFAbsoluteTimeGetCurrent(), mach_absolute_time() и CACurrentMediaTime() основаны на внутренних часах хоста, точной одноатомной мере и не подвержены изменениям во внешнем 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")

Ответ 14

Вот еще один способ, в Swift, сделать это, используя ключевое слово defer

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

От Apple docs: Оператор defer используется для выполнения кода непосредственно перед передачей управления программой за пределы области действия, которая появляется в заявлении об отсрочке.

Это похоже на блок try/finally с тем преимуществом, что связанный с ним код сгруппирован.

Ответ 15

Поскольку вы хотите оптимизировать время перехода от одной страницы к другой в UIWebView, не означает ли вы, что вы действительно хотите оптимизировать Javascript, используемый при загрузке этих страниц?

С этой целью я бы посмотрел на профайлер WebKit, о котором здесь говорили:

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

Другим подходом было бы начать на высоком уровне и подумать над тем, как вы можете проектировать рассматриваемые веб-страницы, чтобы минимизировать время загрузки, используя загрузку страницы стиля AJAX, а не каждый раз обновляя весь веб-просмотр.

Ответ 16

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

Ответ 17

Здесь быстрое решение для дешифрования кода в любом месте, чтобы найти длительный процесс.

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(название: "Start View Did Load", point: 1, elapsedTime: 0.040504038333892822), MyApp.SomeViewController.Instrumentation(название: "Готовое добавление SubViews", point: 2, elapsedTime: 0.010585010051727295), MyApp.SomeViewController.Instrumentation(название: "Вид появился", точка: 3, истекшее время: 0.56564098596572876)]

Ответ 18

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

вот что я использую, чтобы получить MS (миллисекунды):

Swift:

let startTime = NSDate().timeIntervalSince1970 * 1000

// your Swift code

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

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

Ответ 19

Для 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")

Ответ 20

Я использую это в моей библиотеке утилит (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 назвать это что-то помимо значений по умолчанию.