Чи є спосіб визначити, скільки часу потрібно виконати методу (в мілісекундах)?
Чи є спосіб визначити, скільки часу потрібно виконати методу (в мілісекундах)?
Відповіді:
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)")
Простий у використанні та має субмілісекундну точність.
NSLog(@"executionTime = %f", executionTime);
NSDate
і mach_absolute_time()
приблизно на рівні 30 мс. 27 проти 29, 36 проти 39, 43 проти 45. NSDate
Для мене було легше використовувати, і результати були досить схожі, щоб не турбувати mach_absolute_time()
.
Ось два однорядкових макроси, які я використовую:
#define TICK NSDate *startTime = [NSDate date]
#define TOCK NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])
Використовуйте його так:
TICK;
/* ... Do Some Work Here ... */
TOCK;
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])
змушує цю відповідь також повертати, в якій функції використовувався таймер. Я вважаю це корисним, якщо я використовував кнопку TICK TOCK, щоб увімкнути кілька функцій.
__PRETTY_FUNCTION__
та, __LINE__
якщо хочете, більш детальну інформацію.
Для тонкозернистої синхронізації в 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. Це не дасть точної інформації про терміни, але покаже, який відсоток часу в програмі витрачається куди, що часто корисніше (але не завжди).
Є зручна обгортка для 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")
NSDate
.
У 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()
\(-startTime.timeIntervalSinceNow)
(зауважте негатив)
Я знаю, що це старе, але навіть я знову опинився, коли блукав повз нього, тому подумав, що подаю тут свій варіант.
Найкраще перевірити мою публікацію в блозі на цю тему : Визначення часу в «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
Я використовую дуже мінімальну реалізацію класу на одній сторінці, натхненну кодом з цієї публікації в блозі :
#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"];
після фінішу, щоб отримати часВи можете отримати справді точний час (секунди. Частини секунд) за допомогою цього класу 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]]];
}
Я використовую цей код:
#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;
}
Я використовую це:
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. Ви можете залишити його.
Приклад тонкозернистої синхронізації з використанням 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")
}
Гаразд, якщо ваша мета - з'ясувати, що ви можете виправити, щоб зробити це швидше, це трохи інша мета. Вимірювання часу, який займають функції, - це хороший спосіб з’ясувати, чи змінилось те, що ви зробили, але щоб дізнатися, що робити, вам потрібна інша техніка. Це я рекомендую , і я знаю, що ви можете це зробити на iPhone.
Редагувати: Рецензенти запропонували розробити відповідь, тому я намагаюся продумати короткий спосіб сказати це.
Загальна програма займає достатньо годинного часу, щоб вас турбувати. Припустимо, це N секунд.
Ви припускаєте, що можете пришвидшити це. Єдиний спосіб зробити це - змусити його не робити те, що він робить у цей час, що становить м секунди.
Ви спочатку не знаєте, що це за річ. Ви можете здогадатися, як це роблять усі програмісти, але це легко може бути чимось іншим. Як би там не було, ось як його знайти:
Оскільки ця річ, що б вона не була, припадає на частку m / N часу, це означає, що якщо ви її випадково призупинили, то ймовірність m / N, що ви впізнаєте її в акті здійснення цієї речі. Звичайно, це може робити щось інше, але призупиніть це і подивіться, що він робить.
Тепер зробіть це ще раз. Якщо ви побачите, як знову роблять те саме, ви можете бути більш підозрілими.
Зробіть це 10 разів або 20. Тепер, якщо ви бачите, що це робить якусь конкретну річ (незалежно від того, як ви це описуєте) під час декількох пауз, від яких можна позбутися, ви знаєте дві речі. Ви дуже грубо знаєте, яка частина часу займає, але ви точно знаєте , що потрібно виправити.
Якщо ви також хочете точно знати , скільки часу буде заощаджено, це просто. Виміряйте її раніше, зафіксуйте та виміряйте після. Якщо ви справді розчаровані, відмовтеся від виправлення.
Ви бачите, чим це відрізняється від вимірювання? Це знаходження, а не вимірювання . Більшість профілювань ґрунтується на тому, щоб якомога точніше виміряти, скільки часу потрібно, як ніби це важливо, і подати рукою проблему визначення того, що потрібно виправити. Профілювання не знаходить кожної проблеми, але цей метод виявляє кожну проблему, і саме проблеми, які ви не знаєте, шкодять вам.
Ось ще один спосіб у Swift зробити це за допомогою ключового слова defer
func methodName() {
let methodStart = Date()
defer {
let executionTime = Date().timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")
}
// do your stuff here
}
З Документів Apple : Оператор відкладання використовується для виконання коду безпосередньо перед перенесенням програмного керування за межі області, у якій з'являється заява відстрочки.
Це схоже на блок "спробувати / остаточно" з перевагою групування відповідного коду.
Я використовую це у своїй бібліотеці утиліт ( 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 назвати це чимось, крім за замовчуванням.
Оскільки ви хочете оптимізувати час переміщення з однієї сторінки на іншу в UIWebView, чи не означає це, що ви дійсно шукаєте оптимізувати Javascript, що використовується для завантаження цих сторінок?
З цією метою я б подивився на профілера WebKit, про який говорили тут:
http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/
Іншим підходом було б почати на високому рівні і подумати, як можна розробити відповідні веб-сторінки, щоб мінімізувати час завантаження, використовуючи завантаження сторінки в стилі AJAX, а не оновлюючи весь веб-перегляд щоразу.
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 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)]
багато відповідей дивні і насправді не дають результату в мілісекундах (але в секундах або що-небудь ще):
ось що я використовую для отримання 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 );
Для 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")