سؤال

هل هو موافق على الإطلاق للاستخدام Environment.TickCountلحساب الفترات الزمنية؟

int start = Environment.TickCount;
// Do stuff
int duration = Environment.TickCount - start;
Console.WriteLine("That took " + duration " ms");

لأن TickCount تم توقيعه وسيتم تمريره بعد 25 يومًا (يستغرق الأمر 50 يومًا للوصول إلى جميع البتات الـ 32، ولكن يتعين عليك إلغاء البت الموقع إذا كنت تريد فهم الرياضيات بأي شكل من الأشكال)، يبدو أنه من الخطورة جدًا أن يكون مفيدًا.

أنا استخدم DateTime.Now بدلاً من.هل هذه هي أفضل طريقة للقيام بذلك؟

DateTime start = DateTime.Now;
// Do stuff
TimeSpan duration = DateTime.Now - start;
Console.WriteLine("That took " + duration.TotalMilliseconds + " ms");
هل كانت مفيدة؟

المحلول

والطبقة استخدام ساعة توقيت. هناك مثال لائق على MSDN: http://msdn.microsoft كوم / EN-US / مكتبة / system.diagnostics.stopwatch.aspx

    Stopwatch stopWatch = Stopwatch.StartNew();
    Thread.Sleep(10000);
    stopWatch.Stop();
    // Get the elapsed time as a TimeSpan value.
    TimeSpan ts = stopWatch.Elapsed;

نصائح أخرى

البيئة. عدد القراد مبني على GetTickCount() وظيفة WinAPI.إنه في مللي ثانية ولكن الدقة الفعلية لها حوالي 15.6 مللي ثانية.لذلك لا يمكنك قياس فترات زمنية أقصر (وإلا ستحصل على 0)

ملحوظة: القيمة التي تم إرجاعها هي Int32، لذلك يتم تمرير هذا العداد كل 49.7 يومًا تقريبًا.لا يجب عليك استخدامه لقياس مثل هذه الفترات الطويلة.

DateTime.Ticks مبني على GetSystemTimeAsFileTime() وظيفة WinAPI.إنه في 100 نانو ثانية (أعشار الميكروسوند).تعتمد الدقة الفعلية لـ DateTime.Ticks على النظام.في نظام XP، تبلغ زيادة ساعة النظام حوالي 15.6 مللي ثانية، كما هو الحال في Environment.TickCount.في نظام التشغيل Windows 7، تبلغ دقتها 1 مللي ثانية (بينما لا تزال دقة Environemnt.TickCount 15.6 مللي ثانية)، ولكن إذا تم استخدام نظام توفير الطاقة (عادةً على أجهزة الكمبيوتر المحمولة) فيمكن أن تنخفض إلى 15.6 مللي ثانية أيضًا.

ساعة التوقيف مبني على استعلام الأداء () وظيفة WinAPI (ولكن إذا لم يكن نظامك يدعم عداد الأداء عالي الدقة، فسيتم استخدام DateTime.Ticks)

قبل استخدام StopWatch لاحظ مشكلتين:

  • يمكن أن يكون غير موثوق به في الأنظمة متعددة المعالجات (انظر MS kb895980, kb896256)
  • يمكن أن يكون غير موثوق به إذا تغير تردد وحدة المعالجة المركزية (اقرأ هذا شرط)

يمكنك تقييم دقة نظامك من خلال اختبار بسيط:

static void Main(string[] args)
{
    int xcnt = 0;
    long xdelta, xstart;
    xstart = DateTime.UtcNow.Ticks;
    do {
        xdelta = DateTime.UtcNow.Ticks - xstart;
        xcnt++;
    } while (xdelta == 0);

    Console.WriteLine("DateTime:\t{0} ms, in {1} cycles", xdelta / (10000.0), xcnt);

    int ycnt = 0, ystart;
    long ydelta;
    ystart = Environment.TickCount;
    do {
        ydelta = Environment.TickCount - ystart;
        ycnt++;
    } while (ydelta == 0);

    Console.WriteLine("Environment:\t{0} ms, in {1} cycles ", ydelta, ycnt);


    Stopwatch sw = new Stopwatch();
    int zcnt = 0;
    long zstart, zdelta;

    sw.Start();
    zstart = sw.ElapsedTicks; // This minimizes the difference (opposed to just using 0)
    do {
        zdelta = sw.ElapsedTicks - zstart;
        zcnt++;
    } while (zdelta == 0);
    sw.Stop();

    Console.WriteLine("StopWatch:\t{0} ms, in {1} cycles", (zdelta * 1000.0) / Stopwatch.Frequency, zcnt);
    Console.ReadKey();
}

