8

I am looking for an iOS analog for Android's SystemClock.currentThreadTimeMillis() or Microsoft's GetThreadTimes() or Posix clock_gettime(CLOCK_THREAD_CPUTIME_ID, ) and pthread_getcpuclockid() functions to measure the actual "clean" time used by a function in a multithreaded application. That is, I don't want to measure the actual wall clock time spent in a function, but the on-CPU time.

I found interesting discussions about this here on stackoverflow and elsewhere. Unfortunately, neither applies to iOS.

Is there a comparable function for this on iOS?

Community
  • 1
  • 1
Alex Cohn
  • 56,089
  • 9
  • 113
  • 307
  • Are you just looking for precise wall-clock time? If so, the answers to this question should work: [How can I get a precise time, for example in milliseconds in Objective-C?](http://stackoverflow.com/questions/889380/how-can-i-get-a-precise-time-for-example-in-milliseconds-in-objective-c) – Brad Larson Jan 31 '12 at 19:49
  • @Brad Larson: No, I want to measure the opposite of wall-clock, to measure the pure CPU time that the given thread was using. – Alex Cohn Feb 01 '12 at 07:42
  • 1
    Just to check: do you really need to put this in your code? Instruments does this very well without requiring you to modify the program. – Rob Napier Feb 01 '12 at 16:17
  • Rob's right in that Instruments has great capabilities to show only on-CPU execution time for methods (as does Shark, but Instruments is a lot easier to work with). You could also use DTrace's `vtimestamp` like I show in the "Time profiling" section of this article: http://www.macresearch.org/tuning-cocoa-applications-using-dtrace-writing-scripts , but that would only work on applications run in the iOS Simulator, not on the actual device. – Brad Larson Feb 01 '12 at 17:45
  • That's cool, but unfortunately my profiling is very specific to the ARM processor, therefore the iOS Simulator is irrelevant. – Alex Cohn Feb 26 '12 at 15:11
  • Obviously this information is important for you, but why? – Reinhard Männer Jul 21 '19 at 17:13
  • @ReinhardMänner We had an issue with performance of some networking C++ component that was shared between Android and iOS. The tricky part was that we wanted to understand how the code behaved on real device in natural conditions, so this excluded running Simulator or even full-scale profiler. The bottom line was, the measurements proved that the cross-platform differences we observed were not due to our implementation. I don't remember exactly how we fixed the problem in the end. – Alex Cohn Jul 22 '19 at 09:28

3 Answers3

13

In case anyone is looking for a good answer:

A while ago I found some great code in this answer (for finding CPU time/memory usage in OSX), and adapted it slightly. I used this for benchmarking some NEON optimizations on the ARM. You would probably only need the section which gets time for the current thread.

#include <sys/types.h>
#include <sys/sysctl.h>
#include <mach/mach_init.h>
#include <mach/mach_host.h>
#include <mach/mach_port.h>
#include <mach/mach_traps.h>
#include <mach/task_info.h>
#include <mach/thread_info.h>
#include <mach/thread_act.h>
#include <mach/vm_region.h>
#include <mach/vm_map.h>
#include <mach/task.h>


typedef struct {
    double utime, stime;
} CPUTime;

int get_cpu_time(CPUTime *rpd, bool_t thread_only)
{
    task_t task;
    kern_return_t error;
    mach_msg_type_number_t count;
    thread_array_t thread_table;
    thread_basic_info_t thi;
    thread_basic_info_data_t thi_data;
    unsigned table_size;
    struct task_basic_info ti;

    if (thread_only) {
        // just get time of this thread
        count = THREAD_BASIC_INFO_COUNT;
        thi = &thi_data;
        error = thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t)thi, &count);
        rpd->utime = thi->user_time.seconds + thi->user_time.microseconds * 1e-6;
        rpd->stime = thi->system_time.seconds + thi->system_time.microseconds * 1e-6;
        return 0;
    }


    // get total time of the current process

    task = mach_task_self();
    count = TASK_BASIC_INFO_COUNT;
    error = task_info(task, TASK_BASIC_INFO, (task_info_t)&ti, &count);
    assert(error == KERN_SUCCESS);
    { /* calculate CPU times, adapted from top/libtop.c */
        unsigned i;
        // the following times are for threads which have already terminated and gone away
        rpd->utime = ti.user_time.seconds + ti.user_time.microseconds * 1e-6;
        rpd->stime = ti.system_time.seconds + ti.system_time.microseconds * 1e-6;
        error = task_threads(task, &thread_table, &table_size);
        assert(error == KERN_SUCCESS);
        thi = &thi_data;
        // for each active thread, add up thread time
        for (i = 0; i != table_size; ++i) {
            count = THREAD_BASIC_INFO_COUNT;
            error = thread_info(thread_table[i], THREAD_BASIC_INFO, (thread_info_t)thi, &count);
            assert(error == KERN_SUCCESS);
            if ((thi->flags & TH_FLAGS_IDLE) == 0) {
                rpd->utime += thi->user_time.seconds + thi->user_time.microseconds * 1e-6;
                rpd->stime += thi->system_time.seconds + thi->system_time.microseconds * 1e-6;
            }
            error = mach_port_deallocate(mach_task_self(), thread_table[i]);
            assert(error == KERN_SUCCESS);
        }
        error = vm_deallocate(mach_task_self(), (vm_offset_t)thread_table, table_size * sizeof(thread_array_t));
        assert(error == KERN_SUCCESS);
    }
    if (task != mach_task_self()) {
        mach_port_deallocate(mach_task_self(), task);
        assert(error == KERN_SUCCESS);
    }
    return 0;
}
Community
  • 1
  • 1
