メソッドの実行時間を正確にミリ秒単位で記録する方法は?


222

メソッドの実行に必要な時間(ミリ秒単位)を決定する方法はありますか?


2
あなたがそれをより速くするために最適化できるものを見つけたいので、あなたはたまたま尋ねていますか?
Mike Dunlavey、2010年

1
はい、一部のページを読み込んでいるUIWebViewを使用しています。メソッドがページ1からページ10をロードするのに必要な時間をチェックすることにより、ページロードを最適化したい
dan

2
これはこの質問の複製のようです:stackoverflow.com/questions/889380/…–
Brad Larson

@BradLarson重複しているように見えますが、他の質問の方が良い答えがあります。つまり、顕著な答えがNSDateの使用(間違った)を示唆していない代わりに、NSDateがこの目的で間違った方法である理由をよく説明しています。
Thomas Tempelmann、2016年

回答:


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はdoubleのtypedefであり、秒として定義されます-developer.apple.com/library/mac/#documentation/Cocoa/Reference/…を参照して
Ben Lings

5
この値は、%f-NSLog( "executionTime =%f"、executionTime);で記録できます。
Tony

1
@ Tony、@を忘れたNSLog(@"executionTime = %f", executionTime);
John Riselvato 2013

6
私はちょうど30msレベルで比較NSDatemach_absolute_time()ました。27対29、36対39、43対45のNSDate方が私には使いやすく、結果はと気にしないほど十分に似ていましたmach_absolute_time()
ネヴァンキング2013

5
NSDateに基づくものは、時間が逆方向にさえジャンプする可能性があるため、経過時間の測定には安全ではありません。ここで他の多くの回答に示されているように、より安全な方法はmach_absolute_timeを使用することです。これは悪い例であるために反対票を投じるべきです。:また、より詳細にこのすべてを説明し、関連の回答を参照してくださいstackoverflow.com/a/30363702/43615
トーマスTempelmann

252

ここに私が使用する2つの1行のマクロがあります:

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

次のように使用します。

TICK;

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

TOCK;

13
はは。私はそれが好きです!
bobmoff 2013年

5
これがとても良いことは、tick-tockが非常に記憶に残るフレーズであるため、ロギングにはほとんど何も必要ないということです。
John Riselvato、2014

30
#define TOCK NSLog(@"%s Time: %f", __func__, -[startTime timeIntervalSinceNow])この答えは、タイマーが使用された関数も返すようにします。TICKTOCKを使用して複数の関数の時間を計った場合、これは便利です。
golmschenk 2014年

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
これをSwiftで動作させるようにしようとしています...何か提案はありますか?
zumzum 2015年

1
「人は単に...スイフトに変換しない」-ネッド・スターク
stonedauwg

@zumzum Swiftでこれを行う例については、私の答えを参照してください。
jbg 2018

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に変換されたRonのコードに基づいています。彼はクレジットを持っています
  • 私はグローバルレベルで開始日を使用しています。改善の提案は大歓迎です

これは\(-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


あなたの答えはロンの答えとそれほど違いはなく、私はどういうわけかそれがどのように優れているのか見落としていますか?
Trilarion 2016年

2
@Ronのソリューションを1つのコンテキスト内で2回使用することはできません。これがこのマクロの主な理由です。
Sergey Teryokhin、2016年

4

私はこのブログ投稿のコードに触発された非常に最小限の1ページクラス実装を使用しています

#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クラスを使用すると、非常に細かいタイミング(seconds.parts of seconds)を取得できます。iPhoneの高精度タイマーを使用しています。NSDateを使用すると、秒の精度しか得られません。このバージョンは、自動リリースとobjective-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、自動解放されたオブジェクトを返す静的メソッドがあります。

を呼び出したらstartsecondsメソッドを使用して経過時間を取得します。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);

しかし、iPhoneのCLOCKS_PER_SECについてはよくわかりません。オフにしておくことをお勧めします。


2
iPhoneのCLOCKS_PER_SECは、非常に不正確な値です。
mxcl 2011年

1
知っておくと良い。これを今しなければならなかったら、私はマシューの答えを使うでしょう。
David Kanarek

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

