การใช้ ANSI C เพียงอย่างเดียวมีวิธีใดในการวัดเวลาด้วยความแม่นยำระดับมิลลิวินาทีขึ้นไปหรือไม่? ฉันกำลังเรียกดูเวลา h แต่ฉันพบฟังก์ชันความแม่นยำที่สองเท่านั้น
การใช้ ANSI C เพียงอย่างเดียวมีวิธีใดในการวัดเวลาด้วยความแม่นยำระดับมิลลิวินาทีขึ้นไปหรือไม่? ฉันกำลังเรียกดูเวลา h แต่ฉันพบฟังก์ชันความแม่นยำที่สองเท่านั้น
คำตอบ:
ไม่มีฟังก์ชัน ANSI C ที่ให้ความละเอียดได้ดีกว่า 1 วินาที แต่ฟังก์ชัน 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 วินาทีคุณควร: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
จะเป็นผลคูณของ 1,000 เสมอ) การทำ(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 ที่เหมาะสมที่มีความแม่นยำเพียงพอสำหรับปัญหาการวัดเวลาฉันต้องการเขียนเกี่ยวกับวิธีการรับอุปกรณ์พกพาและวิธีการวัดเวลาที่มีความละเอียดสูงหากเป็นไปได้
นาฬิกาเสียงเดียวเทียบกับการประทับเวลา
โดยทั่วไปการวัดเวลามีสองวิธี:
ตัวแรกใช้ตัวนับนาฬิกาแบบโมโนโทนิค (บางครั้งเรียกว่าตัวนับเห็บ) ซึ่งจะนับเห็บด้วยความถี่ที่กำหนดไว้ล่วงหน้าดังนั้นหากคุณมีค่าเห็บและทราบความถี่คุณสามารถแปลงเห็บเป็นเวลาที่ผ่านไปได้อย่างง่ายดาย จริง ๆ แล้วไม่รับประกันว่านาฬิกาโมโนโทนิกจะสะท้อนเวลาของระบบปัจจุบันไม่ว่าในทางใดก็ตามมันอาจนับเห็บตั้งแต่เริ่มต้นระบบ แต่รับประกันได้ว่านาฬิกาจะทำงานในรูปแบบที่เพิ่มมากขึ้นโดยไม่คำนึงถึงสถานะของระบบ โดยปกติความถี่จะถูกผูกไว้กับแหล่งที่มาของฮาร์ดแวร์ที่มีความละเอียดสูงนั่นเป็นเหตุผลว่าทำไมจึงให้ความแม่นยำสูง (ขึ้นอยู่กับฮาร์ดแวร์ แต่ฮาร์ดแวร์สมัยใหม่ส่วนใหญ่ไม่มีปัญหากับแหล่งสัญญาณนาฬิกาที่มีความละเอียดสูง)
วิธีที่สองให้ค่าเวลา (วันที่) ตามค่านาฬิกาของระบบปัจจุบัน นอกจากนี้ยังอาจมีความละเอียดสูง แต่ก็มีข้อเสียเปรียบที่สำคัญอย่างหนึ่งค่าเวลาประเภทนี้อาจได้รับผลกระทบจากการปรับเปลี่ยนเวลาของระบบที่แตกต่างกันเช่นการเปลี่ยนโซนเวลาการเปลี่ยนแปลงเวลาออมแสง (DST) การอัปเดตเซิร์ฟเวอร์ NTP การไฮเบอร์เนตของระบบและอื่น ๆ บน. ในบางสถานการณ์คุณอาจได้รับค่าเวลาที่ผ่านไปเป็นลบซึ่งอาจนำไปสู่พฤติกรรมที่ไม่ได้กำหนดได้ อันที่จริงแหล่งเวลาประเภทนี้มีความน่าเชื่อถือน้อยกว่าแหล่งแรก
ดังนั้นกฎข้อแรกในการวัดช่วงเวลาคือการใช้นาฬิกาแบบโมโนโทนิคถ้าเป็นไปได้ โดยปกติจะมีความแม่นยำสูงและมีความน่าเชื่อถือจากการออกแบบ
กลยุทธ์ทางเลือก
เมื่อใช้โซลูชันแบบพกพาคุณควรพิจารณากลยุทธ์ทางเลือก: ใช้นาฬิกาโมโนโทนิกหากมีและใช้วิธีการประทับเวลาแทนหากไม่มีนาฬิกาโมโนโทนิกในระบบ
ของ windows
มีบทความดีๆที่ชื่อว่าAcquiring high-resolution time stampบน MSDN เกี่ยวกับการวัดเวลาบน Windows ซึ่งอธิบายรายละเอียดทั้งหมดที่คุณอาจต้องรู้เกี่ยวกับการสนับสนุนซอฟต์แวร์และฮาร์ดแวร์ ในการรับการประทับเวลาที่มีความแม่นยำสูงบน Windows คุณควร:
ค้นหาความถี่ตัวจับเวลา (ขีดต่อวินาที) ด้วยQueryPerformanceFrequency :
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:
GetTickCount
แต่สามารถใช้ได้ตั้งแต่ Windows Vista ขึ้นไปOS X (macOS)
OS X (macOS) มีหน่วยเวลาสัมบูรณ์ของ Mach ซึ่งเป็นตัวแทนของนาฬิกาแบบโมโนโทนิค วิธีที่ดีที่สุดในการเริ่มต้นคือบทความของ Apple Q&A ทางเทคนิค QA1398: Mach Absolute Time Unitsซึ่งอธิบาย (พร้อมตัวอย่างโค้ด) วิธีการใช้ Mach-specific API เพื่อรับเครื่องหมายโมโนโทนิก นอกจากนี้ยังมีคำถามในท้องถิ่นที่เรียกว่าclock_gettime ทางเลือกใน Mac OS Xซึ่งในตอนท้ายอาจทำให้คุณสับสนเล็กน้อยว่าจะทำอย่างไรกับค่าที่เป็นไปได้มากเกินไปเนื่องจากความถี่ของตัวนับถูกใช้ในรูปแบบของตัวเศษและตัวส่วน ตัวอย่างสั้น ๆ เกี่ยวกับการใช้เวลาที่ผ่านไป:
รับตัวนับความถี่นาฬิกาและตัวส่วน:
#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
เพื่อให้ได้ไมโครวินาที คุณสามารถค้นหาวิธีการเดียวกับที่ใช้ในโครเมี่ยมของ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
ส่งกลับค่าเป็นนาโนวินาทีปัดตามความละเอียดของการนำไปใช้งาน
หน้าตาเหมือนกันหา ANSI จาก clock_gettime
POSIX'
ตัวอย่าง: a printf
จะทำทุกๆ 100ms บน 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;
}
ร่าง C11 N1570 มาตรฐาน 7.27.2.5 "ฟังก์ชั่น timespec_get ว่า":
ถ้าฐานคือ TIME_UTC สมาชิก tv_sec จะถูกตั้งค่าเป็นจำนวนวินาทีนับตั้งแต่ยุคที่กำหนดการนำไปใช้งานซึ่งจะถูกตัดทอนเป็นค่าทั้งหมดและสมาชิก tv_nsec จะถูกตั้งค่าเป็นจำนวนหนึ่งในนาโนวินาทีโดยปัดตามความละเอียดของนาฬิการะบบ (321)
321) แม้ว่าออบเจ็กต์ไทม์สเปคโครงสร้างจะอธิบายเวลาด้วยความละเอียดระดับนาโนวินาที แต่ความละเอียดที่มีนั้นขึ้นอยู่กับระบบและอาจมากกว่า 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 ที่ดีกว่าแม้ว่าจะมีความแม่นยำสูงสุดระดับไมโครวินาทีที่ต่ำกว่าก็ตาม
ข้อมูลเพิ่มเติมที่: Measure time in Linux - time vs clock vs getrusage vs clock_gettime vs gettimeofday vs timespec_get?
ความแม่นยำที่ดีที่สุดที่คุณจะได้รับคือการใช้คำสั่ง "rdtsc" x86 เท่านั้นซึ่งสามารถให้ความละเอียดระดับนาฬิกาได้ (แน่นอนว่าต้องคำนึงถึงต้นทุนของการเรียก rdtsc ซึ่งสามารถวัดได้อย่างง่ายดายบน การเริ่มต้นแอปพลิเคชัน)
การจับหลักที่นี่คือการวัดจำนวนนาฬิกาต่อวินาทีซึ่งไม่ควรยากเกินไป
คำตอบที่ยอมรับนั้นดีพอ แต่วิธีแก้ปัญหาของฉันนั้นง่ายกว่าฉันแค่ทดสอบใน Linux ใช้ gcc (Ubuntu 7.2.0-8ubuntu3.2) 7.2.0
ใช้ alse gettimeofday
ที่tv_sec
เป็นส่วนหนึ่งของที่สองและtv_usec
เป็นmicrosecondsไม่มิลลิวินาที
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
วินาทีนั้นเอง
ใต้หน้าต่าง:
SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);