Linux'ta milisaniye süresi elde eden C ++ - clock () düzgün çalışmıyor


102

Windows'ta, clock()zamanı milisaniye cinsinden döndürür, ancak üzerinde çalıştığım bu Linux kutusunda, onu en yakın 1000'e yuvarlar, böylece hassasiyet, milisaniye düzeyinde değil, yalnızca "ikinci" düzeydedir.

QTimeSınıfı kullanarak Qt ile bir çözüm buldum , bir nesneyi start()başlattım ve onu çağırıp ardından elapsed()geçen milisaniye sayısını almak için çağırdım.

Şanslıydım çünkü başlangıçta Qt ile çalışıyorum, ancak üçüncü taraf kitaplıklara dayanmayan bir çözüm istiyorum.

Bunu yapmanın standart bir yolu yok mu?

GÜNCELLEME

Lütfen Boost'u önermeyin ..

Boost ve Qt bunu yapabiliyorsa, bu kesinlikle sihir değil, kullandıkları standart bir şey olmalı!


2
Düzenleme hakkında - ancak bunu taşınabilir bir şekilde yapmak biraz acı verir.
Anonim

Yanıtlar:


37

Gettimeofday'i yönteminizin başında ve sonunda kullanabilir ve ardından iki dönüş yapısını birbirinden ayırabilirsiniz. Aşağıdaki gibi bir yapı elde edeceksiniz:

struct timeval {
  time_t tv_sec;
  suseconds_t tv_usec;
}

DÜZENLEME: Aşağıdaki iki yorumun önerdiği gibi, clock_gettime (CLOCK_MONOTONIC) eğer varsa çok daha iyi bir seçimdir ve bu günlerde hemen hemen her yerde olmalıdır.

DÜZENLEME: Bir başkası std :: chrono :: high_resolution_clock ile modern C ++ kullanabileceğiniz yorumunu yaptı, ancak bunun monoton olacağı garanti edilmedi. Bunun yerine steady_clock kullanın.


38
ciddi işler için korkunç. Yılda iki kez, birisi -s ve tabii ki NTP senkronizasyonu yaptığında büyük sorunlar. Clock_gettime (CLOCK_MONOTONIC,) kullanın
AndrewStone

9
@AndrewStone: UNIX zamanı yılda iki kez değişmez. Hatta yılda bir kez. Ancak evet, CLOCK_MONOTONICyerelleştirilmiş sistem saati ayarlamalarından kaçınmak için harikadır.
Orbit'te Hafiflik Yarışları

138
#include <sys/time.h>
#include <stdio.h>
#include <unistd.h>
int main()
{
    struct timeval start, end;

    long mtime, seconds, useconds;    

    gettimeofday(&start, NULL);
    usleep(2000);
    gettimeofday(&end, NULL);

    seconds  = end.tv_sec  - start.tv_sec;
    useconds = end.tv_usec - start.tv_usec;

    mtime = ((seconds) * 1000 + useconds/1000.0) + 0.5;

    printf("Elapsed time: %ld milliseconds\n", mtime);

    return 0;
}

4
Farka neden +0.5 ekliyorsunuz?
Mahmoud Al-Qudsi

19
@Computer Guru, pozitif değerleri yuvarlamak için yaygın bir tekniktir. Değer bir tamsayı değerine kesildiğinde, toplama 0'a kesilmeden önce 0.0 ile 0.4999 ... ve 0.5 ile 0.9999 arasındaki herhangi bir şey ... 1'e kesilir.
Mark Ransom

8
tv_usec milisaniye değil, mikro saniyedir.
NebulaFox

13
ciddi işler için korkunç. Yılda iki kez büyük sorunlar, birisi tarih -s yaptığında ve tabii ki NTP senkronizasyonu
AndrewStone

