22

CLOCK_MONOTONICは使用できないようであるため、clock_gettimeはアウトです。

mach_absolute_time()が正しい方法である可能性があることをいくつかの場所で読んだことがありますが、それが「CPU依存値」であることを読んだ後、すぐにrtdscを使用しているかどうか疑問に思いました。したがって、値は単調であっても時間の経過とともにドリフトする可能性があります。また、スレッドアフィニティの問題により、関数の呼び出しとは意味のある異なる結果が生じる可能性があります(すべてのコアで単調ではなくなります)。

もちろん、それは単なる憶測です。mach_absolute_timeが実際にどのように機能するか知っている人はいますか?私は実際にclock_gettime(CLOCK_MONOTONIC ...またはOSXのそれに似たものに代わるものを探しています。クロックソースが何であれ、少なくともミリ秒の精度とミリ秒の精度を期待しています。

どのクロックが利用可能か、どのクロックが単調であるか、特定のクロックがドリフトするか、スレッドアフィニティの問題があるか、すべてのMacハードウェアでサポートされていないか、または実行に「超高」CPUサイクルを要するかを理解したいと思います。

このトピックについて私が見つけたリンクは次のとおりです(一部はすでにデッドリンクであり、archive.orgでは見つかりません):

https://developer.apple.com/library/mac/#qa/qa1398/_index.html http://www.wand.net.nz/~smr26/wordpress/2009/01/19/monotonic-time-in -mac-os-x / http://www.meandmark.com/timing.pdf

ありがとう!ブレット

4

3 に答える 3

27

Machカーネルは、システムクロックへのアクセスを提供します。そのうち、少なくとも1つ(SYSTEM_CLOCK)は、単調に増分するものとしてドキュメントによってアドバタイズされます。

#include <mach/clock.h>
#include <mach/mach.h>

clock_serv_t cclock;
mach_timespec_t mts;

host_get_clock_service(mach_host_self(), SYSTEM_CLOCK, &cclock);
clock_get_time(cclock, &mts);
mach_port_deallocate(mach_task_self(), cclock);

mach_timespec_tナノ秒の精度があります。ただし、精度についてはよくわかりません。

Mac OS Xは、次の3つのクロックをサポートしています。

  • SYSTEM_CLOCK起動時からの時間を返します。
  • CALENDAR_CLOCK1970-01-01以降のUTC時刻を返します。
  • REALTIME_CLOCKSYSTEM_CLOCKは非推奨であり、現在の実装と同じです。

ドキュメントにclock_get_timeは、誰かが呼び出さない限り、時計は単調に増加していると書かれていますclock_set_time。クロックの単調な特性を壊す可能性があるため、への呼び出しclock_set_timeお勧めしません。実際、現在の実装はKERN_FAILURE何もせずに戻ります。

于 2012-07-27T03:31:29.960 に答える
9

これに対するいくつかの異なる答えを調べた後、私はマッハでclock_gettimeをエミュレートするヘッダーを定義することになりました。

#include <sys/types.h>
#include <sys/_types/_timespec.h>
#include <mach/mach.h>
#include <mach/clock.h>

#ifndef mach_time_h
#define mach_time_h

/* The opengroup spec isn't clear on the mapping from REALTIME to CALENDAR
 being appropriate or not.
 http://pubs.opengroup.org/onlinepubs/009695299/basedefs/time.h.html */

// XXX only supports a single timer
#define TIMER_ABSTIME -1
#define CLOCK_REALTIME CALENDAR_CLOCK
#define CLOCK_MONOTONIC SYSTEM_CLOCK

typedef int clockid_t;

/* the mach kernel uses struct mach_timespec, so struct timespec
    is loaded from <sys/_types/_timespec.h> for compatability */
// struct timespec { time_t tv_sec; long tv_nsec; };

int clock_gettime(clockid_t clk_id, struct timespec *tp);

#endif

およびmach_gettime.c

#include "mach_gettime.h"
#include <mach/mach_time.h>

#define MT_NANO (+1.0E-9)
#define MT_GIGA UINT64_C(1000000000)

// TODO create a list of timers,
static double mt_timebase = 0.0;
static uint64_t mt_timestart = 0;

// TODO be more careful in a multithreaded environement
int clock_gettime(clockid_t clk_id, struct timespec *tp)
{
    kern_return_t retval = KERN_SUCCESS;
    if( clk_id == TIMER_ABSTIME)
    {
        if (!mt_timestart) { // only one timer, initilized on the first call to the TIMER
            mach_timebase_info_data_t tb = { 0 };
            mach_timebase_info(&tb);
            mt_timebase = tb.numer;
            mt_timebase /= tb.denom;
            mt_timestart = mach_absolute_time();
        }

        double diff = (mach_absolute_time() - mt_timestart) * mt_timebase;
        tp->tv_sec = diff * MT_NANO;
        tp->tv_nsec = diff - (tp->tv_sec * MT_GIGA);
    }
    else // other clk_ids are mapped to the coresponding mach clock_service
    {
        clock_serv_t cclock;
        mach_timespec_t mts;

        host_get_clock_service(mach_host_self(), clk_id, &cclock);
        retval = clock_get_time(cclock, &mts);
        mach_port_deallocate(mach_task_self(), cclock);

        tp->tv_sec = mts.tv_sec;
        tp->tv_nsec = mts.tv_nsec;
    }

    return retval;
}
于 2013-12-26T17:54:16.363 に答える
4

MachTimeを使用するだけです。
これはパブリックAPIであり、macOS、iOS、およびtvOSで動作し、サンドボックス内から動作します。

Mach Timeは、私が通常「クロックティック」と呼ぶ抽象的な時間単位を返します。クロックティックの長さはシステム固有であり、CPUによって異なります。現在のIntelシステムでは、クロックティックは実際には正確に1ナノ秒ですが、これに依存することはできません(ARMでは異なる場合があり、PowerPC CPUでは確かに異なります)。システムは、クロックティックをナノ秒に変換し、ナノ秒をクロックティックに変換する変換係数も通知できます(この係数は静的であり、実行時に変更されることはありません)。システムが起動すると、クロックは開始し0、その後はクロックティックごとに単調に増加するため、マッハタイムを使用してシステムの稼働時間を取得することもできます(もちろん、稼働時間は単調です!)。

ここにいくつかのコードがあります:

#include <stdio.h>
#include <inttypes.h>
#include <mach/mach_time.h>

int main ( ) {
    uint64_t clockTicksSinceSystemBoot = mach_absolute_time();
    printf("Clock ticks since system boot: %"PRIu64"\n",
        clockTicksSinceSystemBoot
    );

    static mach_timebase_info_data_t timebase;
    mach_timebase_info(&timebase);
    // Cast to double is required to make this a floating point devision,
    // otherwise it would be an interger division and only the result would
    // be converted to floating point!
    double clockTicksToNanosecons = (double)timebase.numer / timebase.denom;

    uint64_t systemUptimeNanoseconds = (uint64_t)(
        clockTicksToNanosecons * clockTicksSinceSystemBoot
    );
    uint64_t systemUptimeSeconds = systemUptimeNanoseconds / (1000 * 1000 * 1000);
    printf("System uptime: %"PRIu64" seconds\n", systemUptimeSeconds);
}

特定のマッハ時間に達するまでスレッドをスリープ状態にすることもできます。そのためのコードは次のとおりです。

// Sleep for 750 ns
uint64_t machTimeNow = mach_absolute_time();
uint64_t clockTicksToSleep = (uint64_t)(750 / clockTicksToNanosecons);
uint64_t machTimeIn750ns = machTimeNow + clockTicksToSleep;
mach_wait_until(machTimeIn750ns);

Mach Timeは実時間とは関係がないため、システムの日付と時刻の設定を好きなように試すことができます。これはMachTimeには影響しません。

ただし、特別な考慮事項が1つあります。これにより、マッハタイムが特定のユースケースに適さなくなる可能性があります。システムがスリープしている間はCPUクロックが実行されません。したがって、スレッドを5分間待機させ、1分後にシステムがスリープ状態になり、30分間スリープ状態を維持した場合、30分のスリープ時間はカウントされないため、システムがウェイクアップした後もスレッドはさらに4分間待機します。 !!その間、CPUクロックも停止していました。しかし、他の場合には、これはまさにあなたがしたいことです。

マッハ時間は、費やした時間を測定するための非常に正確な方法でもあります。そのタスクを示すコードは次のとおりです。

// Measure time
uint64_t machTimeBegin = mach_absolute_time();
sleep(1);
uint64_t machTimeEnd = mach_absolute_time();
uint64_t machTimePassed = machTimeEnd - machTimeBegin;
uint64_t timePassedNS = (uint64_t)(
    machTimePassed * clockTicksToNanosecons
);
printf("Thread slept for: %"PRIu64" ns\n", timePassedNS);

スレッドが正確に1秒間スリープしないことがわかります。これは、スレッドをスリープ状態にしてから再度ウェイクアップするのに時間がかかり、すべてのコアがスリープ状態になってもすぐにCPU時間を取得できないためです。その時点ですでにスレッドの実行で忙しいです。

更新(2018-09-26)

macOS 10.12(Sierra)以降、も存在しmach_continuous_timeます。mach_continuous_timeとの唯一の違いはmach_absolute_time、システムがスリープしているときに継続時間が進むことです。したがって、これがこれまでの問題であり、Mach Time 10.12以降を使用しない理由がこの問題の解決策を提供する場合は、使い方は上記と全く同じです。

また、macOS 10.9(Mavericks)からは、がmach_approximate_timeあり、10.12にはもありmach_continuous_approximate_timeます。これら2つは同一でmach_absolute_timeありmach_continuous_time、唯一の違いは、高速でありながら精度が低いことです。カーネルがマッハ時間を処理するため、標準関数ではカーネルを呼び出す必要があります。このような呼び出しは、特にすでにMeltdown修正が行われているシステムでは、いくらか費用がかかります。おおよそのバージョンは、常にカーネルを呼び出す必要はありません。カーネルクロックと時々同期するだけのユーザースペースのクロックを使用して、同期がずれすぎないようにしますが、常に小さな偏差が発生する可能性があるため、これは「おおよその」マッハ時間にすぎません。

于 2016-08-26T09:58:41.930 に答える