سؤال

الدقة مقابل.دقة

ما أود معرفته هو ما إذا كان يجب علي استخدامه System.currentTimeMillis() أو System.nanoTime() عند تحديث مواقع كائني في لعبتي؟يتناسب التغير في حركتها بشكل مباشر مع الوقت المنقضي منذ آخر مكالمة وأريد أن أكون دقيقًا قدر الإمكان.

لقد قرأت أن هناك بعض المشكلات الخطيرة المتعلقة بحل الوقت بين أنظمة التشغيل المختلفة (على وجه التحديد أن دقة Mac / Linux تبلغ 1 مللي ثانية تقريبًا بينما تبلغ دقة Windows 50 مللي ثانية ؟؟).أقوم بتشغيل تطبيقاتي بشكل أساسي على نظام التشغيل Windows ويبدو أن دقة 50 مللي ثانية غير دقيقة إلى حد ما.

هل هناك خيارات أفضل من الخيارين اللذين ذكرتهما؟

أي اقتراحات / تعليقات؟

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

المحلول

إذا كنت تبحث فقط عن قياسات دقيقة للغاية لـ الوقت المنقضي, ، يستخدم System.nanoTime(). System.currentTimeMillis() سيعطيك الوقت المنقضي الأكثر دقة بالمللي ثانية منذ العصر، ولكن System.nanoTime() يمنحك وقتًا دقيقًا بالنانو ثانية، بالنسبة لبعض النقاط العشوائية.

من وثائق جافا:

public static long nanoTime()

إرجاع القيمة الحالية لمؤقت النظام المتوفر الأكثر دقة، بالنانو ثانية.

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

على سبيل المثال، لقياس المدة التي يستغرقها تنفيذ بعض التعليمات البرمجية:

long startTime = System.nanoTime();    
// ... the code being measured ...    
long estimatedTime = System.nanoTime() - startTime;

أنظر أيضا: JavaDoc System.nanoTime() و JavaDoc System.currentTimeMillis() لمزيد من المعلومات.

نصائح أخرى

وبما أن أحدا لم يذكر هذا…

ليس من الآمن مقارنة نتائج System.nanoTime() المكالمات بين المواضيع المختلفة.حتى لو حدثت أحداث الخيوط بترتيب يمكن التنبؤ به، فإن الفرق بالنانو ثانية يمكن أن يكون إيجابيًا أو سلبيًا.

System.currentTimeMillis() آمن للاستخدام بين الخيوط.

تحديث بواسطة أركادي:لقد لاحظت السلوك الصحيح لـ System.currentTimeMillis() على نظام التشغيل Windows 7 في Oracle Java 8.تم إرجاع الوقت بدقة 1 مللي ثانية.لم يتغير كود المصدر في OpenJDK، لذلك لا أعرف ما الذي يسبب السلوك الأفضل.


نشر David Holmes من Sun مقالة مدونة منذ عامين تحتوي على نظرة تفصيلية للغاية على واجهات برمجة تطبيقات توقيت Java (على وجه الخصوص System.currentTimeMillis() و System.nanoTime())، عندما تريد استخدام أي منها وكيفية عملها داخليًا.

داخل Hotspot VM:الساعات والمؤقتات وجدولة الأحداث - الجزء الأول - Windows

أحد الجوانب المثيرة للاهتمام للغاية للمؤقت الذي تستخدمه Java على Windows لواجهات برمجة التطبيقات (APIs) التي تحتوي على معلمة انتظار موقوتة هو أن دقة المؤقت يمكن أن تتغير اعتمادًا على ما قد يتم إجراؤه من استدعاءات API الأخرى - على مستوى النظام (وليس فقط في عملية معينة) .ويظهر مثالا حيث استخدام Thread.sleep() سيؤدي إلى تغيير هذا القرار.

System.nanoTime() غير مدعوم في أجهزة JVM الأقدم.إذا كان هذا مصدر قلق، فالتزم به currentTimeMillis

فيما يتعلق بالدقة، أنت على حق تقريبا.في بعض أجهزة Windows، currentTimeMillis() لديه دقة تبلغ حوالي 10 مللي ثانية (وليس 50 مللي ثانية).لست متأكدًا من السبب، لكن بعض الأجهزة التي تعمل بنظام Windows تكون دقيقة تمامًا مثل أجهزة Linux.

لقد استخدمت GAGETimer في الماضي بنجاح معتدل.

