C # DateTime.Now hassasiyeti


97

Bazı birim testleri yaparken DateTime.UtcNow ile bazı beklenmedik davranışlarla karşılaştım. Art arda DateTime.Now/UtcNow'u aradığınızda, daha kesin milisaniye artışları yakalamak yerine, beklenenden daha uzun bir zaman aralığı için aynı değeri size geri veriyor gibi görünüyor.

Kesin zaman ölçümleri yapmak için daha uygun bir Kronometre sınıfı olduğunu biliyorum, ancak birinin bu davranışı DateTime'da açıklayıp açıklayamayacağını merak ediyordum. DateTime.Now için belgelenmiş resmi bir kesinlik var mı (örneğin, 50 ms içinde kesin mi?)? Neden DateTime.Now çoğu CPU saatinin kaldırabileceğinden daha az hassas hale getirilsin? Belki de en düşük ortak payda CPU için tasarlanmıştır?

public static void Main(string[] args)
{
    var stopwatch = new Stopwatch();
    stopwatch.Start();
    for (int i=0; i<1000; i++)
    {
        var now = DateTime.Now;
        Console.WriteLine(string.Format(
            "Ticks: {0}\tMilliseconds: {1}", now.Ticks, now.Millisecond));
    }

    stopwatch.Stop();
    Console.WriteLine("Stopwatch.ElapsedMilliseconds: {0}",
        stopwatch.ElapsedMilliseconds);

    Console.ReadLine();
}

Aynı değeri geri aldığınız aralık nedir?
ChrisF


Yukarıdaki kodu çalıştırdığımda, tıklama sayısı ve milisaniye için yalnızca 3 benzersiz değer elde ettim ve son durdurma izleme süresi 147 ms idi, bu yüzden makinemde yalnızca yaklaşık 50 ms için hassas olduğu anlaşılıyor ...
Andy White

Söylemeliyim ki döngü birkaç kez çalıştı, ancak sadece 3 farklı değer gördüm ...
Andy White

Buraya gelen herkes için TL; DR // QueryPerformanceCounter işlevini kullanın "Zaman aralığı ölçümleri için kullanılabilen yüksek çözünürlüklü (<1us) bir zaman damgası olan performans sayacının mevcut değerini alır." (Yönetilen kod için System.Diagnostics.Stopwatch sınıfı hassas zaman esas QPC kullanır.) Msdn.microsoft.com/en-us/library/windows/desktop/...
AnotherUser

Yanıtlar:


184

Neden DateTime.Now çoğu CPU saatinin kaldırabileceğinden daha az hassas hale getirilsin?

İyi bir saat hem olmalıdır kesin ve doğru ; bunlar farklı. Eski şakaya göre, durmuş bir saat günde iki kez tam olarak doğrudur, dakika yavaş bir saat hiçbir zaman doğru değildir. Ancak, bir dakika yavaş olan saat her zaman en yakın dakikaya göre hassastır, halbuki durdurulmuş bir saatin hiç kullanışlı bir hassasiyeti yoktur.

Neden DateTime olmalıdır kesin muhtemelen olamaz zaman, bir mikrosaniye söylemek doğru mikrosaniye için? Çoğu insan, mikrosaniye kadar doğru olan resmi zaman sinyalleri için herhangi bir kaynağa sahip değildir. Bu nedenle bir ondalık yeri altı basamak veren hassas , son beş bunlardan olan çöp olacağını yalan .

DateTime'ın amacının bir tarih ve saati temsil etmek olduğunu unutmayın . Yüksek hassasiyetli zamanlamalar hiçbir şekilde DateTime'ın amacı değildir; Dikkat ettiğiniz gibi, StopWatch'ın amacı budur. DateTime'ın amacı, kullanıcıya geçerli saati göstermek, gelecek Salı gününe kadar olan gün sayısını hesaplamak gibi amaçlar için bir tarih ve saati temsil etmektir.

Kısacası, "saat kaç?" ve "bu ne kadar sürdü?" tamamen farklı sorulardır; bir soruyu yanıtlamak için tasarlanmış bir aracı diğerini yanıtlamak için kullanmayın.

Soru için teşekkürler; bu iyi bir blog yazısı olacak! :-)


