Este posibil ca acest cronometru Java să aibă o rezoluție mai bună de ~15 ms? (Programare, Java, Timer)

Jared Lindsey a intrebat.

Am construit o buclă de temporizare în Java. Simplu. Evit Thread.sleep() pentru că am programarea firelor face imposibilă programarea întârzierilor de înaltă rezoluție, așa că, în schimb, am folosit următoarea buclă foarte ineficientă și am obținut rezultate mai bune:

public static void timerBlockingDelayTest()
{
    long DELAY_TARGET = 5; 
    long t0, t; 

    t0 = System.currentTimeMillis(); 
    while (System.currentTimeMillis() < t0+DELAY_TARGET) {}
    t = System.currentTimeMillis(); 

    long offTargetAmt = Math.abs(t-t0-DELAY_TARGET); 
    System.out.format("Timer loop was off target by %d milliseconds
",
            offTargetAmt);
}

Lucruri de care sunt conștient: sistemul de operare nu este în timp real, programarea firelor este la cheremul sistemului de operare, iar GC poate provoca o întârziere.

Ce nu am luat în considerare?

Pe mașina mea (Windows 7 x64, i5, 2,4 GHz), cea mai bună rezoluție pe care o pot obține este de aproximativ 15 ms. De fapt, dacă fac DELAY_TARGET un multiplu de 15, lucrurile funcționează FOARTE bine. Cu toate acestea, dacă timpul țintă nu se apropie de un multiplu de 15, se offTargetAmt de mai sus va fi în mod regulat ~8 (ms).

Sunt, de asemenea, conștient de această postare: Cronometru de înaltă rezoluție în java

Ce naiba! Este plus sau minus ~8 ms serios cel mai bun lucru pe care îl pot face?!?! Caut doar un răspuns „da, așa este corect” sau „nu, nu ai luat în considerare ___”. Mulțumesc

UPDATE:

Utilizarea System.nanoTime() pare să facă o diferență uriașă. Nu am crezut la început, dar iată codul meu actualizat care compară cele două metode. Vedeți cu ochii voștri.

public static void timerBlockingDelayTest()
{
    long DELAY_TARGET_MS = 5; 
    long NS_PER_MS = 1000000; 
    long DELAY_TARGET_NS = DELAY_TARGET_MS * NS_PER_MS; 
    long t0, t; 

    // Using System.currentTimeMillis() 
    t0 = System.currentTimeMillis(); 
    while (System.currentTimeMillis() < t0+DELAY_TARGET_MS) {}
    t = System.currentTimeMillis(); 
    long msOffTarget = Math.abs(t-t0-DELAY_TARGET_MS); 

    // Using System.nanoTime()
    t0 = System.nanoTime(); 
    while (System.nanoTime() < t0+DELAY_TARGET_NS) {}; 
    t = System.nanoTime(); 
    long nsOffTarget = Math.abs(t-t0-DELAY_TARGET_NS); 

    // Compare the two methods
    System.out.format("System.currentTimeMillis() method: "); 
    System.out.format(" - Off by %d ms (%d ns) 
", 
            msOffTarget, msOffTarget*NS_PER_MS); 
    System.out.format("System.nanoTime() method:          "); 
    System.out.format(" - Off by %d ms (%d ns)
", 
            nsOffTarget/NS_PER_MS, nsOffTarget); 
}

Iată o mostră de ieșire:

debug:
System.currentTimeMillis() method:  - Off by 11 ms (11000000 ns) 
System.nanoTime() method:           - Off by 0 ms (109 ns)
BUILD SUCCESSFUL (total time: 0 seconds)

UPDATE 2 (sper că ultima):

Duh. Măsurarea performanțelor unei funcții de timp cuantificate sau imperfecte de una singură este un pic stupidă. Ceea ce vreau să spun este că, de fapt, măsuram performanța lui currentTimeMillis() de unul singur, ceea ce nu este cel mai inteligent lucru pe care l-am făcut vreodată. După ce mi-am dat seama de acest lucru, am PROFILED ambele metode de mai sus și am constatat că, într-adevăr, nanoTime() oferă o rezoluție mai bună.

Dacă nu aveți un profiler, utilizați nanoTime() pentru a măsura durata buclei currentTimeMillis(), astfel:

public static void timerBlockingDelayTest()
{
    long DELAY_TARGET_MS = 5; 
    long NS_PER_MS = 1000000; 
    long DELAY_TARGET_NS = DELAY_TARGET_MS * NS_PER_MS; 
    long t0ms, t0, t; 

    // Using System.currentTimeMillis() 
    t0 = System.nanoTime(); 
    t0ms = System.currentTimeMillis(); 
    while (System.currentTimeMillis() < t0ms+DELAY_TARGET_MS) {}
    t = System.nanoTime(); 
    long nsOffTarget1 = Math.abs(t-t0-DELAY_TARGET_NS); 

    // Using System.nanoTime()
    t0 = System.nanoTime(); 
    while (System.nanoTime() < t0+DELAY_TARGET_NS) {}; 
    t = System.nanoTime(); 
    long nsOffTarget2 = Math.abs(t-t0-DELAY_TARGET_NS); 

    // Compare the two methods
    System.out.format("System.currentTimeMillis() method: "); 
    System.out.format(" - Off by %d ms (%d ns)
", 
            nsOffTarget1/NS_PER_MS, nsOffTarget1); 
    System.out.format("System.nanoTime() method:          "); 
    System.out.format(" - Off by %d ms (%d ns)
", 
            nsOffTarget2/NS_PER_MS, nsOffTarget2); 
}

Cel puțin în acest fel sunt măsurate ambele întârzieri prin aceeași referință, ceea ce este doar puțin mai inteligent. Cele de mai sus dau o ieșire de genul acesta:

debug:
System.currentTimeMillis() method:  - Off by 4 ms (4040402 ns)
System.nanoTime() method:           - Off by 0 ms (110 ns)
BUILD SUCCESSFUL (total time: 0 seconds)

Concluzie: folosiți nanoTime(), și să aveți o zi bună.

Comentarii

  • System.nanotime vă va oferi o rezoluție mai mare a timpului: stackoverflow.com/questions/351565/… -…  > Por Alex Kleiman.
  • blogs.oracle.com/dholmes/entry/inside_the_hotspot_vm_clocks –  > Por Jared Lindsey.
  • @JaredLindsey Detaliile din acel link sunt următoarele mult mai bune decât în răspunsurile legate (deși încă ar putea exista un răspuns mai bun pe care nu l-am văzut), deoarece aruncă de fapt niște numere așteptate… Aș vota în sus un răspuns care rezumă / evidențiază în mod corespunzător resursa respectivă în context. –  > Por user2864740.
  • Am făcut câteva profilări și este foarte clar că utilizarea nanoTime() este o abordare mult mai bună, pentru toți cei care în viitor vor avea această problemă. Vă mulțumesc tuturor. –  > Por Jared Lindsey.
1 răspunsuri
Craig Gidney

Utilizați System.nanoTime în schimb. Consultați acest răspuns despre diferența dintre nanoTime și currentTimeMillis.

Comentarii

  • Sfinte Sisoe. Nu am crezut… dar acum cred. Wow. Voi posta codul meu editat și voi arăta diferența…  > Por Jared Lindsey.

Tags:,