لماذا أنت قلق على التمديد؟ طالما أن مدة كنت قياس تحت 24.9 يوما، وعلى حساب النسبية المدة، وكنت على ما يرام. لا يهم كم من الوقت وقد تم تشغيل النظام، طالما كنت قلق نفسك فقط مع نصيبك من ذلك الوقت تشغيل (في مقابل أداء مباشرة أقل من أو أكبر مما مقارنات على البدء ونقطة النهاية). بمعنى آخر. هذا:

 int before_rollover = Int32.MaxValue - 5;
 int after_rollover = Int32.MinValue + 7;
 int duration = after_rollover - before_rollover;
 Console.WriteLine("before_rollover: " + before_rollover.ToString());
 Console.WriteLine("after_rollover: " + after_rollover.ToString());
 Console.WriteLine("duration: " + duration.ToString());

وبشكل صحيح يطبع:

 before_rollover: 2147483642
 after_rollover: -2147483641
 duration: 13

وليس لديك ما يدعو للقلق بت تسجيل. C #، مثل C، يتيح للCPU التعامل مع هذا.

وهذه هي حالة شائعة لقد واجهت من قبل مع التهم الوقت في الأنظمة المضمنة. أنا لن مقارنة beforerollover

وربما كنت تريد System.Diagnostics.StopWatch .

إذا كنت تبحث عن وظيفة Environment.TickCount ولكن من دون النفقات العامة من إنشاء كائنات جديدة Stopwatch، يمكنك استخدام الأسلوب Stopwatch.GetTimestamp() ثابت (جنبا إلى جنب مع Stopwatch.Frequency) لحساب فترات زمنية طويلة. لأن GetTimestamp() إرجاع long، فإنه لن تجاوز لفترة طويلة جدا (أكثر من 100،000 سنة، على الجهاز الذي أستخدمه لكتابة هذا). كما انها أكثر دقة بكثير من Environment.TickCount الذي لديه الحد الأقصى من القرار 10 إلى 16 ميلي ثانية.

استخدم

System.Diagnostics.Stopwatch

ولديها خاصية تسمى

EllapsedMilliseconds

وEnvironment.TickCount يبدو أن أسرع بكثير ثم الحلول الأخرى:

Environment.TickCount 71
DateTime.UtcNow.Ticks 213
sw.ElapsedMilliseconds 1273

وتم إنشاؤها القياسات بواسطة التعليمات البرمجية التالية:

static void Main( string[] args ) {
    const int max = 10000000;
    //
    //
    for ( int j = 0; j < 3; j++ ) {
        var sw = new Stopwatch();
        sw.Start();
        for ( int i = 0; i < max; i++ ) {
            var a = Environment.TickCount;
        }
        sw.Stop();
        Console.WriteLine( $"Environment.TickCount {sw.ElapsedMilliseconds}" );
        //
        //
        sw = new Stopwatch();
        sw.Start();
        for ( int i = 0; i < max; i++ ) {
            var a = DateTime.UtcNow.Ticks;
        }
        sw.Stop();
        Console.WriteLine( $"DateTime.UtcNow.Ticks {sw.ElapsedMilliseconds}" );
        //
        //
        sw = new Stopwatch();
        sw.Start();
        for ( int i = 0; i < max; i++ ) {
            var a = sw.ElapsedMilliseconds;
        }
        sw.Stop();
        Console.WriteLine( $"sw.ElapsedMilliseconds {sw.ElapsedMilliseconds}" );
    }
    Console.WriteLine( "Done" );
    Console.ReadKey();
}

فيما يلي نوع من الملخص المحدث والمحدث لما قد يكون الإجابات والتعليقات الأكثر فائدة في هذا الموضوع + المعايير والمتغيرات الإضافية:

أول شيء أولاً:كما أشار آخرون في التعليقات، تغيرت الأمور في السنوات الأخيرة ومع نظام التشغيل Windows "الحديث" (Win XP ++) و.NET والأجهزة الحديثة، لا توجد أسباب أو أسباب قليلة لعدم استخدام Stopwatch().يرى MSDN للتفاصيل.الاقتباسات:

