Chỉ sử dụng ANSI C, có cách nào để đo thời gian với độ chính xác mili giây hoặc hơn không? Tôi đã duyệt time.h nhưng tôi chỉ tìm thấy các chức năng chính xác thứ hai.
Chỉ sử dụng ANSI C, có cách nào để đo thời gian với độ chính xác mili giây hoặc hơn không? Tôi đã duyệt time.h nhưng tôi chỉ tìm thấy các chức năng chính xác thứ hai.
Câu trả lời:
Không có chức năng ANSI C cung cấp độ phân giải thời gian tốt hơn 1 giây nhưng chức năng POSIX gettimeofday
cung cấp độ phân giải micro giây. Chức năng đồng hồ chỉ đo lượng thời gian mà một quy trình đã dành để thực hiện và không chính xác trên nhiều hệ thống.
Bạn có thể sử dụng chức năng này như thế này:
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);
Điều này trả Time elapsed: 1.000870
về máy của tôi.
timeval::tv_usec
là luôn dưới một giây, nó lặp đi lặp lại. Tức là để có sự khác biệt về thời gian lớn hơn 1 giây, bạn nên:long usec_diff = (e.tv_sec - s.tv_sec)*1000000 + (e.tv_usec - s.tv_usec);
timersub
hàm bên trong hàm. Chúng ta có thể sử dụng tval_result
các giá trị (tv_sec và tv_usec) như hiện trạng.
#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);
CLOCKS_PER_SEC / 1000
có thể không chính xác có thể ảnh hưởng đến kết quả cuối cùng (mặc dù theo kinh nghiệm của tôi CLOCKS_PER_SEC
luôn là bội số của 1000). Làm (1000 * clock()) / CLOCKS_PER_SEC
là ít dễ bị phân chia không chính xác, nhưng mặt khác dễ bị tràn hơn. Chỉ cần một số vấn đề để xem xét.
Tôi luôn sử dụng hàm clock_gettime (), trả về thời gian từ đồng hồ CLOCK_MONOTONIC. Thời gian được trả về là lượng thời gian, tính bằng giây và nano giây, vì một số điểm không xác định trong quá khứ, chẳng hạn như khởi động hệ thống của thời đại.
#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, ...)
và thậm chí còn có macro kiểm tra tính năng _POSIX_MONOTONIC_CLOCK
.
Thực hiện một giải pháp di động
Như đã đề cập ở đây rằng không có giải pháp ANSI phù hợp với độ chính xác đủ cho vấn đề đo thời gian, tôi muốn viết về cách lấy di động và, nếu có thể, giải pháp đo thời gian có độ phân giải cao.
Đồng hồ đơn điệu so với tem thời gian
Nói chung, có hai cách đo thời gian:
Cái đầu tiên sử dụng bộ đếm đồng hồ đơn điệu (đôi khi nó được gọi là bộ đếm tick) đếm số lần đánh dấu với tần số được xác định trước, vì vậy nếu bạn có giá trị tick và tần số được biết, bạn có thể dễ dàng chuyển đổi tick thành thời gian trôi qua. Thực tế không đảm bảo rằng đồng hồ đơn điệu phản ánh thời gian hệ thống hiện tại theo bất kỳ cách nào, nó cũng có thể đếm tích tắc kể từ khi khởi động hệ thống. Nhưng nó đảm bảo rằng một chiếc đồng hồ luôn luôn chạy theo xu hướng ngày càng tăng bất kể trạng thái hệ thống. Thông thường tần số được liên kết với nguồn có độ phân giải cao phần cứng, đó là lý do tại sao nó cung cấp độ chính xác cao (phụ thuộc vào phần cứng, nhưng hầu hết phần cứng hiện đại không có vấn đề với nguồn đồng hồ có độ phân giải cao).
Cách thứ hai cung cấp giá trị thời gian (ngày) dựa trên giá trị đồng hồ hệ thống hiện tại. Nó cũng có thể có độ phân giải cao, nhưng nó có một nhược điểm lớn: loại giá trị thời gian này có thể bị ảnh hưởng bởi các điều chỉnh thời gian hệ thống khác nhau, tức là thay đổi múi giờ, thay đổi thời gian ban ngày (DST), cập nhật máy chủ NTP, ngủ đông hệ thống, v.v. trên. Trong một số trường hợp, bạn có thể nhận được giá trị thời gian trôi qua tiêu cực có thể dẫn đến một hành vi không xác định. Trên thực tế loại nguồn thời gian này ít đáng tin cậy hơn nguồn đầu tiên.
Vì vậy, quy tắc đầu tiên trong đo khoảng thời gian là sử dụng đồng hồ đơn điệu nếu có thể. Nó thường có độ chính xác cao, và nó đáng tin cậy bởi thiết kế.
Chiến lược dự phòng
Khi thực hiện một giải pháp di động, đáng để xem xét một chiến lược dự phòng: sử dụng đồng hồ đơn điệu nếu có sẵn và dự phòng theo cách tiếp cận tem thời gian nếu không có đồng hồ đơn điệu trong hệ thống.
các cửa sổ
Có một bài viết tuyệt vời có tên Thu thập tem thời gian độ phân giải cao trên MSDN về đo thời gian trên Windows mô tả tất cả các chi tiết bạn có thể cần biết về hỗ trợ phần mềm và phần cứng. Để có được dấu thời gian chính xác cao trên Windows, bạn nên:
truy vấn tần số hẹn giờ (đánh dấu mỗi giây) với QueryPerformanceFrequency :
LARGE_INTEGER tcounter;
LARGE_INTEGER freq;
if (QueryPerformanceFrequency (&tcounter) != 0)
freq = tcounter.QuadPart;
Tần số hẹn giờ được cố định khi khởi động hệ thống, do đó bạn chỉ cần lấy một lần.
truy vấn giá trị tick hiện tại với QueryPerformanceCorer :
LARGE_INTEGER tcounter;
LARGE_INTEGER tick_value;
if (QueryPerformanceCounter (&tcounter) != 0)
tick_value = tcounter.QuadPart;
chia tỷ lệ tích tắc thành thời gian trôi qua, tức là đến micro giây:
LARGE_INTEGER usecs = (tick_value - prev_tick_value) / (freq / 1000000);
Theo Microsoft, bạn không nên có bất kỳ vấn đề nào với cách tiếp cận này trên Windows XP và các phiên bản mới hơn trong hầu hết các trường hợp. Nhưng bạn cũng có thể sử dụng hai giải pháp dự phòng trên Windows:
GetTickCount
, nhưng nó có sẵn bắt đầu từ Windows Vista trở lên.Hệ điều hành X (macOS)
OS X (macOS) có đơn vị thời gian tuyệt đối Mach riêng đại diện cho đồng hồ đơn điệu. Cách tốt nhất để bắt đầu là bài viết của Apple Q & A QA1398: Đơn vị thời gian tuyệt đối Mach mô tả (với các ví dụ mã) cách sử dụng API dành riêng cho Mach để có dấu tick đơn điệu. Ngoài ra còn có một câu hỏi cục bộ về nó được gọi là thay thế clock_gettime trong Mac OS X , ở phần cuối có thể khiến bạn hơi bối rối không biết phải làm gì với tràn giá trị có thể vì tần số bộ đếm được sử dụng ở dạng tử số và mẫu số. Vì vậy, một ví dụ ngắn làm thế nào để có được thời gian trôi qua:
lấy tử số và mẫu số tần số đồng hồ:
#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;
}
}
Bạn chỉ cần làm điều đó một lần.
truy vấn giá trị đánh dấu hiện tại với mach_absolute_time
:
uint64_t tick_value = mach_absolute_time ();
chia tỷ lệ các dấu tick thành thời gian đã trôi qua, tức là đến micro giây, sử dụng tử số và mẫu số được truy vấn trước đó:
uint64_t value_diff = tick_value - prev_tick_value;
/* To prevent overflow */
value_diff /= 1000;
value_diff *= freq_num;
value_diff /= freq_denom;
Ý tưởng chính để ngăn chặn tràn là giảm tỷ lệ tích tắc xuống độ chính xác mong muốn trước khi sử dụng tử số và mẫu số. Vì độ phân giải bộ đếm thời gian ban đầu là tính bằng nano giây, chúng tôi chia nó 1000
cho để có được micro giây. Bạn có thể tìm thấy cách tiếp cận tương tự được sử dụng trong thời gian_mac.c của Chromium . Nếu bạn thực sự cần độ chính xác nano giây, hãy cân nhắc đọc Làm thế nào tôi có thể sử dụng mach_absolute_time mà không bị tràn? .
Linux và UNIX
Cuộc clock_gettime
gọi là cách tốt nhất của bạn trên bất kỳ hệ thống thân thiện POSIX nào. Nó có thể truy vấn thời gian từ các nguồn đồng hồ khác nhau, và cái chúng ta cần là CLOCK_MONOTONIC
. Không phải tất cả các hệ thống có clock_gettime
hỗ trợ CLOCK_MONOTONIC
, vì vậy điều đầu tiên bạn cần làm là kiểm tra tính khả dụng của nó:
_POSIX_MONOTONIC_CLOCK
được định nghĩa theo một giá trị >= 0
thì có nghĩa CLOCK_MONOTONIC
là có sẵn;nếu _POSIX_MONOTONIC_CLOCK
được định nghĩa cho 0
nó có nghĩa là bạn nên kiểm tra thêm nếu nó hoạt động trong thời gian chạy, tôi đề nghị sử dụng sysconf
:
#include <unistd.h>
#ifdef _SC_MONOTONIC_CLOCK
if (sysconf (_SC_MONOTONIC_CLOCK) > 0) {
/* A monotonic clock presents */
}
#endif
Cách sử dụng clock_gettime
khá dễ dàng:
lấy giá trị thời gian:
#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;
}
Tôi đã giảm thời gian xuống còn micro giây ở đây.
tính toán chênh lệch với giá trị thời gian trước đó nhận được theo cùng một cách:
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;
Chiến lược dự phòng tốt nhất là sử dụng lệnh gettimeofday
gọi: nó không phải là đơn điệu, nhưng nó cung cấp độ phân giải khá tốt. Ý tưởng tương tự như với clock_gettime
, nhưng để có được giá trị thời gian, bạn nên:
#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;
}
Một lần nữa, giá trị thời gian được thu nhỏ lại thành micro giây.
SGI IRIX
IRIX có clock_gettime
cuộc gọi, nhưng nó thiếu CLOCK_MONOTONIC
. Thay vào đó, nó có nguồn đồng hồ đơn điệu của riêng nó được xác định CLOCK_SGI_CYCLE
mà bạn nên sử dụng thay vì CLOCK_MONOTONIC
với clock_gettime
.
Solaris và HP-UX
Solaris có giao diện hẹn giờ độ phân giải cao của riêng nó gethrtime
trả về giá trị bộ hẹn giờ hiện tại tính bằng nano giây. Mặc dù các phiên bản mới hơn của Solaris có thể có clock_gettime
, nhưng bạn có thể sử dụng gethrtime
nếu bạn cần hỗ trợ các phiên bản Solaris cũ.
Cách sử dụng rất đơn giản:
#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 thiếu clock_gettime
, nhưng nó hỗ trợ gethrtime
mà bạn nên sử dụng giống như trên Solaris.
BeOS
BeOS cũng có giao diện hẹn giờ độ phân giải cao của riêng nó system_time
, trả về số lượng micro giây đã trôi qua kể từ khi máy tính được khởi động.
Ví dụ sử dụng:
#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;
}
HĐH / 2
OS / 2 có API riêng để lấy dấu thời gian có độ chính xác cao:
truy vấn tần số hẹn giờ (đánh dấu trên mỗi đơn vị) với DosTmrQueryFreq
(đối với trình biên dịch GCC):
#define INCL_DOSPROFILE
#define INCL_DOSERRORS
#include <os2.h>
#include <stdint.h>
ULONG freq;
DosTmrQueryFreq (&freq);
truy vấn giá trị tick hiện tại với 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;
}
chia tỷ lệ tích tắc thành thời gian trôi qua, tức là đến micro giây:
uint64_t usecs = (prev_timestamp - timestamp) / (freq / 1000000);
Ví dụ thực hiện
Bạn có thể xem thư viện plibsys thực hiện tất cả các chiến lược được mô tả ở trên (xem ptimeprofiler * .c để biết chi tiết).
timespec_get
: stackoverflow.com/a/36095407/895245
timespec_get
không đơn điệu.
timespec_get
từ C11
Trả về tối đa nano giây, làm tròn đến độ phân giải của việc thực hiện.
Trông giống như một bản trích xuất ANSI từ POSIX ' clock_gettime
.
Ví dụ: a printf
được thực hiện cứ sau 100ms trên Ubuntu 15.10:
#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;
}
Bản nháp tiêu chuẩn C11 N1570 7.27.2.5 "Hàm timespec_get nói":
Nếu cơ sở là TIME_UTC, thành viên tv_sec được đặt thành số giây kể từ khi một epoch được xác định thực hiện, được cắt thành toàn bộ giá trị và thành viên tv_nsec được đặt thành số lượng nano giây, được làm tròn thành độ phân giải của đồng hồ hệ thống. (321)
321) Mặc dù một đối tượng timespec cấu trúc mô tả thời gian với độ phân giải nano giây, độ phân giải khả dụng phụ thuộc vào hệ thống và thậm chí có thể lớn hơn 1 giây.
C ++ 11 cũng có std::chrono::high_resolution_clock
: Bộ định thời độ phân giải cao đa nền tảng C ++
glibc 2.21 thực hiện
Có thể được tìm thấy dưới sysdeps/posix/timespec_get.c
đây:
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;
}
rất rõ ràng
hiện chỉ TIME_UTC
được hỗ trợ
nó chuyển tiếp tới __clock_gettime (CLOCK_REALTIME, ts)
, đó là API POSIX: http://pub.opengroup.org/onlinepub/9699919799/fifts/clock_getres.html
Linux x86-64 có một clock_gettime
cuộc gọi hệ thống.
Lưu ý rằng đây không phải là một phương pháp đo điểm chuẩn không bằng chứng vì:
man clock_gettime
nói rằng biện pháp này có thể có sự không liên tục nếu bạn thay đổi một số cài đặt thời gian hệ thống trong khi chương trình của bạn chạy. Tất nhiên đây là một sự kiện hiếm hoi và bạn có thể bỏ qua nó.
Điều này đo thời gian trên tường, vì vậy nếu người lập lịch quyết định quên nhiệm vụ của bạn, nó sẽ xuất hiện để chạy lâu hơn.
Vì những lý do đó getrusage()
có thể là một công cụ đo điểm chuẩn POSIX tốt hơn, mặc dù độ chính xác tối đa micro giây thấp hơn.
Thêm thông tin tại: Đo thời gian trong Linux - thời gian so với đồng hồ so với getrusage vs clock_gettime vs gettimeofday vs timespec_get?
Độ chính xác tốt nhất mà bạn có thể có được là thông qua việc sử dụng hướng dẫn "rdtsc" chỉ x86, có thể cung cấp độ phân giải ở mức đồng hồ (tất nhiên phải tính đến chi phí của chính cuộc gọi ndtsc, có thể dễ dàng đo được khởi động ứng dụng).
Cái bắt chính ở đây là đo số lượng đồng hồ mỗi giây, không quá khó.
Câu trả lời được chấp nhận là đủ tốt. Nhưng giải pháp của tôi đơn giản hơn. Tôi chỉ cần thử nghiệm trong Linux, sử dụng gcc (Ubuntu 7.2.0-8ubfox3.2) 7.2.0.
Alse sử dụng gettimeofday
, tv_sec
là một phần của giây và tv_usec
là micro giây , không phải mili giây .
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;
}
Nó in:
1522139691342
1522139692342
, chính xác là một giây.