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

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

19 ответов


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

легкий для использования и имеет точность sub-миллисекунды.


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

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

используйте его так:

TICK;

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

TOCK;

для точного времени на 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;

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

кроме того, обратите внимание, что вы можете найти его более полезным профиль приложения, используя такое средство, как Акулий. Это не даст вам точную информация о времени, но она скажет вам, что процент времени приложения тратится где, что часто более полезно (но не всегда).


в Swift, я использую:

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

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

лучше всего проверить мой пост в блоге на этом: синхронизация вещей в Objective-C: секундомер

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

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

и вы в конечном итоге с:

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

в бревно...

опять же, проверьте мой пост для немного больше или скачать его здесь: MMStopwatch.zip


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

#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


вы можете узнать действительно точное время (секунды.части секунд), используя этот класс секундомера. Он использует высокоточный таймер в iPhone. Используя NSDate, вы получите только вторую(ые) точность. Эта версия разработана специально для autorelease и objective-c. У меня также есть версия c++, если это необходимо. вы можете найти версию c++ вот!--14-->.

секундомер.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

секундомер.м

#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 метод, который возвращает объект autoreleased.

как только вы позвоните 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]]];
}

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

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

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


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

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. Может, ты захочешь оставить это.


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

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

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

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

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

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


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

вот еще один способ, в Swift, сделать это с помощью ключевого слова defer

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

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

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


вот 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.Контрольно-измерительные приборы(название: "Start View Did Load", пункт: 1, истекшее время: 0.040504038333892822), Приложение myapp.SomeViewController.Контрольно-измерительные приборы(название: "готовое добавление SubViews", пункт: 2, истекшее время: 0.010585010051727295), Приложение myapp.SomeViewController.Инструментирование (название: "вид появился", точка: 3, прошедшее время: 0,56564098596572876)]


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

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

Свифт:

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

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

Я использую это в моей библиотеке 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.")
    }
}

... затем вызовите метод, подобный:

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

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

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

не так лаконично, как тик / ток выше, но достаточно ясно, чтобы увидеть, что он делает, и автоматически включает в себя то, что приурочено (по файлу, строке в начале метода и имени функции). Очевидно, если бы я хотел больше деталей (например, если я не просто синхронизация вызова метода, как обычно, но вместо этого я синхронизирую блок внутри этого метода), я могу добавить параметр"name=" Foo" " в init PrintTimer, чтобы назвать его чем-то помимо значений по умолчанию.


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