ANSI Cのみを使用して、ミリ秒以上の精度で時間を測定する方法はありますか?私はtime.hを参照していましたが、2番目の精度関数しか見つかりませんでした。
ANSI Cのみを使用して、ミリ秒以上の精度で時間を測定する方法はありますか?私はtime.hを参照していましたが、2番目の精度関数しか見つかりませんでした。
回答:
1秒以上の時間分解能を提供するANSI C関数はありませんが、POSIX関数gettimeofday
はマイクロ秒の分解能を提供します。クロック機能は、プロセスが実行に費やした時間のみを測定し、多くのシステムでは正確ではありません。
この関数は次のように使用できます。
struct timeval tval_before, tval_after, tval_result;
gettimeofday(&tval_before, NULL);
// Some code you want to time, for example:
sleep(1);
gettimeofday(&tval_after, NULL);
timersub(&tval_after, &tval_before, &tval_result);
printf("Time elapsed: %ld.%06ld\n", (long int)tval_result.tv_sec, (long int)tval_result.tv_usec);
これはTime elapsed: 1.000870
私のマシンに戻ります。
timeval::tv_usec
常に1秒未満であり、ループしていることに注意してください。1秒よりも大きな時間の違いを取るために、すなわち、あなたがすべきである:long usec_diff = (e.tv_sec - s.tv_sec)*1000000 + (e.tv_usec - s.tv_usec);
timersub
ます。tval_result
値(tv_secおよびtv_usec)をそのまま使用できます。
#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);
CLOCKS_PER_SEC / 1000
が不正確になる可能性があることにも注意してください(これは、私の経験でCLOCKS_PER_SEC
は常に1000の倍数でしたが)。行うこと(1000 * clock()) / CLOCKS_PER_SEC
は、分割の不正確さの影響を受けにくくなりますが、一方でオーバーフローの影響を受けやすくなります。考慮すべきいくつかの問題。
私は常にclock_gettime()関数を使用して、CLOCK_MONOTONICクロックから時間を返します。返される時間は、エポックのシステム起動など、過去の特定されていない時点からの時間(秒およびナノ秒)です。
#include <stdio.h>
#include <stdint.h>
#include <time.h>
int64_t timespecDiff(struct timespec *timeA_p, struct timespec *timeB_p)
{
return ((timeA_p->tv_sec * 1000000000) + timeA_p->tv_nsec) -
((timeB_p->tv_sec * 1000000000) + timeB_p->tv_nsec);
}
int main(int argc, char **argv)
{
struct timespec start, end;
clock_gettime(CLOCK_MONOTONIC, &start);
// Some code I am interested in measuring
clock_gettime(CLOCK_MONOTONIC, &end);
uint64_t timeElapsed = timespecDiff(&end, &start);
}
clock_gettime(CLOCK_MONOTONIC, ...)
ており、機能テストマクロさえある_POSIX_MONOTONIC_CLOCK
。
ポータブルソリューションの実装
時間測定の問題に対して十分な精度を備えた適切なANSIソリューションがないことはすでにここで述べたので、ポータブルで、可能であれば高解像度の時間測定ソリューションを取得する方法について書きたいと思います。
単調な時計とタイムスタンプ
一般的に言えば、時間測定には2つの方法があります。
1つ目は、事前定義された頻度でティックをカウントする単調なクロックカウンター(ティックカウンターと呼ばれることもあります)を使用するため、ティック値があり、頻度がわかっている場合、ティックを経過時間に簡単に変換できます。単調時計が現在のシステム時刻を何らかの形で反映することは実際には保証されていません。システムの起動以降、ティックをカウントすることもあります。ただし、システムの状態に関係なく、クロックが常に増加していくことが保証されます。通常、周波数はハードウェアの高解像度ソースにバインドされているため、高精度が提供されます(ハードウェアによって異なりますが、最新のハードウェアのほとんどには高解像度クロックソースの問題はありません)。
2番目の方法は、現在のシステムクロック値に基づいて(日付)時刻値を提供します。高解像度の場合もありますが、1つの大きな欠点があります。この種類の時間値は、さまざまなシステム時間調整、つまり、タイムゾーンの変更、夏時間(DST)の変更、NTPサーバーの更新、システムの休止などの影響を受ける可能性があります。オン。状況によっては、負の経過時間の値を取得して、未定義の動作を引き起こす可能性があります。実際、この種のタイムソースは最初のものより信頼性が低くなります。
したがって、時間間隔測定の最初のルールは、可能であれば単調クロックを使用することです。通常は精度が高く、設計上信頼性があります。
フォールバック戦略
ポータブルソリューションを実装する場合は、フォールバック戦略を検討する価値があります。システムに単調クロックがない場合は、単調クロックを使用し、タイムスタンプへのフォールバックアプローチを使用します。
ウィンドウズ
MSDNには、Windowsでの時間測定に関する高解像度タイムスタンプの取得というすばらしい記事があり、ソフトウェアとハードウェアのサポートについて知っておく必要があるすべての詳細が説明されています。Windowsで高精度のタイムスタンプを取得するには、次のことを行う必要があります。
QueryPerformanceFrequencyでタイマーの頻度(1秒あたりのティック数)をクエリします。
LARGE_INTEGER tcounter;
LARGE_INTEGER freq;
if (QueryPerformanceFrequency (&tcounter) != 0)
freq = tcounter.QuadPart;
タイマーの頻度はシステムの起動時に固定されるため、一度だけ取得する必要があります。
QueryPerformanceCounterを使用して現在のティック値をクエリします。
LARGE_INTEGER tcounter;
LARGE_INTEGER tick_value;
if (QueryPerformanceCounter (&tcounter) != 0)
tick_value = tcounter.QuadPart;
ティックを経過時間、つまりマイクロ秒にスケーリングします。
LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);
Microsoftによると、ほとんどの場合、Windows XP以降のバージョンでは、このアプローチに問題はないはずです。ただし、Windowsでは2つのフォールバックソリューションを使用することもできます。
GetTickCount
、Windows Vista以降で使用できます。OS X(macOS)
OS X(macOS)には、単調な時計を表す独自のマッハ絶対時間単位があります。開始する最良の方法は、Appleの記事「テクニカルQ&A QA1398:Mach Absolute Time Units」で、コード例を使用して、Mach固有のAPIを使用して単調なティックを取得する方法を説明しています。また、Mac OS Xのclock_gettimeオルタナティブと呼ばれるローカルの質問もあります。カウンター周波数が分子と分母の形式で使用されているため、最後に、可能な値のオーバーフローをどう処理するかが少し混乱する可能性があります。したがって、経過時間を取得する方法の短い例:
クロック周波数の分子と分母を取得します。
#include <mach/mach_time.h>
#include <stdint.h>
static uint64_t freq_num = 0;
static uint64_t freq_denom = 0;
void init_clock_frequency ()
{
mach_timebase_info_data_t tb;
if (mach_timebase_info (&tb) == KERN_SUCCESS && tb.denom != 0) {
freq_num = (uint64_t) tb.numer;
freq_denom = (uint64_t) tb.denom;
}
}
あなたはそれを一度だけ行う必要があります。
現在のティック値をクエリしますmach_absolute_time
:
uint64_t tick_value = mach_absolute_time ();
以前に照会された分子と分母を使用して、ティックを経過時間、つまりマイクロ秒にスケーリングします。
uint64_t value_diff = tick_value - prev_tick_value;
/* To prevent overflow */
value_diff /= 1000;
value_diff *= freq_num;
value_diff /= freq_denom;
オーバーフローを防ぐための主なアイデアは、分子と分母を使用する前に、目盛りを望ましい精度に縮小することです。最初のタイマーの分解能はナノ秒単位なので、1000
マイクロ秒を取得するためにそれを除算します。Chromiumのtime_mac.cで使用されているのと同じアプローチを見つけることができます。本当にナノ秒の精度が必要な場合は、オーバーフローせずにmach_absolute_timeを使用するにはどうすればよいですか?。
LinuxおよびUNIX
このclock_gettime
呼び出しは、POSIX対応システムでの最善の方法です。さまざまなクロックソースから時間をクエリできますCLOCK_MONOTONIC
。必要なのはです。clock_gettime
サポートされているすべてのシステムではないCLOCK_MONOTONIC
ので、最初に行う必要があるのは、その可用性を確認することです。
_POSIX_MONOTONIC_CLOCK
の値に定義されている>= 0
、それはそれは意味CLOCK_MONOTONIC
下盛です。に_POSIX_MONOTONIC_CLOCK
定義されて0
いる場合、実行時に機能するかどうかをさらに確認する必要があることを意味しますsysconf
:
#include <unistd.h>
#ifdef _SC_MONOTONIC_CLOCK
if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
/* A monotonic clock presents */
}
#endif
の使い方clock_gettime
はかなり簡単です:
時間の値を取得します。
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
uint64_t get_posix_clock_time ()
{
struct timespec ts;
if (clock_gettime (CLOCK_MONOTONIC, &ts) == 0)
return (uint64_t) (ts.tv_sec * 1000000 + ts.tv_nsec / 1000);
else
return 0;
}
ここでは時間をマイクロ秒に縮小しました。
同じ方法で受け取った前回の時間値との差を計算します。
uint64_t prev_time_value, time_value;
uint64_t time_diff;
/* Initial time */
prev_time_value = get_posix_clock_time ();
/* Do some work here */
/* Final time */
time_value = get_posix_clock_time ();
/* Time difference */
time_diff = time_value - prev_time_value;
最適なフォールバック戦略はgettimeofday
呼び出しを使用することです。これは単調ではありませんが、非常に優れた解決策を提供します。考え方はと同じclock_gettime
ですが、時間の値を取得するには次のようにする必要があります。
#include <time.h>
#include <sys/time.h>
#include <stdint.h>
uint64_t get_gtod_clock_time ()
{
struct timeval tv;
if (gettimeofday (&tv, NULL) == 0)
return (uint64_t) (tv.tv_sec * 1000000 + tv.tv_usec);
else
return 0;
}
この場合も、時間の値はマイクロ秒に縮小されます。
SGI IRIX
IRIXはclock_gettime
電話をかけましたが、それは欠けていCLOCK_MONOTONIC
ます。代わりに、として、独自の単調なクロックソースが定義されていCLOCK_SGI_CYCLE
ますが、代わりに使用すべきCLOCK_MONOTONIC
でclock_gettime
。
SolarisおよびHP-UX
Solarisにはgethrtime
、現在のタイマー値をナノ秒単位で返す独自の高解像度タイマーインターフェイスがあります。新しいバージョンのSolarisにはclock_gettime
が付いているgethrtime
場合がありますが、古いバージョンのSolarisをサポートする必要がある場合はそのまま使用できます。
使い方は簡単です:
#include <sys/time.h>
void time_measure_example ()
{
hrtime_t prev_time_value, time_value;
hrtime_t time_diff;
/* Initial time */
prev_time_value = gethrtime ();
/* Do some work here */
/* Final time */
time_value = gethrtime ();
/* Time difference */
time_diff = time_value - prev_time_value;
}
HP-UXにはclock_gettime
がgethrtime
ありませんが、Solarisと同じようにどちらを使用するかをサポートしています。
BeOS
BeOSにはsystem_time
、コンピューターが起動してから経過したマイクロ秒数を返す独自の高解像度タイマーインターフェイスもあります。
使用例:
#include <kernel/OS.h>
void time_measure_example ()
{
bigtime_t prev_time_value, time_value;
bigtime_t time_diff;
/* Initial time */
prev_time_value = system_time ();
/* Do some work here */
/* Final time */
time_value = system_time ();
/* Time difference */
time_diff = time_value - prev_time_value;
}
OS / 2
OS / 2には、高精度のタイムスタンプを取得するための独自のAPIがあります。
DosTmrQueryFreq
(GCCコンパイラの場合)を使用してタイマーの頻度(ユニットあたりのティック数)をクエリします。
#define INCL_DOSPROFILE
#define INCL_DOSERRORS
#include <os2.h>
#include <stdint.h>
ULONG freq;
DosTmrQueryFreq (&freq);
現在のティック値をクエリしますDosTmrQueryTime
:
QWORD tcounter;
unit64_t time_low;
unit64_t time_high;
unit64_t timestamp;
if (DosTmrQueryTime (&tcounter) == NO_ERROR) {
time_low = (unit64_t) tcounter.ulLo;
time_high = (unit64_t) tcounter.ulHi;
timestamp = (time_high << 32) | time_low;
}
ティックを経過時間、つまりマイクロ秒にスケーリングします。
uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);
実装例
上記のすべての戦略を実装するplibsysライブラリを確認できます(詳細については、ptimeprofiler * .cを参照してください)。
timespec_get
: stackoverflow.com/a/36095407/895245
timespec_get
単調ではありません。
timespec_get
C11から
実装の解像度に丸められたナノ秒までを返します。
POSIXからのANSI詐欺のように見えclock_gettime
ます。
例:a printf
はUbuntu 15.10で100ミリ秒ごとに実行されます。
#include <stdio.h>
#include <stdlib.h>
#include <time.h>
static long get_nanos(void) {
struct timespec ts;
timespec_get(&ts, TIME_UTC);
return (long)ts.tv_sec * 1000000000L + ts.tv_nsec;
}
int main(void) {
long nanos;
long last_nanos;
long start;
nanos = get_nanos();
last_nanos = nanos;
start = nanos;
while (1) {
nanos = get_nanos();
if (nanos - last_nanos > 100000000L) {
printf("current nanos: %ld\n", nanos - start);
last_nanos = nanos;
}
}
return EXIT_SUCCESS;
}
C11 N1570標準案 7.27.2.5「timespec_get機能は言います」:
baseがTIME_UTCの場合、tv_secメンバーは、実装で定義されたエポックからの秒数に設定され、整数値に切り捨てられ、tv_nsecメンバーはシステムクロックの分解能に丸められたナノ秒の整数に設定されます。(321)
321)struct timespecオブジェクトはナノ秒の分解能で時間を記述しますが、利用可能な分解能はシステムに依存し、1秒よりも大きい場合があります。
C ++ 11も取得std::chrono::high_resolution_clock
:C ++ Cross-Platform High-Resolution Timer
glibc 2.21の実装
次の場所にありますsysdeps/posix/timespec_get.c
:
int
timespec_get (struct timespec *ts, int base)
{
switch (base)
{
case TIME_UTC:
if (__clock_gettime (CLOCK_REALTIME, ts) < 0)
return 0;
break;
default:
return 0;
}
return base;
}
とても明確:
TIME_UTC
現在のみサポートされています
__clock_gettime (CLOCK_REALTIME, ts)
POSIX APIであるに転送します:http : //pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
Linux x86-64にはclock_gettime
システムコールがあります。
次の理由により、これはフェイルプルーフのマイクロベンチマーク手法ではないことに注意してください。
man clock_gettime
プログラムの実行中にシステム時間の設定を変更すると、この測定が不連続になる可能性があると述べています。もちろん、これはまれなイベントであり、無視してかまいません。
これは実時間を測定するので、スケジューラーがタスクを忘れると決めた場合、それはより長く実行されているように見えます。
これらの理由からgetrusage()
、マイクロ秒の最大精度が低いにもかかわらず、POSIXベンチマークツールの方が優れている可能性があります。
詳細情報:Linuxでの時間の測定-時間vsクロックvs getrusage vs clock_gettime vs gettimeofday vs timespec_get?
あなたが得ることができる最高の精度は、クロックレベルの解決を提供できるx86のみの「rdtsc」命令の使用によるものです(もちろん、簡単に測定できるrdtsc呼び出し自体のコストを考慮する必要があります)アプリケーションの起動)。
ここでの主な問題は、毎秒のクロック数を測定することです。
受け入れられた答えは十分ですが、私の解決策はより単純です.Linuxでテストするだけで、gcc(Ubuntu 7.2.0-8ubuntu3.2)7.2.0を使用します。
Alse使用はgettimeofday
、tv_sec
第二の一部であり、tv_usec
あるマイクロではなく、ミリ秒。
long currentTimeMillis() {
struct timeval time;
gettimeofday(&time, NULL);
return time.tv_sec * 1000 + time.tv_usec / 1000;
}
int main() {
printf("%ld\n", currentTimeMillis());
// wait 1 second
sleep(1);
printf("%ld\n", currentTimeMillis());
return 0;
}
それは印刷します:
1522139691342
1522139692342
、ちょうど1秒。