14
@AndrewStone haklı, aynı bilgisayardaki saatleri karşılaştırmak için CLOCK_REALTIME ile clock_gettime (2) kullanın. Gettimeofday itibaren (2) manpage: POSIX.1-2008 marks gettimeofday() as obsolete, recommending the use of clock_gettime(2) instead. @CTT, sen değiştirerek örnek güncelleme olabilir struct timevaliçin struct timespecve gettimeofday(&start, NULL)karşı clock_gettime(CLOCK_MONOTONIC, &start)insanların derde kalmamak?
Bobby Powers

58

Lütfen dikkat clocketmez değil duvar saati süresini ölçün. Bu, programınız 5 saniye sürerse, 5 saniyeyi clockölçmeyeceği, ancak daha fazlasını (programınız birden fazla iş parçacığı çalıştırabilir ve böylece gerçek zamandan daha fazla CPU tüketebilir) veya daha azını ölçebileceği anlamına gelir . Kullanılan yaklaşık CPU zamanını ölçer . Farkı görmek için bu kodu düşünün

#include <iostream>
#include <ctime>
#include <unistd.h>

int main() {
    std::clock_t a = std::clock();
    sleep(5); // sleep 5s
    std::clock_t b = std::clock();

    std::cout << "difference: " << (b - a) << std::endl;
    return 0;
}

Sistemime çıktı

$ difference: 0

Çünkü tek yaptığımız uyumak ve CPU zamanı kullanmamaktı! Ancak kullanarak gettimeofdayistediğimizi elde ederiz (?)

#include <iostream>
#include <ctime>
#include <unistd.h>
#include <sys/time.h>

int main() {
    timeval a;
    timeval b;

    gettimeofday(&a, 0);
    sleep(5); // sleep 5s
    gettimeofday(&b, 0);

    std::cout << "difference: " << (b.tv_sec - a.tv_sec) << std::endl;
    return 0;
}

Sistemimdeki çıktılar

$ difference: 5

Daha fazla hassasiyete ihtiyacınız varsa ancak CPU zamanı elde etmek istiyorsanız , bu getrusageişlevi kullanmayı düşünebilirsiniz .


⁺¹ hakkında sayılabilir sleep()- Zaten bir soru sormak düşünce am (öyle neden ?! Ben hariç herkes cezasını çalışır) , zaman cevabını buldu.
Hi-Angel

18

Ayrıca Boost'un sunduğu araçları da tavsiye ederim. Bahsedilen Boost Timer veya Boost.DateTime'dan bir şey hackleyin veya sandbox'ta yeni önerilen kitaplık var - Boost.Chrono : Bu sonuncusu Timer'ın yerini alacak ve şunları içerecek:

  • C ++ 0x Standart Kitaplığı'nın zaman yardımcı programları dahil:
    • Sınıf şablonu duration
    • Sınıf şablonu time_point
    • Saatler:
      • system_clock
      • monotonic_clock
      • high_resolution_clock
  • timerTypedef'ler içeren sınıf şablonu :
    • system_timer
    • monotonic_timer
    • high_resolution_timer
  • Süreç saatleri ve zamanlayıcılar:
    • process_clock, gerçek, kullanıcı-CPU ve sistem-CPU zamanlarını yakalar.
    • process_timer, geçen gerçek, kullanıcı-CPU ve sistem-CPU sürelerini yakalar.
    • run_timer, uygun raporlama | process_timer | Sonuçlar.
  • C ++ 0x Standart Kitaplığı'nın derleme zamanı rasyonel aritmetiği.

İşte özellik listesinin kaynağı


Şimdilik Boost Timer'ı kullanabilir ve ardından incelendiğinde / kabul edildiğinde zarif bir şekilde Chrono'ya geçebilirsiniz.
Anonim

13

CTT'nin cevabınaTimer göre bir ders yazdım . Aşağıdaki şekilde kullanılabilir:

Timer timer = Timer();
timer.start();
/* perform task */
double duration = timer.stop();
timer.printTime(duration);

İşte uygulaması:

#include <stdio.h>
#include <stdlib.h>
#include <sys/time.h>
using namespace std;

class Timer {
private:

