Yalnızca ANSI C kullanarak, zamanı milisaniye hassasiyetinde veya daha fazla ölçmenin bir yolu var mı? Time.h'ye göz atıyordum ama sadece ikinci hassas fonksiyonlar buldum.
Yalnızca ANSI C kullanarak, zamanı milisaniye hassasiyetinde veya daha fazla ölçmenin bir yolu var mı? Time.h'ye göz atıyordum ama sadece ikinci hassas fonksiyonlar buldum.
Yanıtlar:
1 saniyeden daha iyi çözünürlük sağlayan ANSI C işlevi yoktur, ancak POSIX işlevi gettimeofday
mikrosaniye çözünürlük sağlar. Saat işlevi yalnızca bir işlemin yürütmek için harcadığı süreyi ölçer ve birçok sistemde doğru değildir.
Bu işlevi şu şekilde kullanabilirsiniz:
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);
Bu Time elapsed: 1.000870
benim makinemde geri döner .
timeval::tv_usec
zaman bir saniyenin altında olduğunu , döngü halinde olduğunu belirtmek gerekir . Yani 1 saniyeden uzun zaman farklarını almak için şunları yapmalısınız:long usec_diff = (e.tv_sec - s.tv_sec)*1000000 + (e.tv_usec - s.tv_usec);
timersub
fonksiyonun içinde kapsüle edilmiştir . tval_result
Değerleri (tv_sec ve tv_usec) olduğu gibi kullanabiliriz .
#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);
CLOCKS_PER_SEC / 1000
muhtemelen nihai sonucu etkileyebilecek kesin olmayabileceğine dikkat edin (ancak benim deneyimime CLOCKS_PER_SEC
göre her zaman 1000'in katı olmuştur). Yapmak (1000 * clock()) / CLOCKS_PER_SEC
, bölünmeye daha az duyarlıdır, ancak diğer yandan taşmaya daha yatkındır. Sadece dikkate alınması gereken bazı sorunlar.
Ben her zaman clock_gettime () işlevini kullanıyorum, saati CLOCK_MONOTONIC saatinden döndürüyorum. Dönen zaman, çağın sistem başlangıcı gibi geçmişte belirtilmemiş bazı noktalardan bu yana saniye ve nanosaniye cinsinden süredir.
#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, ...)
ve hatta özellik test makrosu olduğuna inanıyorum _POSIX_MONOTONIC_CLOCK
.
Taşınabilir bir çözüm uygulama
Burada zaman ölçümü problemi için yeterli hassasiyete sahip uygun bir ANSI çözümü bulunmadığından daha önce bahsedildiği gibi, taşınabilir ve mümkünse yüksek çözünürlüklü bir zaman ölçüm çözümünün nasıl elde edileceğini yazmak istiyorum.
Monoton saat ve zaman damgaları
Genel olarak konuşursak, zaman ölçümünün iki yolu vardır:
İlki, önceden tanımlanmış bir frekansla tıklamaları sayan monoton bir saat sayacı (bazen bir tik sayacı olarak adlandırılır) kullanır, böylece bir tik değeriniz varsa ve sıklık biliniyorsa, işaretleri kolayca geçen zamana dönüştürebilirsiniz. Aslında monoton bir saatin mevcut sistem saatini herhangi bir şekilde yansıtacağı garanti edilmez, ayrıca bir sistem başlangıcından itibaren tik sayabilir. Ancak, sistem durumundan bağımsız olarak bir saatin her zaman artan bir şekilde çalıştırılmasını garanti eder. Genellikle frekans, yüksek çözünürlüklü bir donanım kaynağına bağlıdır, bu nedenle yüksek bir doğruluk sağlar (donanıma bağlıdır, ancak modern donanımların çoğunun yüksek çözünürlüklü saat kaynakları ile herhangi bir sorunu yoktur).
İkinci yol, mevcut sistem saat değerine göre bir (tarih) saat değeri sağlar. Aynı zamanda yüksek bir çözünürlüğe sahip olabilir, ancak önemli bir dezavantajı vardır: bu tür bir zaman değeri, farklı sistem saati ayarlamalarından etkilenebilir, yani saat dilimi değişikliği, gün ışığından yararlanma saati (DST) değişikliği, NTP sunucusu güncellemesi, sistem hazırda bekletme vb. üzerinde. Bazı durumlarda, tanımlanmamış bir davranışa yol açabilecek negatif bir geçen zaman değeri elde edebilirsiniz. Aslında bu tür bir zaman kaynağı, ilkinden daha az güvenilirdir.
Bu nedenle, zaman aralığı ölçümünde ilk kural, mümkünse monoton bir saat kullanmaktır. Genellikle yüksek bir hassasiyete sahiptir ve tasarımı gereği güvenilirdir.
Yedek strateji
Taşınabilir bir çözüm uygularken, bir geri dönüş stratejisi göz önünde bulundurmaya değer: varsa monoton bir saat kullanın ve sistemde monoton saat yoksa zaman damgalarına geri dönüş yaklaşımı kullanın.
pencereler
Yazılım ve donanım desteği hakkında bilmeniz gerekebilecek tüm ayrıntıları açıklayan, Windows'ta zaman ölçümü hakkında MSDN'de yüksek çözünürlüklü zaman damgaları edinme adlı harika bir makale var . Windows'ta yüksek hassasiyetli bir zaman damgası elde etmek için şunları yapmalısınız:
QueryPerformanceFrequency ile bir zamanlayıcı sıklığını (saniye başına tıklama) sorgulama :
LARGE_INTEGER tcounter;
LARGE_INTEGER freq;
if (QueryPerformanceFrequency (&tcounter) != 0)
freq = tcounter.QuadPart;
Zamanlayıcı frekansı sistem önyüklemesinde sabittir, bu nedenle yalnızca bir kez almanız gerekir.
QueryPerformanceCounter ile geçerli kene değerini sorgulayın :
LARGE_INTEGER tcounter;
LARGE_INTEGER tick_value;
if (QueryPerformanceCounter (&tcounter) != 0)
tick_value = tcounter.QuadPart;
keneleri geçen zamana, yani mikrosaniyelere ölçekleyin:
LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);
Microsoft'a göre, çoğu durumda Windows XP ve sonraki sürümlerde bu yaklaşımla ilgili herhangi bir sorun yaşamazsınız. Ancak Windows'ta iki yedek çözüm de kullanabilirsiniz:
GetTickCount
, ancak Windows Vista ve sonraki sürümlerden itibaren mevcuttur.OS X (macOS)
OS X (macOS), monoton bir saati temsil eden kendi Mach mutlak zaman birimlerine sahiptir. Başlamanın en iyi yolu, Apple'ın, monoton tikler elde etmek için Mach'a özgü API'nin nasıl kullanılacağını (kod örnekleriyle birlikte) açıklayan Teknik Soru-Cevap QA1398: Mach Absolute Time Units makalesidir . Bununla ilgili olarak Mac OS X'te clock_gettime alternatifi olarak adlandırılan yerel bir soru da vardır; bu, sonunda olası değer taşması ile ne yapmanız gerektiği konusunda sizi biraz karıştırabilir çünkü sayaç frekansı pay ve payda şeklinde kullanılır. Öyleyse, geçen zamanın nasıl alınacağına dair kısa bir örnek:
saat frekansı payını ve paydasını alın:
#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;
}
}
Bunu yalnızca bir kez yapmanız gerekir.
geçerli kene değerini şununla sorgulayın mach_absolute_time
:
uint64_t tick_value = mach_absolute_time ();
daha önce sorgulanan pay ve paydayı kullanarak keneleri geçen zamana, yani mikrosaniyelere ölçekleyin:
uint64_t value_diff = tick_value - prev_tick_value;
/* To prevent overflow */
value_diff /= 1000;
value_diff *= freq_num;
value_diff /= freq_denom;
Bir taşmayı önlemenin ana fikri, pay ve paydayı kullanmadan önce işaretlerin ölçeğini istenen doğrulukta küçültmektir. İlk zamanlayıcı çözünürlüğü nanosaniye cinsinden olduğundan, onu 1000
mikrosaniye elde etmek için böleriz . Chromium'un time_mac.c'sinde kullanılanla aynı yaklaşımı bulabilirsiniz . Gerçekten bir nanosaniye doğruluğuna ihtiyacınız varsa, mach_absolute_time'ı taşmadan nasıl kullanabilirim? .
Linux ve UNIX
clock_gettime
Çağrı herhangi POSIX dostu sistem üzerinde en iyi yoldur. Farklı saat kaynaklarından zamanı sorgulayabilir ve ihtiyacımız olan şeydir CLOCK_MONOTONIC
. clock_gettime
Desteği olan tüm sistemler değil CLOCK_MONOTONIC
, bu nedenle yapmanız gereken ilk şey kullanılabilirliğini kontrol etmektir:
_POSIX_MONOTONIC_CLOCK
bir değere tanımlanır >= 0
bu demektirCLOCK_MONOTONIC
avaiable;eğer _POSIX_MONOTONIC_CLOCK
üzere tanımlanır 0
bunun zamanında çalışır eğer ayrıca kontrol gerektiği anlamına gelir, ben kullanımına önermek sysconf
:
#include <unistd.h>
#ifdef _SC_MONOTONIC_CLOCK
if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
/* A monotonic clock presents */
}
#endif
Kullanımı clock_gettime
oldukça basittir:
zaman değerini alın:
#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;
}
Burada zamanı mikrosaniyelere düşürdüm.
aynı şekilde alınan önceki zaman değeriyle farkı hesaplayın:
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;
En iyi geri dönüş stratejisi, gettimeofday
aramayı kullanmaktır : tekdüze değildir, ancak oldukça iyi bir çözüm sağlar. Fikir ile aynıdır clock_gettime
, ancak bir zaman değeri elde etmek için şunları yapmalısınız:
#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;
}
Yine, zaman değeri mikrosaniyelere küçültülür.
SGI IRIX
IRIX vardır clock_gettime
çağrıyı ama yoksun CLOCK_MONOTONIC
. Bunun CLOCK_SGI_CYCLE
yerine, CLOCK_MONOTONIC
with yerine kullanmanız gereken şekilde tanımlanan kendi monoton saat kaynağına sahiptir clock_gettime
.
Solaris ve HP-UX
Solaris, gethrtime
nanosaniye cinsinden geçerli zamanlayıcı değerini döndüren kendi yüksek çözünürlüklü zamanlayıcı arayüzüne sahiptir. Solaris'in daha yeni sürümleri olsa da clock_gettime
, gethrtime
eski Solaris sürümlerini desteklemeniz gerekiyorsa devam edebilirsiniz.
Kullanımı basittir:
#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 eksiktir clock_gettime
, ancak desteklergethrtime
hangisini Solaris'te olduğu gibi kullanmanız gerektiğini .
BeOS
BeOS ayrıca, system_time
bilgisayarın önyüklenmesinden bu yana geçen mikrosaniye sayısını döndüren kendi yüksek çözünürlüklü zamanlayıcı arayüzüne sahiptir.
Örnek kullanım:
#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 , yüksek hassasiyetli zaman damgalarını almak için kendi API'sine sahiptir:
DosTmrQueryFreq
(GCC derleyicisi için ) ile bir zamanlayıcı frekansı (birim başına tıklama) sorgulama :
#define INCL_DOSPROFILE
#define INCL_DOSERRORS
#include <os2.h>
#include <stdint.h>
ULONG freq;
DosTmrQueryFreq (&freq);
şununla geçerli kene değerini sorgulayın 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;
}
keneleri geçen zamana, yani mikrosaniyelere ölçekleyin:
uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);
Örnek uygulama
Yukarıda açıklanan tüm stratejileri uygulayan plibsys kitaplığına göz atabilirsiniz (ayrıntılar için ptimeprofiler * .c'ye bakın).
timespec_get
: stackoverflow.com/a/36095407/895245
timespec_get
tekdüze değildir.
timespec_get
C11'den
Uygulamanın çözünürlüğüne yuvarlanmış nanosaniyeye kadar geri döner.
POSIX'ten bir ANSI kopması gibi görünüyor clock_gettime
.
Örnek: a printf
, Ubuntu 15.10'da her 100 ms'de yapılır:
#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 standart taslak 7.27.2.5 "timespec_get fonksiyonu diyor":
Taban TIME_UTC ise tv_sec üyesi, bir uygulama tanımlı dönemden bu yana geçen saniye sayısına ayarlanır, tam bir değere kısaltılır ve tv_nsec üyesi, sistem saatinin çözünürlüğüne yuvarlanan nanosaniye integral sayısına ayarlanır. (321)
321) Bir struct timespec nesnesi zamanları nanosaniye çözünürlükle tanımlasa da, mevcut çözünürlük sisteme bağlıdır ve hatta 1 saniyeden büyük olabilir.
C ++ 11 ayrıca std::chrono::high_resolution_clock
şunları da içerir : C ++ Çapraz Platform Yüksek Çözünürlük Zamanlayıcısı
glibc 2.21 uygulaması
Şu şekilde bulunabilir 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;
}
çok açıkça:
sadece TIME_UTC
şu anda desteklenmektedir
bu ileri doğru __clock_gettime (CLOCK_REALTIME, ts)
, POSIX API olan: http://pubs.opengroup.org/onlinepubs/9699919799/functions/clock_getres.html
Linux x86-64'te bir clock_gettime
sistem çağrısı var.
Bunun, hataya dayanıklı bir mikro kıyaslama yöntemi olmadığını unutmayın, çünkü:
man clock_gettime
, programınız çalışırken bazı sistem saati ayarını değiştirirseniz bu önlemin süreksizliklere sahip olabileceğini söylüyor. Bu elbette nadir bir olay olmalı ve bunu görmezden gelebilirsiniz.
bu duvar zamanını ölçer, bu nedenle zamanlayıcı görevinizi unutmaya karar verirse, daha uzun süre çalışıyor gibi görünecektir.
Bu nedenlerle getrusage()
, daha düşük mikrosaniye maksimum hassasiyetine rağmen, daha iyi bir POSIX kıyaslama aracı olabilir.
Daha fazla bilgi: Linux'ta zamanı ölçün - zaman - saat - getrusage - clock_gettime - gettimeofday - timespec_get?
Muhtemelen elde edebileceğiniz en iyi kesinlik, saat düzeyinde çözünürlük sağlayabilen yalnızca x86 "rdtsc" talimatının kullanılmasıdır (tabii ki, rdtsc çağrısının maliyetini hesaba katmak gerekir, bu da kolayca ölçülebilir) uygulama başlangıcı).
Buradaki ana nokta, çok zor olmaması gereken saniyedeki saat sayısını ölçmektir.
Kabul edilen cevap yeterince iyi ama benim çözümüm daha basit. Sadece Linux'ta test ediyorum, gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0 kullanıyorum.
Alse kullanımı gettimeofday
, tv_sec
saniyenin parçasıdır ve tv_usec
bir mikrosaniye değil milisaniye .
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;
}
Yazdır:
1522139691342
1522139692342
tam olarak bir saniye.