Gettimeofday () 'ın mikrosaniye çözünürlükte olduğu garanti ediliyor mu?


97

Başlangıçta Win32 API'si için yazılmış bir oyunu Linux'a aktarıyorum (iyi, Win32 bağlantı noktasının OS X bağlantı noktasını Linux'a taşımak).

QueryPerformanceCounterİşlem başladığından beri uSeconds vererek uyguladım :

BOOL QueryPerformanceCounter(LARGE_INTEGER* performanceCount)
{
    gettimeofday(&currentTimeVal, NULL);
    performanceCount->QuadPart = (currentTimeVal.tv_sec - startTimeVal.tv_sec);
    performanceCount->QuadPart *= (1000 * 1000);
    performanceCount->QuadPart += (currentTimeVal.tv_usec - startTimeVal.tv_usec);

    return true;
}

Bu, QueryPerformanceFrequency()frekans olarak sabit 1000000 vermekle birleştiğinde , makinemde iyi çalışıyor ve bana uSecondsprogramın başlangıcından beri içeren 64 bitlik bir değişken veriyor .

Peki bu taşınabilir mi? Çekirdek belirli bir şekilde veya buna benzer bir şekilde derlenmişse farklı çalıştığını keşfetmek istemiyorum. Bununla birlikte, Linux dışında bir şeye taşınabilir olmamasından memnunum.

Yanıtlar:


57

Olabilir. Ama daha büyük sorunların var. gettimeofday()Sisteminizde zamanlayıcıyı değiştiren işlemler varsa (yani, ntpd) yanlış zamanlamalara neden olabilir. "Normal" bir linux üzerinde, çözünürlüğünün gettimeofday()10us olduğuna inanıyorum . İleri ve geri atlayabilir ve sonuç olarak, sisteminizde çalışan işlemlere bağlı olarak zaman alabilir. Bu, sorunuzun cevabını etkili bir şekilde yapar.

clock_gettime(CLOCK_MONOTONIC)Zamanlama aralıklarına bakmalısınız . Çok çekirdekli sistemler ve harici saat ayarları gibi şeyler nedeniyle birkaç daha az sorundan muzdariptir.

Ayrıca, clock_getres()işleve bakın.


1
clock_gettime yalnızca en yeni Linux'ta mevcuttur. diğer sistemde yalnızca gettimeofday ()
vitaly.v.ch

3
@ vitaly.v.ch POSIX olduğundan sadece Linux ve 'newist' değil mi? Red Hat Enterprise Linux gibi 'Kurumsal' dağıtımlar bile 2.6.18'e dayanmaktadır ki bu saat_gettime değerine sahiptir, bu yüzden hayır, çok yeni değildir .. (RHEL'deki kılavuz tarihi 2004-Mart-12'dir, bu nedenle bir süredir ortalıktaydı) GERÇEKTEN SIKILAN ESKİ çekirdeklerden bahsederken WTF mi demek istiyorsun?
Spudd86

clock_gettime 2001'de POSIX'e dahil edildi. Şu anda clock_gettime () Linux 2.6 ve qnx'te uygulandı. ancak linux 2.4 şu anda birçok üretim sisteminde kullanılmaktadır.
vitaly.v.ch

2001'de tanıtıldı, ancak POSIX 2008'e kadar zorunlu değildi.
R .. GitHub BUZ YARDIMINI DURDUR

2
Lock_gettime için Linux SSS'den (David Schlosnagle'ın cevabına bakın) "CLOCK_MONOTONIC ... adjtimex () aracılığıyla NTP tarafından frekans ayarlanıyor. Gelecekte (hala yamayı eklemeye çalışıyorum) olmayacak bir CLOCK_MONOTONIC_RAW olacak tamamen değiştirilebilir ve donanım sayaçlarıyla doğrusal bir korelasyona sahip olacaktır. " _RAW saatinin çekirdeğe girdiğini sanmıyorum (_HR olarak yeniden adlandırılmadıkça, ancak araştırmam çabaların da terk edildiğini gösteriyor).
Tony Delroy

41

Intel İşlemciler için Yüksek Çözünürlük, Düşük Genel Yük Zamanlaması