    timeval startTime;

public:

    void start(){
        gettimeofday(&startTime, NULL);
    }

    double stop(){
        timeval endTime;
        long seconds, useconds;
        double duration;

        gettimeofday(&endTime, NULL);

        seconds  = endTime.tv_sec  - startTime.tv_sec;
        useconds = endTime.tv_usec - startTime.tv_usec;

        duration = seconds + useconds/1000000.0;

        return duration;
    }

    static void printTime(double duration){
        printf("%5.6f seconds\n", duration);
    }
};

2
Bu harika ama "nseconds" yanıltıcı çünkü timeval nanosaniye değil, mikrosaniye tutuyor, bu yüzden insanların buna "kullanım saniyesi" demelerini öneririm.
pho0

Teşekkürler. Düzeltme yapıldı.
Chris Redford

9

Eski aygıtlara taşınabilir olması için koda ihtiyacınız yoksa clock_gettime () kullanabilirsiniz, bu size zamanı nanosaniye cinsinden verir (işlemciniz bu çözünürlüğü destekliyorsa). POSIX, ancak 2001'den.


4

clock () genellikle oldukça kötü bir çözünürlüğe sahiptir. Zamanı milisaniye düzeyinde ölçmek istiyorsanız , bu soruda açıklandığı gibi bir alternatif clock_gettime () kullanmaktır .

