web-dev-qa-db-ja.com

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

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

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

使いやすく、ミリ秒未満の精度です。

425

以下に、私が使用する2つの1行マクロを示します。

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

次のように使用します。

TICK;

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

TOCK;
248
Ron

OS Xでのきめ細かいタイミングの場合、<mach/mach_time.h>で宣言されたmach_absolute_time( )を使用する必要があります。

#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などのツールを使用して実行しているアプリケーションをprofileにするとより便利になる場合があることに注意してください。これは正確なタイミング情報を提供しませんが、アプリケーションの時間のパーセントがどこで費やされているかを教えてくれます。

48
Stephen Canon

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に翻訳されています。
  • 私はグローバルレベルで開始日を使用しています。改善する提案は歓迎します
12
Adriano Spadoni

私はこれが古いものであることを知っていますが、私はそれをまた過ぎてさまようことに気づいたので、私はここで自分の選択肢を提出するだろうと思いました。

最善の策は、これについての私のブログ投稿をチェックすることです: Objective-Cのタイミング:ストップウォッチ

基本的に、非常に基本的な方法で視聴を停止するクラスを作成しましたが、カプセル化されているため、次の操作を行うだけで済みます。

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

そして、次のようになります。

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

ログに...

もう一度、私の投稿をもう少しチェックするか、ここからダウンロードしてください: MMStopwatch.Zip

9
bladnman

mach_absolute_time()の便利なラッパーがあります。これはCACurrentMediaTime()関数です。

NSDateCFAbsoluteTimeGetCurrent()オフセットとは異なり、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")
9
Alex Nazaroff

Ron's ソリューションに基づいたマクロを使用します。

#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

このブログ投稿のコード に触発された、最小限の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"];終了後、時間を取得します
4
zubko

このStopWatchクラスを使用して、正確なタイミング(seconds.parts of seconds)をreally取得できます。 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メソッドがあります。

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]]];
}
3

Swift 4でmach_absolute_time()を使用したきめ細かいタイミングの例:

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
jbg

私はこのコードを使用します:

#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
cleexiang

OK、より速くするために修正できるものを見つけることが目的である場合、それは少し異なる目標です。関数にかかる時間を測定することは、あなたが何をしたのかを知る良い方法ですが、何をすべきかを見つけるには技術。 これは私が推奨するものです 、そしてあなたがiPhoneでそれを行うことができることを知っています。

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

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

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

これが測定とどう違うのかわかりますか?それは発見であり、測定ではありません。ほとんどのプロファイリングは、どれだけ時間がかかっているかをできるだけ正確に測定することに基づいており、それが重要であるかのように、何を修正する必要があるかを特定する問題を手作業で振り分けます。プロファイリングではすべての問題が検出されるわけではありませんが、この方法ではすべての問題が検出されるので、見つけられない問題があなたを傷つけます。

2
Mike Dunlavey

私はこれを使用します:

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
David Kanarek

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 ""パラメーターを追加できます。デフォルト以外の名前を付けます。

0
Tom Dibble

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ブロックに似ていますが、関連するコードがグループ化されるという利点があります。

0
CMont