Intel donanımı üzerindeyseniz, burada CPU gerçek zamanlı talimat sayacını nasıl okuyacağınız. İşlemci önyüklendiğinden beri gerçekleştirilen CPU döngüsü sayısını size söyleyecektir. Bu muhtemelen performans ölçümü için alabileceğiniz en iyi ayrıntılı sayaçtır.

Bunun CPU döngülerinin sayısı olduğunu unutmayın. Linux'ta CPU hızını / proc / cpuinfo'dan alabilir ve saniye sayısını elde etmek için bölebilirsiniz. Bunu ikiye katlamak oldukça kullanışlıdır.

Bunu kutumda çalıştırdığımda

11867927879484732
11867927879692217
it took this long to call printf: 207485

İşte tonlarca ayrıntı veren Intel geliştirici kılavuzu .

#include <stdio.h>
#include <stdint.h>

inline uint64_t rdtsc() {
    uint32_t lo, hi;
    __asm__ __volatile__ (
      "xorl %%eax, %%eax\n"
      "cpuid\n"
      "rdtsc\n"
      : "=a" (lo), "=d" (hi)
      :
      : "%ebx", "%ecx");
    return (uint64_t)hi << 32 | lo;
}

main()
{
    unsigned long long x;
    unsigned long long y;
    x = rdtsc();
    printf("%lld\n",x);
    y = rdtsc();
    printf("%lld\n",y);
    printf("it took this long to call printf: %lld\n",y-x);
}

11
TSC'nin her zaman çekirdekler arasında senkronize edilmeyebileceğini, işlemci düşük güç modlarına girdiğinde durabileceğini veya frekansını değiştirebileceğini (ve bunu yaptığını bilmenin hiçbir yolu olmadığını) ve genel olarak her zaman güvenilir olmadığını unutmayın. Çekirdek ne zaman güvenilir olduğunu algılayabilir, HPET ve ACPI PM zamanlayıcı gibi diğer alternatifleri algılayabilir ve otomatik olarak en iyi olanı seçebilir. TSC'nin kararlı ve monoton olduğundan gerçekten emin değilseniz, zamanlama için her zaman çekirdeği kullanmak iyi bir fikirdir.
CesarB

12
Core ve üzeri Intel platformlarındaki TSC, birden fazla CPU arasında senkronize edilir ve güç yönetimi durumlarından bağımsız olarak sabit bir frekansta artışlar yapılır. Intel Yazılım Geliştiricisinin Kılavuzu, Cilt. 3 Bölüm 18.10. Ancak sayacın artış hızı CPU'nun frekansı ile aynı değildir . TSC, "ölçeklenebilir veri yolu frekansı ve maksimum çözülmüş veri yolu oranı ürününe eşit olan platformun maksimum çözümlenmiş frekansında" artar Intel Yazılım Geliştiricisi Kılavuzu, Cilt. 3 Bölüm 18.18.5. Bu değerleri CPU'nun modele özgü kayıtlarından (MSR'ler) alırsınız.
sstock

7
Ölçeklenebilir veri yolu frekansı ve maksimum çözümlenmiş veri yolu oranını, CPU'nun modele özgü kayıtlarını (MSR'ler) aşağıdaki şekilde sorgulayarak elde edebilirsiniz: Ölçeklenebilir veri yolu frekansı == MSR_FSB_FREQ [2: 0] id 0xCD, Maksimum çözümlenmiş veri yolu oranı == MSR_PLATFORM_ID [12: 8] id 0x17. Kayıt değerlerini yorumlamak için Intel SDM Cilt 3 Ek B.1'e başvurun. Kayıtları sorgulamak için Linux'ta msr araçlarını kullanabilirsiniz. kernel.org/pub/linux/utils/cpu/msr-tools
sstock

1
Kodunuzun CPUIDilk RDTSCtalimattan sonra ve karşılaştırılan kodu çalıştırmadan önce tekrar kullanılması gerekmez mi? Aksi takdirde, karşılaştırmalı kodun ilkinden önce / onunla paralel olarak yürütülmesini RDTSCve dolayısıyla RDTSCdeltada yetersiz temsil edilmesini ne durdurabilir ?
Tony Delroy