(Linux'ta -lrt ile bağlantı kurmanız gerektiğini unutmayın).


4

C ++ 11 ile std::chrono::high_resolution_clockşunları yapabilirsiniz:

#include <iostream>
#include <chrono>
#include <thread>
typedef std::chrono::high_resolution_clock Clock;

int main()
{
    std::chrono::milliseconds three_milliseconds{3};

    auto t1 = Clock::now();
    std::this_thread::sleep_for(three_milliseconds);
    auto t2 = Clock::now();

    std::cout << "Delta t2-t1: " 
              << std::chrono::duration_cast<std::chrono::milliseconds>(t2 - t1).count()
              << " milliseconds" << std::endl;
}

Çıktı:

Delta t2-t1: 3 milliseconds

Demo bağlantısı: http://cpp.sh/2zdtu


2

clock (), linux'ta milisaniye veya saniye döndürmez. Genellikle clock (), bir linux sisteminde mikrosaniye döndürür. Clock () tarafından döndürülen değeri yorumlamanın doğru yolu, ne kadar zaman geçtiğini bulmak için CLOCKS_PER_SEC'ye bölmektir.


üzerinde çalıştığım kutuda değil! artı, ben am CLOCKS_PER_SEC bölerek, ama çözünürlük sadece aşağı ikinci olduğu için anlamsız
Hasen

İyi adil olmak, birimler olan (CLOCKS_PER_SEC tüm POSIX sistemlerinde 1000000 ise) mikrosaniye. Sadece saniye çözünürlüğü var. :-P.
Evan Teran

1

Bu çalışmalı ... bir mac üzerinde test edilmiş ...

#include <stdio.h>
#include <sys/time.h>

int main() {
        struct timeval tv;
        struct timezone tz;
        struct tm *tm;
        gettimeofday(&tv,&tz);
        tm=localtime(&tv.tv_sec);
        printf("StartTime: %d:%02d:%02d %d \n", tm->tm_hour, tm->tm_min, tm->tm_sec, tv.tv_usec);
}

Evet ... iki kez çalıştır ve çıkar ...


1

POSIX standardında clockdönüş değeri CLOCKS_PER_SEC sembolü ile tanımlanmıştır ve bir uygulama bunu herhangi bir uygun şekilde tanımlamakta serbesttir. Linux altında, bu times()işlevde iyi şanslar elde ettim .


1

gettimeofday - sorun şu ki, donanım saatinizi değiştirirseniz (örneğin NTP ile) Boost - bu proje için mevcut değil clock () - genellikle 4 baytlık bir tamsayı döndürür, bu da düşük bir kapasite anlamına gelir ve bir süre sonra negatif sayılar döndürür.

Kendi sınıfımı oluşturmayı ve her 10 milisaniyede bir güncellemeyi tercih ederim, böylece bu yol daha esnektir ve hatta abonelere sahip olmak için onu geliştirebilirim.

class MyAlarm {
static int64_t tiempo;
static bool running;
public:
static int64_t getTime() {return tiempo;};
static void callback( int sig){
    if(running){
        tiempo+=10L;
    }
}
static void run(){ running = true;}
};

int64_t MyAlarm::tiempo = 0L;
bool MyAlarm::running = false;

yenilemek için setitimer kullanıyorum:

int main(){
struct sigaction sa; 
struct itimerval timer; 

MyAlarm::run();
memset (&sa, 0, sizeof (sa)); 
sa.sa_handler = &MyAlarm::callback; 

sigaction (SIGALRM, &sa, NULL); 


timer.it_value.tv_sec = 0; 
timer.it_value.tv_usec = 10000; 



timer.it_interval.tv_sec = 0; 
timer.it_interval.tv_usec = 10000; 


setitimer (ITIMER_REAL, &timer, NULL); 
.....

Setitimer ve ITIMER_VIRTUAL ve ITIMER_REAL'e bakın.

Alarm veya ualarm işlevlerini kullanmayın, işleminiz zor bir iş olduğunda düşük hassasiyete sahip olursunuz.



0

Bir güncelleme olarak, Windows clock () 'un duvar saati zamanını (CLOCKS_PER_SEC hassasiyetiyle) ölçtüğü görülmektedir.

 http://msdn.microsoft.com/en-us/library/4e2ess30(VS.71).aspx

Linux'ta ise mevcut işlem tarafından kullanılan çekirdeklerdeki cpu süresini ölçer

http://www.manpagez.com/man/3/clock

ve (görünür ve orijinal gönderen tarafından belirtildiği gibi) aslında CLOCKS_PER_SEC'den daha az hassasiyetle, ancak bu Linux'un belirli sürümüne bağlı olabilir.


0

Gettimeofday () kullanmama Hola Soy yöntemini seviyorum. Yönetici saat dilimini değiştiren çalışan bir sunucuda başıma geldi. Saat, aynı (doğru) yerel değeri gösterecek şekilde güncellendi. Bu, time () ve gettimeofday () işlevinin 2 saat kaymasına ve bazı hizmetlerdeki tüm zaman damgalarının takılmasına neden oldu.


0

C++Kullanarak bir sınıf yazdım timeb.

#include <sys/timeb.h>
class msTimer 
{
public:
    msTimer();
    void restart();
    float elapsedMs();
private:
    timeb t_start;
};

Üye fonksiyonları:

msTimer::msTimer() 
{ 
    restart(); 
}

void msTimer::restart() 
{ 
    ftime(&t_start); 
}

float msTimer::elapsedMs() 
{
    timeb t_now;
    ftime(&t_now);
    return (float)(t_now.time - t_start.time) * 1000.0f +
           (float)(t_now.millitm - t_start.millitm);
}

Kullanım örneği:

#include <cstdlib>
#include <iostream>

using namespace std;

int main(int argc, char** argv) 
{
    msTimer t;
    for (int i = 0; i < 5000000; i++)
        ;
    std::cout << t.elapsedMs() << endl;
    return 0;
}

Bilgisayarımdaki çıktı '19'. msTimerSınıfın doğruluğu milisaniye mertebesindedir. Yukarıdaki kullanım örneğinde, for-loop tarafından alınan toplam yürütme süresi izlenir. Bu süre, işletim sisteminin main()çoklu görev nedeniyle yürütme bağlamına girip çıkmasını içeriyordu .

Sitemizi kullandığınızda şunları okuyup anladığınızı kabul etmiş olursunuz: Çerez Politikası ve Gizlilik Politikası.
Licensed under cc by-sa 3.0 with attribution required.