كيفية قياس الوقت بالمللي ثانية باستخدام ANSI C ؟

StackOverflow https://stackoverflow.com/questions/361363

  •  21-08-2019
  •  | 
  •  

سؤال

فقط باستخدام ANSI C, هل هناك أي طريقة لقياس الوقت مع الدقة ميلي ثانية أو أكثر ؟ كنت أتصفح الوقت.ح ولكن أنا فقط وجدت الدقة الثانية المهام.

هل كانت مفيدة؟

المحلول

وليس هناك وظيفة ANSI C التي توفر أفضل من 1 قرار ثان الوقت ولكن وظيفة POSIX <لأ href = "http://www.opengroup.org/onlinepubs/000095399/functions/gettimeofday.html" يختلط = "noreferrer "> 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 على الجهاز الخاص بي.

نصائح أخرى

#include <time.h>
clock_t uptime = clock() / (CLOCKS_PER_SEC / 1000);

وأنا دائما استخدام وظيفة 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);
}

تنفيذ المحمولة الحل

كما ذكر هنا أنه لا يوجد السليم ANSI الحل بدقة كافية لقياس الوقت مشكلة ، أريد أن أكتب عن طرق كيفية الحصول على جهاز محمول و إذا أمكن عالية الدقة قياس الوقت الحل.

رتيب على مدار الساعة مقابلالطوابع الزمنية

عموما هناك طريقتان قياس الوقت:

  • رتيب على مدار الساعة.
  • الحالي (تاريخ)ختم الوقت.

أول واحد يستخدم رتيب الساعة العداد (أحيانا يطلق عليه علامة العداد) التي تعتبر القراد مع محددة مسبقا تردد, حتى إذا كان لديك القراد القيمة والتردد هو معروف ، يمكنك بسهولة تحويل القراد إلى الوقت المنقضي.هو في الواقع لا يضمن أن رتيب الساعة يعكس وقت النظام الحالي بأي طريقة ، كما يجوز الاعتماد القراد منذ بدء تشغيل النظام.ولكن من يضمن أن الساعة تعمل دائما في زيادة الموضة بغض النظر عن نظام الدولة.عادة التردد لا بد أن الأجهزة عالية الدقة المصدر, وهذا هو السبب في أنه يوفر درجة عالية من الدقة (يعتمد على الأجهزة ، ولكن معظم الأجهزة الحديثة لا يوجد لديه مشاكل مع عالية الدقة على مدار الساعة المصادر).

الطريقة الثانية توفر (تاريخ)القيمة الزمنية على أساس النظام الحالي على مدار الساعة القيمة.قد يكون أيضا ارتفاع القرار ، ولكن لديه عيب رئيسي واحد:هذا النوع من قيمة الوقت يمكن أن تتأثر مختلفة نظام الوقت التعديلات ، أيتغيير المنطقة الزمنية ، التوقيت الصيفي (DST) تغيير, NTP server تحديث نظام السبات وهلم جرا.في بعض الحالات يمكنك الحصول على السلبية الوقت المنقضي القيمة التي يمكن أن تؤدي إلى السلوك غير معرف.في الواقع هذا النوع من مصدر وقت أقل موثوقية من أول واحد.

إذا القاعدة الأولى في الفاصل الزمني القياس هو استخدام رتيب على مدار الساعة إن أمكن.فإنه عادة ما يحتوي على نسبة عالية الدقة ، وأنه يمكن الاعتماد عليها حسب التصميم.

تراجع استراتيجية

عند تنفيذ المحمولة الحل يشار إلى أن تراجع الاستراتيجية:استخدام رتيب الساعة إذا كانت متوفرة و تراجع الطوابع الزمنية النهج إذا كان هناك أي رتيب على مدار الساعة في النظام.

ويندوز

هناك مقالة رائعة تسمى الحصول على دقة عالية الطوابع الزمنية على MSDN عن قياس الوقت على ويندوز الذي يصف كل تفاصيل قد تحتاج إلى معرفته حول الأجهزة والبرامج الدعم.للحصول على دقة عالية ختم الوقت على ويندوز يجب عليك:

  • الاستعلام الموقت تردد (القراد في الثانية) مع 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);
    

ووفقا لمايكروسوفت يجب أن لا يكون لديك أي مشاكل مع هذا النهج على ويندوز XP والإصدارات الأحدث في معظم الحالات.ولكن يمكنك أيضا استخدام اثنين من تراجع الحلول على ويندوز:

  • GetTickCount يوفر عدد المللي ثانية التي انقضت منذ بدء تشغيل النظام.فإنه يلتف كل 49.7 أيام ، لذا كن حذرا في قياس فترات أطول.
  • GetTickCount64 هو إصدار 64 بت من GetTickCount, لكنه متاح ابتداء من ويندوز فيستا وما فوق.

OS X (ماك)

OS X (ماك) الخاصة ماخ المطلق الوقت الوحدات التي تمثل رتيب على مدار الساعة.أفضل طريقة للبدء هو أبل المادة التقنية Q&A QA1398:ماخ المطلق وحدات الوقت الذي يصف (مع أمثلة التعليمات البرمجية) كيفية استخدام ماخ محددة 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.ج.إذا كنت حقا بحاجة نانوثانية دقة في النظر في قراءة كيف يمكنني استخدام mach_absolute_time دون تفيض ؟ .

لينكس و يونكس

على 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.

سولاريس و HP-UX

سولاريس لديها عالية الدقة واجهة الموقت gethrtime والتي ترجع الحالي الموقت قيمة في نانو ثانية.على الرغم من أن الإصدارات الأحدث من سولاريس قد clock_gettime, يمكنك عصا gethrtime إذا كنت بحاجة إلى دعم من العمر سولاريس الإصدارات.

استخدام بسيط:

#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 والتي يجب أن تستخدم في نفس الطريقة كما في سولاريس.

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 compiler):

    #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*.ج لمزيد من التفاصيل).

timespec_get من C11

عوائد تصل إلى نانو ثانية ، مقربة إلى قرار التنفيذ.

يبدو ANSI تهافت من POSIX' clock_gettime.

على سبيل المثال:a printf ويتم كل 100ms على أوبونتو 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) على الرغم من أن البنية timespec كائن يصف مرات مع نانوثانية القرار ، القرار هو نظام يعتمد وحتى قد يكون أكبر من 1 في الثانية.

C++11 كما حصل std::chrono::high_resolution_clock: C++ عبر منصة عالية الدقة الموقت

سي العمومية 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;
}

بوضوح:

وأفضل الدقة التي يمكن الحصول عليها من خلال استخدام من التعليمات x86 فقط "RDTSC"، التي يمكن أن توفر الدقة مستوى مدار الساعة (يجب شمال شرق بالطبع تأخذ بعين الاعتبار تكلفة RDTSC الدعوة نفسها، والتي يمكن أن تكون قياسه بسهولة على بدء تشغيل التطبيق).

والصيد الرئيسي هنا هو قياس عدد الساعات في الثانية الواحدة، والتي لا ينبغي أن يكون صعبا للغاية.

الجواب المقبول هو جيد بما فيه الكفاية.ولكن الحل هو أكثر بسيطة.أنا فقط اختبار في لينكس, استخدام دول مجلس التعاون الخليجي (أوبونتو 7.2.0-8ubuntu3.2) 7.2.0.

آخر استخدام 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, بالضبط ثانية.

وتحت نوافذ:

SYSTEMTIME t;
GetLocalTime(&t);
swprintf_s(buff, L"[%02d:%02d:%02d:%d]\t", t.wHour, t.wMinute, t.wSecond, t.wMilliseconds);
مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top