18

@Bernard:

İtiraf etmeliyim ki, örneğinizin çoğu doğruca kafamın üzerinden geçti. Derliyor ve yine de çalışıyor gibi görünüyor. Bu SMP sistemleri veya SpeedStep için güvenli mi?

Bu güzel bir soru ... Kodun iyi olduğunu düşünüyorum. Pratik bir bakış açısıyla, onu her gün şirketimde kullanıyoruz ve 2-8 çekirdekten oluşan oldukça geniş bir kutu yelpazesi üzerinde çalışıyoruz. Tabii ki, YMMV, vb, ancak güvenilir ve düşük maliyetli bir zamanlama yöntemi gibi görünüyor (çünkü sistem alanına bir bağlam geçişi yapmıyor).

Genel olarak nasıl çalışır:

  • kod bloğunu derleyici (ve uçucu, böylece optimize edici onu yalnız bırakacak) olarak beyan edin.
  • CPUID komutunu çalıştırın. Bazı CPU bilgilerini elde etmenin yanı sıra (ki bunlarla hiçbir şey yapmıyoruz), CPU'nun yürütme tamponunu senkronize eder, böylece zamanlamalar sıra dışı yürütmeden etkilenmez.
  • rdtsc (zaman damgasını oku) yürütmesini yürütür. Bu, işlemcinin sıfırlanmasından bu yana yürütülen makine döngüsü sayısını getirir. Bu 64 bitlik bir değerdir, bu nedenle mevcut CPU hızlarında yaklaşık 194 yılda bir tamamlanır. İlginç bir şekilde, orijinal Pentium referansında, bunların her 5800 yılda bir bittiğini belirtiyorlar.
  • son çift satır, yazmaçlardan değerleri hi ve lo değişkenlerine saklar ve bunu 64-bit dönüş değerine koyar.

Özel notlar:

  • Arıza dışı yürütme yanlış sonuçlara neden olabilir, bu nedenle size cpu hakkında bazı bilgiler vermenin yanı sıra herhangi bir sıra dışı talimat yürütmesini de senkronize eden "cpuid" komutunu yürütürüz.

  • Çoğu işletim sistemi, başladıklarında CPU'lardaki sayaçları senkronize eder, bu nedenle yanıt birkaç nano saniye içinde iyidir.

  • Hazırda bekletme yorumu muhtemelen doğrudur, ancak pratikte muhtemelen hazırda bekletme sınırlarının ötesindeki zamanlamaları umursamıyorsunuz.

  • hız adımı ile ilgili olarak: Daha yeni Intel CPU'lar hız değişikliklerini telafi eder ve ayarlanmış bir sayı döndürür. Ağımızdaki bazı kutular üzerinde hızlı bir tarama yaptım ve ona sahip olmayan tek bir kutu buldum: eski bir veritabanı sunucusunu çalıştıran bir Pentium 3. (bunlar linux kutuları, bu yüzden kontrol ettim: grep Constant_tsc / proc / cpuinfo)

  • AMD CPU'larından emin değilim, biz öncelikle bir Intel mağazasıyız, ancak bazı düşük seviyeli sistem uzmanlarımızın bir AMD değerlendirmesi yaptığını biliyorum.

Umarım bu merakınızı giderir, ilginç ve (IMHO) az çalışılmış bir programlama alanıdır. Jeff ve Joel, bir programcının C'yi bilmesinin gerekip gerekmediğinden bahsederken biliyor musunuz? Onlara bağırıyordum, "Hey, o üst düzey C şeylerini unut ... assembler, bilgisayarın ne yaptığını bilmek istiyorsan öğrenmen gereken şeydir!"


1
... Çekirdek insanları bir süredir insanların rdtsc'yi kullanmayı bırakmalarını sağlamaya çalışıyorlar ... ve genellikle çekirdekte kullanmaktan kaçınıyorlar çünkü bu güvenilmez.
Spudd86