2
@Eric Lippert: Raymond Chen'in "kesinlik" ve "doğruluk" arasındaki fark konusunda eski ama güzel bir yanı var: blogs.msdn.com/oldnewthing/archive/2005/09/02/459952.aspx
jason

3
Tamam, hassasiyete karşı doğruluk hakkında iyi bir nokta. Sanırım DateTime'ın doğru olmadığı ifadesini hala gerçekten almıyorum çünkü "olması gerekmiyor." Bir işlem sistemim varsa ve her kayıt için bir tarih-saat işaretlemek istiyorsam, bana DateTime sınıfını kullanmak sezgisel görünüyor, ancak .NET'te daha doğru / kesin zaman bileşenleri var gibi görünüyor, öyleyse neden DateTime daha az yetenekli yaptı. Sanırım biraz daha okumak zorunda kalacağım ...
Andy White

11
Tamam @Andy, varsayalım ki böyle bir sisteminiz var. Bir makinede, bir işlemi 1 Ocak 12: 34: 30.23498273'te gerçekleşti olarak işaretlersiniz. Kümenizdeki başka bir makinede, bir işlemi 1 Ocak 12: 34: 30.23498456'da gerçekleşmiş olarak işaretlersiniz. İlk olarak hangi işlem gerçekleşti? İki makinenin saatinin bir mikrosaniye içinde senkronize olduğunu bilmiyorsanız, hangisinin önce gerçekleştiğine dair hiçbir fikriniz yok. Ekstra kesinlik yanıltıcı anlamsızlıktır . Kendi yöntemime sahip olsaydım, tüm DateTimes VBScript'te olduğu gibi en yakın saniyeye yuvarlanacaktı.
Eric Lippert

14
Senkronize edilmemiş ortalama bilgisayarlar genellikle dakikalar içinde bittiği için bunun bahsettiğiniz sorunu çözeceğinden değil . Şimdi, 1'e yuvarlamak hiçbir şeyi çözmezse, o zaman neden yuvarlansın? Başka bir deyişle, mutlak değerlerin neden delta zaman ölçümlerinin doğruluğundan daha küçük bir kesinliğe sahip olması gerektiği konusundaki argümanınızı takip etmiyorum.
Roman Starkov

3
Diyelim ki, (1) takvim alanı açısından bir şeyin ne zaman gerçekleştiğini bilmeyi (birkaç saniye içinde) (2) olaylar arasındaki aralığı tam olarak bilmeyi (50 milisaniye içinde) gerektiren bir etkinlik günlüğü oluşturduğumu varsayalım. Bunun için en güvenli bahis, DateTime.Şimdi ilk eylemin zaman damgası için, daha sonra ilk DateTime'dan farkı belirlemek için sonraki eylemler için bir Kronometre kullanın. Önereceğin yaklaşım bu mu Eric?
devuxer

18

DateTime'ın hassasiyeti, üzerinde çalıştığı sisteme özgüdür. Kesinlik, 15 veya 16 ms civarında olma eğiliminde olan bir bağlam anahtarının hızıyla ilgilidir. (Benim sistemimde, testlerimden yaklaşık 14 ms sonra, ancak 35-40 ms doğruluğa daha yakın olduğu bazı dizüstü bilgisayarlar gördüm.)

Peter Bromberg , C # dilinde yüksek hassasiyetli kod zamanlaması üzerine bunu tartışan bir makale yazdı .


2
Yıllar boyunca sahip olduğum 4 Win7 makinelerinin hepsi kabaca 1ms hassasiyete sahipti. Şimdi () uyku (1) Now (), test ederken her zaman tarih saatinde ~ 1 ms'lik bir değişiklikle sonuçlandı.
Bengie

~ 1ms doğruluğunu da görüyorum.
Timo

12

Kesin bir Tarih Saat istiyorum.Şimdi :), bu yüzden bunu hazırladım:

public class PreciseDatetime
{
    // using DateTime.Now resulted in many many log events with the same timestamp.
    // use static variables in case there are many instances of this class in use in the same program
    // (that way they will all be in sync)
    private static readonly Stopwatch myStopwatch = new Stopwatch();
    private static System.DateTime myStopwatchStartTime;