OK、あなたの目的がそれをより速くするためにあなたが修正できるものを見つけることであるなら、それは少し異なる目標です。関数がかかる時間を測定することは、自分が何をしたかが違いを生んでいるかどうかを知るための良い方法ですが、何をすべきかを知るためには別のテクニックが必要です。これが私がお勧めする方法です。iPhoneでできることは知っています。

編集:査読者は私が答えを詳しく説明することを提案したので、私はそれを言う簡単な方法を考えようとしています。
あなたの全体的なプログラムはあなたを悩ませるために十分なクロック時間を必要とします。それがN秒だとします。
あなたはあなたがそれをスピードアップできると仮定しています。あなたがそれを行うことができる唯一の方法は、m秒を考慮して、その時間に行っていることを行わないようにすることです。
最初はそれが何であるかを知りません。すべてのプログラマーがそうであるように、あなたは推測することができますが、それは簡単に他のものになる可能性があります。それが何であれ、それを見つける方法は次のとおりです。

そのことは、それが何であれ、時間の分数m / Nを占めるので、ランダムに一時停止すると、確率でm / Nになり、そのことを実行する過程でそれをキャッチします。もちろん、それは何か他のことをしているかもしれませんが、それを一時停止して、それが何をしているかを見てください。
もう一度やり直してください。それが同じことをもう一度実行しているのを見た場合は、さらに疑わしいことがあります。

それを10回、つまり20回実行します。今度は、特定のことを(どのように説明したかに関係なく)複数の一時停止で実行して、取り除くことができる場合、2つのことがわかります。あなたはそれがどのくらいの時間を要するかを大まかに知っていますが、何を修正すべきかを非常に正確に知っています。どれだけの時間を節約できるかを正確
に知りたいのなら、それは簡単です。前に測定して修正し、後で測定します。本当にがっかりしている場合は、修正を取り消してください。

これが測定とどのように違うかわかりますか?それはだ測定、ない見つけます。ほとんどのプロファイリングは、まるでそれが重要であるかのように、どれだけの時間がかかるかを可能な限り正確に測定することに基づいており、修正する必要があるものを特定する問題を手振りします。プロファイリングですべての問題が見つかるわけではありませんが、この方法ではすべての問題が見つかるので、気にならないのはあなたが気づかない問題です。


0

Swiftで、deferキーワードを使用してそれを行う別の方法があります

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

Appleのドキュメントよりdeferステートメントは、deferステートメントが出現するスコープの外でプログラム制御を転送する直前にコードを実行するために使用されます。

これはtry / finallyブロックに似ており、関連するコードがグループ化されるという利点があります。


0

私はこれを私の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.

上記のTICK / TOCKほど簡潔ではありませんが、それが何をしているかを確認するのに十分明確であり、(ファイル、メソッドの先頭の行、および関数名によって)計時されているものを自動的に含みます。明らかに、より詳細な情報が必要な場合(たとえば、通常のようにメソッド呼び出しのタイミングをとっているだけでなく、そのメソッド内のブロックのタイミングをとっている場合)、PrintTimer initに "name =" Foo ""パラメータを追加できます。デフォルト以外の名前を付けます。


-1

UIWebViewのあるページから別のページへの移動時間を最適化したいので、これらのページのロードに使用されるJavascriptを最適化しようとしているわけではありませんか?

そのために、ここで話したようなWebKitプロファイラーを見てみましょう。

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

別のアプローチは、高レベルから開始し、問題のWebページを設計して、毎回Webビュー全体を更新するのではなく、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)")

出力例:-

ELAPSED TIMES [MyApp.SomeViewController.Instrumentation(title: "Start View Did Load"、point:1、lapsedTime:0.040504038333892822)、MyApp.SomeViewController.Instrumentation(title: "Finished Added SubViews"、point:2、lapsedTime:0.010585010051727295)、 MyApp.SomeViewController.Instrumentation(title: "ビューが表示されました"、ポイント:3、expededTime: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")

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

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

回答やコメントを読みましたか?これには日付を使用しないでください!
マット
弊社のサイトを使用することにより、あなたは弊社のクッキーポリシーおよびプライバシーポリシーを読み、理解したものとみなされます。
Licensed under cc by-sa 3.0 with attribution required.