"هل تتأثر دقة QPC بتغيرات تردد المعالج الناتجة عن إدارة الطاقة أو تقنية Turbo Boost؟
لا. إذا المعالج لديه TSC ثابت, ، لا تتأثر QPC بهذا النوع من التغييرات.إذا لم يكن المعالج يحتوي على TSC ثابت، فستعود QPC إلى مؤقت أجهزة النظام الأساسي الذي لن يتأثر بتغييرات تردد المعالج أو تقنية Turbo Boost.

هل تعمل QPC بشكل موثوق على الأنظمة متعددة المعالجات والأنظمة متعددة النواة والأنظمة ذات الخيوط الفائقة؟
نعم

كيف يمكنني تحديد والتحقق من أن QPC يعمل على جهازي؟
لا تحتاج إلى إجراء مثل هذه الفحوصات.

ما هي المعالجات التي تحتوي على TSCs غير ثابتة؟.. اقرأ كذلك ..] "

ولكن إذا كنت لا تحتاج إلى دقة Stopwatch() أو على الأقل تريد أن تعرف بالضبط عن أداء Stopwatch (static vs.على أساس المثيل) والمتغيرات المحتملة الأخرى، تابع القراءة:

لقد توليت المعيار أعلاه من cskwg، وقمت بتوسيع الكود ليشمل المزيد من المتغيرات.لقد قمت بالقياس باستخدام i7 4700 MQ منذ عدة سنوات وC# 7 مع VS 2017 (لكي تكون أكثر دقة، تم تجميعها باستخدام .NET 4.5.2، على الرغم من القيم الحرفية الثنائية، فهي C# 6 (تستخدم من هذا:سلسلة حرفية و"استخدام ثابت").يبدو أن أداء Stopwatch() قد تحسن بشكل خاص مقارنة بالمعيار المذكور.

هذا مثال لنتائج 10 ملايين تكرار في حلقة، كما هو الحال دائمًا، القيم المطلقة ليست مهمة، ولكن حتى القيم النسبية قد تختلف على الأجهزة الأخرى:

32 بت، وضع الإصدار بدون تحسين:

قياس:GetTickCount64() [مللي ثانية]:275
قياس:البيئة. عدد القراد [مللي ثانية]:45
قياس:DateTime.UtcNow.Ticks [مللي ثانية]: 167
قياس:ساعة التوقيف:.ElapsedTicks [مللي ثانية]:277
قياس:ساعة التوقيف:المللي ثانية المنقضية [ms]:548
قياس:ساعة الإيقاف الثابتة.GetTimestamp [ms]:193
قياس:ساعة الإيقاف + التحويل إلى DateTime [ms]:551
قارن ذلك بـ DateTime.Now.Ticks [ms]: 9010

32 بت، وضع الإصدار، الأمثل:

قياس:GetTickCount64() [مللي ثانية]:198
قياس:البيئة. عدد القراد [مللي ثانية]:39
قياس:DateTime.UtcNow.Ticks [مللي ثانية]:66 (!)
قياس:ساعة التوقيف:.ElapsedTicks [مللي ثانية]:175
قياس:ساعة التوقيف:المللي ثانية المنقضية [ms]: 491
قياس:ساعة الإيقاف الثابتة.GetTimestamp [ms]:175
قياس:ساعة الإيقاف + التحويل إلى DateTime [ms]: 510
قارن ذلك بـ DateTime.Now.Ticks [ms]: 8460

64 بت، وضع الإصدار بدون تحسين:

قياس:GetTickCount64() [مللي ثانية]:205
قياس:البيئة. عدد القراد [مللي ثانية]:39
قياس:DateTime.UtcNow.Ticks [مللي ثانية]: 127
قياس:ساعة التوقيف:.ElapsedTicks [مللي ثانية]:209
قياس:ساعة التوقيف:المللي ثانية المنقضية [ms]:285
قياس:ساعة الإيقاف الثابتة.GetTimestamp [ms]:187
قياس:ساعة الإيقاف + التحويل إلى DateTime [ms]:319
قارن ذلك بـ DateTime.Now.Ticks [ms]:3040

64 بت، وضع الإصدار، الأمثل:

قياس:GetTickCount64() [مللي ثانية]:148
قياس:البيئة. عدد القراد [مللي ثانية]:31 (غير أنه لا يزال يستحق كل هذا العناء؟)
قياس:DateTime.UtcNow.Ticks [مللي ثانية]:76 (!)
قياس:ساعة التوقيف:.ElapsedTicks [مللي ثانية]:178
قياس:ساعة التوقيف:المللي ثانية المنقضية [ms]:226
قياس:ساعة الإيقاف الثابتة.GetTimestamp [ms]:175
قياس:ساعة الإيقاف + التحويل إلى DateTime [ms]:246
قارن ذلك بـ DateTime.Now.Ticks [ms]:3020