tcovo
  • 7,550
  • 2
  • 20
  • 13
4

I use this code to measure thread CPU time. It's based on tcovo's answer, but is more concise and focused.

thread_time.h

#ifndef thread_time_h
#define thread_time_h

#include <stdint.h>

typedef struct thread_time {
    int64_t user_time_us;
    int64_t system_time_us;
} thread_time_t;

thread_time_t thread_time();
thread_time_t thread_time_sub(thread_time_t const a, thread_time_t const b);

#endif /* thread_time_h */

thread_time.c

#include "thread_time.h"
#include <mach/mach_init.h>
#include <mach/thread_act.h>
#include <mach/thread_info.h>

int64_t time_value_to_us(time_value_t const t) {
    return (int64_t)t.seconds * 1000000 + t.microseconds;
}

thread_time_t thread_time() {
    thread_basic_info_data_t basic_info;
    mach_msg_type_number_t count = THREAD_BASIC_INFO_COUNT;
    kern_return_t const result = thread_info(mach_thread_self(), THREAD_BASIC_INFO, (thread_info_t)&basic_info, &count);
    if (result == KERN_SUCCESS) {
        return (thread_time_t){
            .user_time_us   = time_value_to_us(basic_info.user_time),
            .system_time_us = time_value_to_us(basic_info.system_time)
        };
    } else {
        return (thread_time_t){-1, -1};
    }
}

thread_time_t thread_time_sub(thread_time_t const a, thread_time_t const b) {
    return (thread_time_t){
        .user_time_us   = a.user_time_us   - b.user_time_us,
        .system_time_us = a.system_time_us - b.system_time_us
    };
}
Community
  • 1
  • 1
werediver
  • 4,667
  • 1
  • 29
  • 49
  • By chance do you have a linux/android form of this? – Joel Teply Sep 05 '17 at 14:53
  • @JoelTeply This code uses Mach kernel API, so it's specific to macOS and derivatives (iOS, watchOS, tvOS). Unfortunately, I don't have an adaptation for Linux kernel now. – werediver Sep 05 '17 at 15:50
  • Hey, is there a way to tell how much time you spend using the main thread? is this it? Also what is the `_us` meaning in your method names? and finally what is the number `thread_time` is putting out ie secs, milli secs etc? Thanks – Wazza Jul 12 '19 at 13:48
  • `-us` is nanoseconds, got it. lol. have asked a question here https://stackoverflow.com/questions/57010582/detect-percentage-of-time-main-thread-is-used – Wazza Jul 12 '19 at 16:43
  • 1
    @Wazza These functions give you information about the thread they are being called from. If you call them from the main thread, you'll get the info about the main thread. `us` stands for [micro](https://en.wikipedia.org/wiki/Micro-)seconds (10^-6), not nano- (10^-9). – werediver Jul 24 '19 at 07:51
0

From its description it seems that the clock() function does what you need?

http://developer.apple.com/library/ios/#documentation/System/Conceptual/ManPages_iPhoneOS/man3/clock.3.html

onnoweb
  • 3,038
  • 22
  • 29