Có cách nào để xác định thời gian một phương thức cần thực hiện (tính bằng mili giây) không?
Có cách nào để xác định thời gian một phương thức cần thực hiện (tính bằng mili giây) không?
Câu trả lời:
NSDate *methodStart = [NSDate date];
/* ... Do whatever you need to do ... */
NSDate *methodFinish = [NSDate date];
NSTimeInterval executionTime = [methodFinish timeIntervalSinceDate:methodStart];
NSLog(@"executionTime = %f", executionTime);
Nhanh:
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)")
Dễ sử dụng và có độ chính xác dưới một phần nghìn giây.
NSLog(@"executionTime = %f", executionTime);
NSDate
và mach_absolute_time()
ở mức khoảng 30ms. 27 so với 29, 36 so với 39, 43 so với 45. NSDate
dễ sử dụng hơn đối với tôi và kết quả tương tự nhau, không quá bận tâm mach_absolute_time()
.
Đây là hai macro một dòng mà tôi sử dụng:
#define TICK NSDate *startTime = [NSDate date]
#define TOCK NSLog(@"Time: %f", -[startTime timeIntervalSinceNow])
Sử dụng nó như thế này:
TICK;
/* ... Do Some Work Here ... */
TOCK;
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])
làm cho câu trả lời này cũng trả về chức năng mà bộ định thời đã được sử dụng. Tôi thấy điều này hữu ích nếu tôi sử dụng TICK TOCK để tính thời gian cho nhiều chức năng.
__PRETTY_FUNCTION__
và __LINE__
nếu bạn muốn biết thêm thông tin chi tiết.
Để tính thời gian chi tiết trên OS X, bạn nên sử dụng mach_absolute_time( )
khai báo trong <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;
Tất nhiên, hãy cẩn thận thông thường về các phép đo hạt mịn áp dụng; có lẽ bạn tốt nhất nên gọi thói quen được kiểm tra nhiều lần và tính trung bình / lấy tối thiểu / một số hình thức xử lý khác.
Ngoài ra, xin lưu ý rằng bạn có thể thấy hữu ích hơn khi lập hồ sơ cho ứng dụng của mình đang chạy bằng một công cụ như Shark. Điều này sẽ không cung cấp cho bạn thông tin chính xác về thời gian, nhưng nó sẽ cho bạn biết bao nhiêu phần trăm thời gian của ứng dụng đang được sử dụng ở đâu, thường hữu ích hơn (nhưng không phải lúc nào cũng vậy).
Có một trình bao bọc tiện lợi cho mach_absolute_time()
- đó là một CACurrentMediaTime()
chức năng.
Không giống
NSDate
hoặcCFAbsoluteTimeGetCurrent()
bù đắp,mach_absolute_time()
vàCACurrentMediaTime()
dựa trên đồng hồ máy chủ bên trong, một biện pháp chính xác, đơn trị và không chịu sự thay đổi trong tham chiếu thời gian bên ngoài, chẳng hạn như các mốc gây ra bởi múi giờ, tiết kiệm ánh sáng ban ngày hoặc giây nhuận.
ObjC
CFTimeInterval startTime = CACurrentMediaTime();
// Do your stuff here
CFTimeInterval endTime = CACurrentMediaTime();
NSLog(@"Total Runtime: %g s", endTime - startTime);
Nhanh
let startTime = CACurrentMediaTime()
// Do your stuff here
let endTime = CACurrentMediaTime()
print("Total Runtime: \(endTime - startTime) s")
NSDate
.
Trong Swift, tôi đang sử dụng:
Trong Macros.swift của tôi, tôi vừa thêm
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)")
}
bây giờ bạn có thể gọi bất cứ nơi nào
TICK()
// your code to be tracked
TOCK()
\(-startTime.timeIntervalSinceNow)
(chú ý tiêu cực)
Tôi biết đây là một cái cũ nhưng thậm chí tôi còn thấy mình lang thang qua nó một lần nữa, vì vậy tôi nghĩ rằng tôi đã gửi tùy chọn của riêng mình ở đây.
Đặt cược tốt nhất là kiểm tra bài đăng trên blog của tôi về điều này: Thời gian mọi thứ trong Objective-C: Đồng hồ bấm giờ
Về cơ bản, tôi đã viết một lớp không ngừng xem theo cách rất cơ bản nhưng được gói gọn để bạn chỉ cần làm như sau:
[MMStopwatchARC start:@"My Timer"];
// your work here ...
[MMStopwatchARC stop:@"My Timer"];
Và bạn kết thúc với:
MyApp[4090:15203] -> Stopwatch: [My Timer] runtime: [0.029]
trong nhật ký ...
Một lần nữa, hãy xem bài viết của tôi để biết thêm một chút hoặc tải xuống tại đây: MMStopwatch.zip
Tôi sử dụng macro dựa trên giải pháp của Ron .
#define TICK(XXX) NSDate *XXX = [NSDate date]
#define TOCK(XXX) NSLog(@"%s: %f", #XXX, -[XXX timeIntervalSinceNow])
Đối với các dòng mã:
TICK(TIME1);
/// do job here
TOCK(TIME1);
chúng ta sẽ thấy trong bảng điều khiển giống như: TIME1: 0.096618
Tôi sử dụng rất tối thiểu, một lớp thực hiện được lấy cảm hứng từ mã từ bài đăng trên blog này :
#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
Cách sử dụng của nó rất đơn giản:
[DBGStopwatch start:@"slow-operation"];
lúc đầu[DBGStopwatch stop:@"slow-operation"];
khi kết thúc, để có được thời gianBạn có thể có được thời gian thực sự tốt (giây.parts giây) bằng cách sử dụng lớp StopWatch này. Nó sử dụng bộ đếm thời gian chính xác cao trong iPhone. Sử dụng NSDate sẽ chỉ giúp bạn có độ chính xác thứ hai. Phiên bản này được thiết kế dành riêng cho autorelease và object-c. Tôi có một phiên bản c ++ nếu cần. Bạn có thể tìm thấy phiên bản c ++ tại đây .
Đồng hồ bấm giờ.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
Đồng hồ bấm giờ.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
Lớp này có một stopWatch
phương thức tĩnh trả về một đối tượng tự động.
Khi bạn gọi start
, sử dụng seconds
phương pháp để có được thời gian trôi qua. Gọi start
lại để khởi động lại nó. Hoặc stop
để ngăn chặn nó. Bạn vẫn có thể đọc thời gian (cuộc gọi seconds
) bất cứ lúc nào sau khi gọi stop
.
Ví dụ trong Hàm (Gọi thời gian thực hiện)
-(void)SomeFunc
{
StopWatch* stopWatch = [StopWatch stopWatch];
[stopWatch Start];
... do stuff
[stopWatch StopWithContext:[NSString stringWithFormat:@"Created %d Records",[records count]]];
}
Tôi sử dụng mã này:
#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;
}
Tôi sử dụng cái này:
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);
Nhưng tôi không chắc chắn về CLOCKS_PER_SEC trên iPhone. Bạn có thể muốn để nó đi.
Một ví dụ về thời gian hạt mịn sử dụng mach_absolute_time()
trong 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, nếu mục tiêu của bạn là tìm ra những gì bạn có thể sửa chữa để làm cho nó nhanh hơn, thì đó là một mục tiêu hơi khác. Đo thời gian mà các chức năng sử dụng là một cách tốt để tìm hiểu xem những gì bạn đã làm có sự khác biệt hay không, nhưng để tìm ra những gì bạn cần làm một kỹ thuật khác. Đây là những gì tôi khuyên bạn nên và tôi biết bạn có thể làm điều đó trên iPhone.
Chỉnh sửa: Người phản biện đề nghị tôi xây dựng câu trả lời, vì vậy tôi đang cố gắng nghĩ ra một cách ngắn gọn để nói.
Chương trình tổng thể của bạn mất đủ thời gian đồng hồ để làm phiền bạn. Giả sử đó là N giây.
Bạn đang cho rằng bạn có thể tăng tốc nó. Cách duy nhất bạn có thể làm là bằng cách khiến nó không làm điều gì đó đang làm trong thời gian đó, chiếm m giây.
Ban đầu bạn không biết thứ đó là gì. Bạn có thể đoán, như tất cả các lập trình viên làm, nhưng nó có thể dễ dàng là một cái gì đó khác. Dù đó là gì, đây là cách bạn có thể tìm thấy nó:
Vì điều đó, bất kể là gì, chiếm một phần m / N của thời gian, điều đó có nghĩa là nếu bạn tạm dừng nó một cách ngẫu nhiên thì xác suất là m / N mà bạn sẽ nắm bắt được trong hành động thực hiện điều đó. Tất nhiên nó có thể đang làm một cái gì đó khác, nhưng tạm dừng nó và xem những gì nó đang làm.
Bây giờ làm lại. Nếu bạn thấy nó làm điều tương tự một lần nữa, bạn có thể nghi ngờ hơn.
Thực hiện 10 lần hoặc 20. Bây giờ nếu bạn thấy nó làm một số việc cụ thể (bất kể bạn mô tả nó như thế nào) trên nhiều lần tạm dừng, bạn có thể thoát khỏi, bạn biết hai điều. Bạn biết rất nhiều khoảng thời gian cần thiết, nhưng bạn biết rất chính xác những gì cần khắc phục.
Nếu bạn cũng muốn biết chính xác bao nhiêu thời gian sẽ được tiết kiệm, điều đó thật dễ dàng. Đo nó trước, sửa chữa nó, và đo nó sau. Nếu bạn thực sự thất vọng, hãy khắc phục.
Bạn có thấy điều này khác với đo lường như thế nào không? Đó là tìm kiếm, không đo lường . Hầu hết các hồ sơ dựa trên việc đo lường chính xác nhất có thể bao nhiêu thời gian, như thể đó là điều quan trọng, và giải quyết vấn đề xác định những gì cần phải sửa. Hồ sơ không tìm thấy mọi vấn đề, nhưng phương pháp này tìm thấy mọi vấn đề và đó là những vấn đề bạn không thấy làm tổn thương bạn.
Đây là một cách khác, trong Swift, để làm điều đó bằng cách sử dụng từ khóa defer
func methodName() {
let methodStart = Date()
defer {
let executionTime = Date().timeIntervalSince(methodStart)
print("Execution time: \(executionTime)")
}
// do your stuff here
}
Từ tài liệu của Apple : Một câu lệnh hoãn được sử dụng để thực thi mã ngay trước khi chuyển điều khiển chương trình ra ngoài phạm vi mà câu lệnh hoãn lại xuất hiện.
Điều này tương tự như một khối thử / cuối cùng với lợi thế là có mã liên quan được nhóm lại.
Tôi sử dụng điều này trong thư viện utils của tôi ( 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.")
}
}
... sau đó gọi một phương thức như:
func myFunctionCall() {
let timer = PrintTimer()
// ...
timer.done()
}
... mà lần lượt trông như thế này trong bảng điều khiển sau khi chạy:
MyFile.swift:225 - myFunctionCall() took 1.8623 s.
Không ngắn gọn như TICK / TOCK ở trên, nhưng đủ rõ ràng để xem những gì nó đang làm và tự động bao gồm những gì đang được tính thời gian (theo tệp, dòng khi bắt đầu phương thức và tên hàm). Rõ ràng nếu tôi muốn biết thêm chi tiết (ví dụ: nếu tôi không chỉ định thời gian cho một cuộc gọi phương thức như trường hợp thông thường mà thay vào đó là định thời một khối trong phương thức đó), tôi có thể thêm tham số "name =" Foo "trên initTimer init để đặt tên cho nó một cái gì đó bên cạnh các mặc định.
Vì bạn muốn tối ưu hóa thời gian di chuyển từ trang này sang trang khác trong UIWebView, điều đó không có nghĩa là bạn thực sự đang tìm cách tối ưu hóa Javascript được sử dụng trong việc tải các trang này?
Cuối cùng, tôi nhìn vào một hồ sơ WebKit như đã nói ở đây:
http://www.alertdebugging.com/2009/04/29/building-a-better-javascript-profiler-with-webkit/
Một cách tiếp cận khác là bắt đầu ở mức cao và suy nghĩ làm thế nào bạn có thể thiết kế các trang web được đề cập để giảm thiểu thời gian tải bằng cách sử dụng tải trang kiểu AJAX thay vì làm mới toàn bộ webview mỗi lần.
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)
}
Đây là một giải pháp Swift 3 để chia đôi mã ở bất cứ đâu để tìm một quy trình chạy dài.
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()
}
Sử dụng: -
instrument("View Did Appear")
print("ELAPSED TIMES \(elapsedTimes)")
Đầu ra mẫu: -
THỜI GIAN ELAPSED [MyApp.SomeViewCont điều khiển MyApp.SomeViewControll.Instrumentation (tiêu đề: "Xem đã xuất hiện", điểm: 3, elapsedTime: 0.56564098596572876)]
nhiều câu trả lời là lạ và không thực sự cho kết quả tính bằng mili giây (nhưng tính bằng giây hoặc bất cứ điều gì khác):
đây là những gì tôi sử dụng để có được MS (MILLISECONDS):
Nhanh:
let startTime = NSDate().timeIntervalSince1970 * 1000
// your Swift code
let endTimeMinusStartTime = NSDate().timeIntervalSince1970 * 1000 - startTime
print("time code execution \(endTimeMinStartTime) ms")
Mục tiêu-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 );
Đối với Swift 4, hãy thêm làm Đại biểu cho lớp của bạn:
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!))")
}
}
}
Thêm vào lớp của chúng tôi:
class MyViewcontroller: UIViewController, TimingDelegate
Sau đó thêm vào lớp của bạn:
var _TICK: Date?
Khi bạn muốn thời gian một cái gì đó, hãy bắt đầu với:
TICK
Và kết thúc bằng:
TOCK("Timing the XXX routine")