قد يكون الأمر مثيرًا للاهتمام للغاية يبدو أن إنشاء قيمة DateTime لطباعة وقت ساعة الإيقاف ليس له أي تكاليف تقريبًا.ومن المثير للاهتمام بطريقة أكاديمية أكثر منها عملية أن ساعة الإيقاف الثابتة أسرع قليلاً (كما هو متوقع).بعض نقاط التحسين مثيرة للاهتمام للغاية.على سبيل المثال، لا أستطيع أن أشرح لماذا Stopwatch.ElapsedMillithans فقط مع 32 بت يكون بطيئًا جدًا مقارنة بمتغيراته الأخرى، على سبيل المثال الثابت.هذا وDateTime. الآن أكثر من ضعف سرعتهما مع 64 بت.

يمكنك ان ترى:فقط بالنسبة لملايين عمليات الإعدام، أصبح وقت ساعة الإيقاف مهمًا.إذا كان هذا هو الحال بالفعل (ولكن احذر من إجراء التحسين الجزئي مبكرًا جدًا)، فقد يكون من المثير للاهتمام أنه مع GetTickCount64()، ولكن بشكل خاص مع DateTime.UtcNow, ، لديك مؤقت 64 بت (طويل) بدقة أقل من ساعة الإيقاف، ولكنه أسرع، بحيث لا تضطر إلى العبث بالبيئة "القبيحة" 32 بت.

كما هو متوقع، فإن DateTime.Now هو الأبطأ على الإطلاق.

إذا قمت بتشغيله، فسيقوم الرمز أيضًا باسترداد دقة ساعة الإيقاف الحالية والمزيد.

هنا هو رمز المعيار الكامل:

using System.Diagnostics;
using System.Runtime.InteropServices;
using System.Threading;
using static System.Environment;

[...]

    [DllImport("kernel32.dll") ]
    public static extern UInt64 GetTickCount64(); // Retrieves a 64bit value containing ticks since system start

    static void Main(string[] args)
    {
        const int max = 10_000_000;
        const int n = 3;
        Stopwatch sw;

        // Following Process&Thread lines according to tips by Thomas Maierhofer: https://codeproject.com/KB/testing/stopwatch-measure-precise.aspx
        // But this somewhat contradicts to assertions by MS in: https://msdn.microsoft.com/en-us/library/windows/desktop/dn553408%28v=vs.85%29.aspx?f=255&MSPPError=-2147217396#Does_QPC_reliably_work_on_multi-processor_systems__multi-core_system__and_________systems_with_hyper-threading
        Process.GetCurrentProcess().ProcessorAffinity = new IntPtr(1); // Use only the first core
        Process.GetCurrentProcess().PriorityClass = ProcessPriorityClass.High;
        Thread.CurrentThread.Priority = ThreadPriority.Highest;
        Thread.Sleep(2); // warmup

        Console.WriteLine($"Repeating measurement {n} times in loop of {max:N0}:{NewLine}");
        for (int j = 0; j < n; j++)
        {
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var tickCount = GetTickCount64();
            }
            sw.Stop();
            Console.WriteLine($"Measured: GetTickCount64() [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var tickCount = Environment.TickCount; // only int capacity, enough for a bit more than 24 days
            }
            sw.Stop();
            Console.WriteLine($"Measured: Environment.TickCount [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = DateTime.UtcNow.Ticks;
            }
            sw.Stop();
            Console.WriteLine($"Measured: DateTime.UtcNow.Ticks [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = sw.ElapsedMilliseconds;
            }
            sw.Stop();
            Console.WriteLine($"Measured: Stopwatch: .ElapsedMilliseconds [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = Stopwatch.GetTimestamp();
            }
            sw.Stop();
            Console.WriteLine($"Measured: static Stopwatch.GetTimestamp [ms]: {sw.ElapsedMilliseconds}");
            //
            //
            DateTime dt=DateTime.MinValue; // just init
            sw = new Stopwatch();
            sw.Start();
            for (int i = 0; i < max; i++)
            {
                var a = new DateTime(sw.Elapsed.Ticks); // using variable dt here seems to make nearly no difference
            }
            sw.Stop();
            //Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [s] with millisecs: {dt:s.fff}");
            Console.WriteLine($"Measured: Stopwatch+conversion to DateTime [ms]:  {sw.ElapsedMilliseconds}");

            Console.WriteLine();
        }
        //
        //
        sw = new Stopwatch();
        var tickCounterStart = Environment.TickCount;
        sw.Start();
        for (int i = 0; i < max/10; i++)
        {
            var a = DateTime.Now.Ticks;
        }
        sw.Stop();
        var tickCounter = Environment.TickCount - tickCounterStart;
        Console.WriteLine($"Compare that with DateTime.Now.Ticks [ms]: {sw.ElapsedMilliseconds*10}");

        Console.WriteLine($"{NewLine}General Stopwatch information:");
        if (Stopwatch.IsHighResolution)
            Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");
        else
            Console.WriteLine("- Using high-resolution performance counter for Stopwatch class.");

        double freq = (double)Stopwatch.Frequency;
        double ticksPerMicroSec = freq / (1000d*1000d) ; // microsecond resolution: 1 million ticks per sec
        Console.WriteLine($"- Stopwatch accuracy- ticks per microsecond (1000 ms): {ticksPerMicroSec:N1}");
        Console.WriteLine(" (Max. tick resolution normally is 100 nanoseconds, this is 10 ticks/microsecond.)");

        DateTime maxTimeForTickCountInteger= new DateTime(Int32.MaxValue*10_000L);  // tickCount means millisec -> there are 10.000 milliseconds in 100 nanoseconds, which is the tick resolution in .NET, e.g. used for TimeSpan
        Console.WriteLine($"- Approximated capacity (maxtime) of TickCount [dd:hh:mm:ss] {maxTimeForTickCountInteger:dd:HH:mm:ss}");
        // this conversion from seems not really accurate, it will be between 24-25 days.
        Console.WriteLine($"{NewLine}Done.");

        while (Console.KeyAvailable)
            Console.ReadKey(false);
        Console.ReadKey();
    }