كما قال آخرون، CurrentTimeMillis هو وقت الساعة، والذي يتغير بسبب التوقيت الصيفي، وتغيير المستخدمين لإعدادات الوقت، والثواني الكبيسة، ومزامنة وقت الإنترنت.إذا كان تطبيقك يعتمد على زيادة قيم الوقت المنقضي بشكل رتيب، فقد تفضل nanoTime بدلاً من ذلك.

قد تعتقد أن اللاعبين لن يعبثوا بإعدادات الوقت أثناء اللعب، وربما تكون على حق.ولكن لا تقلل من أهمية الاضطراب الناتج عن مزامنة وقت الإنترنت، أو ربما مستخدمي سطح المكتب البعيد.واجهة برمجة تطبيقات nanoTime محصنة ضد هذا النوع من التعطيل.

إذا كنت تريد استخدام وقت الساعة، ولكن مع تجنب الانقطاعات بسبب مزامنة وقت الإنترنت، فقد تفكر في عميل NTP مثل Meinberg، الذي "يضبط" معدل الساعة على الصفر، بدلاً من مجرد إعادة ضبط الساعة بشكل دوري.

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

نعم، إذا كانت هذه الدقة مطلوبة الاستخدام System.nanoTime(), ، ولكن انتبه أنك ستحتاج بعد ذلك إلى Java 5+ JVM.

على أنظمة XP الخاصة بي، أرى أن وقت النظام يتم الإبلاغ عنه على الأقل 100 ميكروثانية 278 نانو ثانية باستخدام الكود التالي:

private void test() {
    System.out.println("currentTimeMillis: "+System.currentTimeMillis());
    System.out.println("nanoTime         : "+System.nanoTime());
    System.out.println();

    testNano(false);                                                            // to sync with currentTimeMillis() timer tick
    for(int xa=0; xa<10; xa++) {
        testNano(true);
        }
    }

private void testNano(boolean shw) {
    long strMS=System.currentTimeMillis();
    long strNS=System.nanoTime();
    long curMS;
    while((curMS=System.currentTimeMillis()) == strMS) {
        if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)); }
        }
    if(shw) { System.out.println("Nano: "+(System.nanoTime()-strNS)+", Milli: "+(curMS-strMS)); }
    }

لقد كانت لدي تجربة جيدة مع وقت النانو.يوفر وقت ساعة الحائط بطول ثانيتين (ثواني منذ العصر ونانو ثانية خلال تلك الثانية)، باستخدام مكتبة JNI.إنه متوفر مع جزء JNI المترجم مسبقًا لكل من Windows وLinux.

للحصول على رسومات اللعبة وتحديثات الموضع السلس، استخدم System.nanoTime() بدلا من System.currentTimeMillis().لقد قمت بالتبديل من currentTimeMillis() إلى nanoTime() في إحدى الألعاب وحصلت على تحسن بصري كبير في سلاسة الحركة.

في حين أن المللي ثانية الواحدة قد تبدو وكأنها يجب أن تكون دقيقة بالفعل، إلا أنها ليست كذلك من الناحية البصرية.العوامل nanoTime() يمكن تحسين ما يلي:

  • تحديد موضع البكسل بدقة أسفل دقة ساعة الحائط
  • القدرة على الصقل بين البكسلات، إذا كنت تريد ذلك
  • عدم دقة ساعة الحائط الخاصة بنظام Windows
  • ارتعاش الساعة (عدم الاتساق عندما تتحرك ساعة الحائط فعليًا للأمام)

كما تشير الإجابات الأخرى، فإن nanoTime له تكلفة أداء إذا تم استدعاؤه بشكل متكرر - سيكون من الأفضل استدعاؤه مرة واحدة فقط لكل إطار، واستخدام نفس القيمة لحساب الإطار بأكمله.

System.currentTimeMillis() ليست آمنة للوقت المنقضي لأن هذه الطريقة حساسة لتغييرات ساعة الوقت الفعلي للنظام.يجب عليك استخدام System.nanoTime.يرجى الرجوع إلى تعليمات نظام جافا:

حول طريقة nanoTime:

..توفر هذه الطريقة دقة نانو ثانية ، ولكن ليس بالضرورة دقة النانو ثانية (أي مدى تغييرات القيمة المتكررة) - لا توجد ضمانات إلا أن الدقة جيدة على الأقل مثل CurrentTimeMillis () ..

إذا كنت تستخدم System.currentTimeMillis() يمكن أن يكون الوقت المنقضي سالبًا (العودة <-- إلى المستقبل)

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

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