1
Referans olarak, sorduğum soru (Ayrı bir yanıtta - yorumlardan önce) şuydu: "Kabul etmeliyim, örneğinizin çoğu doğruca kafamın üzerinden geçti. Derliyor ve işe yarıyor gibi görünüyor. SMP sistemleri mi yoksa SpeedStep mi? "
Bernard



9

Bu yüzden açıkça mikrosaniye diyor, ancak sistem saatinin çözünürlüğünün belirtilmediğini söylüyor. Sanırım bu bağlamda çözünürlük, şimdiye kadarki en küçük miktarın nasıl artırılacağı anlamına geliyor?

Veri yapısı, ölçüm birimi olarak mikrosaniyelere sahip olarak tanımlanır, ancak bu, saatin veya işletim sisteminin aslında bunu hassas bir şekilde ölçebildiği anlamına gelmez.

Diğer insanların önerdiği gibi gettimeofday(), kötüdür çünkü zamanı ayarlamak saatin sapmasına ve hesaplamanızın aksamasına neden olabilir. clock_gettime(CLOCK_MONOTONIC)istediğiniz şeydir ve clock_getres()size saatinizin hassasiyetini söyleyecektir.


Peki gettimeofday () gün ışığından yararlanma ile ileri veya geri atladığında kodunuzda ne olur?
mpez0

3
clock_gettime yalnızca en yeni Linux'ta mevcuttur. diğer sistemde yalnızca gettimeofday ()
vitaly.v.ch

8

Gettimeofday () öğesinin gerçek çözünürlüğü donanım mimarisine bağlıdır. Intel işlemciler ve SPARC makineleri, mikrosaniyeleri ölçen yüksek çözünürlüklü zamanlayıcılar sunar. Diğer donanım mimarileri, tipik olarak 100 Hz olarak ayarlanan sistemin zamanlayıcısına geri döner. Bu gibi durumlarda, zaman çözünürlüğü daha az doğru olacaktır.

Bu yanıtı Yüksek Çözünürlüklü Zaman Ölçümü ve Zamanlayıcılardan aldım, Bölüm I


6

Bu cevap , ayarlanan saatle ilgili sorunlardan bahseder. Hem tik birimlerini garanti eden problemleriniz hem de zamanın ayarlanması ile ilgili problemleriniz <chrono>kütüphane ile C ++ 11'de çözülür .

Saatin std::chrono::steady_clockayarlanmaması garanti edilir ve ayrıca gerçek zamana göre sabit bir hızda ilerleyecektir, bu nedenle SpeedStep gibi teknolojiler onu etkilememelidir.

Tür güvenli birimleri std::chrono::duration, gibi uzmanlıklardan birine dönüştürerek elde edebilirsiniz std::chrono::microseconds. Bu tür ile, kene değeri tarafından kullanılan birimler hakkında hiçbir belirsizlik yoktur. Ancak, saatin mutlaka bu çözünürlüğe sahip olmadığını unutmayın. Doğru bir saate sahip olmadan bir süreyi attosaniyeye dönüştürebilirsiniz.


4

Deneyimlerime ve internette okuduklarıma göre cevap "Hayır", garanti değil. İşlemci hızına, işletim sistemine, Linux'un türüne vb. Bağlıdır.


3

RDTSC'yi okumak SMP sistemlerinde güvenilir değildir, çünkü her CPU kendi sayacını korur ve her sayacın başka bir CPU'ya göre senkronize olması garanti edilmez.

Denemeyi önerebilirim clock_gettime(CLOCK_REALTIME). Posix kılavuzu, bunun tüm uyumlu sistemlerde uygulanması gerektiğini belirtir. Bir nanosaniye sayımı sağlayabilir, ancak clock_getres(CLOCK_REALTIME)gerçek çözünürlüğün ne olduğunu görmek için muhtemelen sisteminizi kontrol etmek isteyeceksiniz .


clock_getres(CLOCK_REALTIME)gerçek çözünürlüğü vermeyecektir. Hrtimers mevcut olduğunda her zaman "1 ns" (bir nanosaniye) döndürür, include/linux/hrtimer.hdosyayı kontrol edin define HIGH_RES_NSEC 1(daha fazlası stackoverflow.com/a/23044075/196561 )
osgx
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.