ويجب عليك استخدام ساعة توقيت الطبقة بدلا من ذلك.

أستخدم Environment.TickCount للأسباب التالية:

  1. فئة ساعة الإيقاف غير موجودة في Compact Framework.
  2. تستخدم ساعة الإيقاف نفس آلية التوقيت الأساسية التي تستخدمها TickCount، وبالتالي فإن النتائج لن تكون أكثر أو أقل دقة.
  3. من غير المرجح أن يتم حل مشكلة الالتفاف مع TickCount من الناحية الكونية (يجب عليك ترك جهاز الكمبيوتر الخاص بك قيد التشغيل لمدة 27 يومًا ثم محاولة قياس الوقت الذي يحدث لتمتد اللحظة الملتفة)، وحتى إذا قمت بذلك، فإن النتيجة ستكون فترة زمنية سلبية ضخمة (لذا من شأنه أن يبرز نوعًا ما).

ومع ذلك، أوصي أيضًا باستخدام Stopwatch، إذا كانت متاحة لك.أو يمكنك أن تستغرق دقيقة واحدة تقريبًا وتكتب فصلًا دراسيًا يشبه ساعة الإيقاف يغلف Environment.TickCount.

راجع للشغل، لا أرى شيئًا في وثائق Stopwatch يشير إلى مشكلة الالتفاف في آلية المؤقت الأساسية، لذلك لن أتفاجأ على الإطلاق عندما أجد أن Stopwatch يعاني من نفس المشكلة.ولكن مرة أخرى، لن أقضي أي وقت في القلق بشأن ذلك.

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

لطلقة واحدة توقيت، انها حتى أبسط لكتابة

Stopwatch stopWatch = Stopwatch.StartNew();
...dostuff...
Debug.WriteLine(String.Format("It took {0} milliseconds",
                              stopWatch.EllapsedMilliseconds)));

وكنت تخمين ملفوف المرجح فلكيا في TickCount، بل هو اقل مدعاة للقلق لساعة توقيت، بالنظر إلى أن مجال ElapsedTicks هو عملية طويلة. على الجهاز الخاص بي، وساعة التوقيت هو ارتفاع القرار، وفي 2.4e9 القراد في الثانية الواحدة. حتى في هذا المعدل، سوف يستغرق أكثر من 121 عاما لتجاوز هذا المجال القراد. بالطبع، أنا لا أعرف ما الذي يحدث تحت الأغطية، حتى تأخذ أنه مع حبة الملح. ومع ذلك، لاحظت أن وثائق ساعة توقيت لا يذكر حتى قضية ملفوف، في حين أن وثيقة لTickCount لا.

مرخصة بموجب: CC-BY-SA مع الإسناد
لا تنتمي إلى StackOverflow
scroll top