    static PreciseDatetime()
    {
        Reset();

        try
        {
            // In case the system clock gets updated
            SystemEvents.TimeChanged += SystemEvents_TimeChanged;
        }
        catch (Exception)
        {                
        }
    }

    static void SystemEvents_TimeChanged(object sender, EventArgs e)
    {
        Reset();
    }

    // SystemEvents.TimeChanged can be slow to fire (3 secs), so allow forcing of reset
    static public void Reset()
    {
        myStopwatchStartTime = System.DateTime.Now;
        myStopwatch.Restart();
    }

    public System.DateTime Now { get { return myStopwatchStartTime.Add(myStopwatch.Elapsed); } }
}

2
Bu çözümü beğendim, ancak emin değildim, bu yüzden kendi sorumu sordum ( stackoverflow.com/q/18257987/270348 ). Servy'nin yorumuna / cevabına göre, kronometreyi asla sıfırlamamalısınız.
RobSiklos

1
Belki sizin bağlamınızda değil, ancak benim bağlamımda sıfırlama mantıklı - sadece zamanlama gerçekten başlamadan önce yapıldığından emin oluyorum.
Jimmy

Aboneliğe ihtiyacınız yoktur ve kronometreyi sıfırlamanız gerekmez. Bu kodu her ~ 10 ms'de çalıştırmak gerekli değildir ve CPU tüketir. Ve bu kod hiç iş parçacığı için güvenli değildir. MyStopwatchStartTime = DateTime.UtcNow; bir kez, statik yapıcıda.
VeganHunter

1
@VeganHunter Yorumunuzu doğru anladığımdan emin değilim, ancak TimeChanged'in her ~ 10 ms'de bir çağrıldığını düşünüyorsunuz? Öyle değil.
Jimmy

@ Jimmy, haklısın. Benim hatam, kodu yanlış anladım. SystemEvents.TimeChanged olayı, yalnızca kullanıcı sistem saatini değiştirdiğinde çağrılır. Nadir bir olaydır.
VeganHunter

6

Gönderen MSDN bunu göreceksiniz DateTime.Nowbir sahip yaklaşık tüm NT işletim sistemleri üzerinde 10 milisaniye çözünürlük.

Gerçek hassasiyet donanıma bağlıdır. Kullanılarak daha iyi hassasiyet elde edilebilir QueryPerformanceCounter.


5

Ne olursa olsun, .NET kaynağını kontrol etmenin yanı sıra, Eric Lippert , DateTime'ın yalnızca yaklaşık 30 ms için doğru olduğunu söyleyerek bu SO sorusuna bir yorum yaptı . Nanosaniye doğru olmamasının nedeni, onun sözleriyle, "olması gerekmediği".


4
Ve daha kötü olabilir. VBScript'te Now () işlevi döndürülen sonucu en yakın saniyeye yuvarlar, döndürülen değerin mikrosaniyeye kadar kesin olması için yeterli kullanılabilir kesinliğe sahip olduğu gerçeğini boşa çıkarır. C # 'da yapı DateTime olarak adlandırılır; hayat sigortanızın ne zaman sona erdiği veya son yeniden başlatmanızdan bu yana ne kadar zaman geçtiği gibi tipik gerçek dünyadaki bilimsel olmayan alanlar için bir tarih ve saati temsil etmesi amaçlanmıştır. Yüksek hassasiyetli saniyenin altında zamanlama için tasarlanmamıştır.
Eric Lippert

3

MSDN belgelerinden :

Bu özelliğin çözümlenmesi sistem zamanlayıcısına bağlıdır.

Ayrıca Windows NT 3.5 ve sonraki sürümlerde yaklaşık çözünürlüğün 10 ms olduğunu iddia ediyorlar :)


1

Bu özelliğin çözümlenmesi, temeldeki işletim sistemine bağlı olan sistem zamanlayıcısına bağlıdır. 0,5 ile 15 milisaniye arasında olma eğilimindedir .

Sonuç olarak, bir döngüde olduğu gibi kısa bir zaman aralığında Now özelliğine yapılan tekrarlanan çağrılar aynı değeri döndürebilir.

MSDN Bağlantısı

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.