2011-09-26 16 views
9

Tengo un extraño problema - Estoy esperando que alguien me puede explicar lo que está sucediendo y una posible solución. Estoy implementando un núcleo Z80 en Java, y estoy intentando ralentizarlo, usando un objeto java.util.Timer en un hilo separado.Java precisión temporal en Windows XP vs Windows 7

La configuración básica es que tengo un hilo ejecuta un bucle ejecutar, 50 veces por segundo. Dentro de este bucle de ejecución, se ejecutan muchos ciclos y luego se invoca wait(). El subproceso externo del temporizador invocará notifyAll() en el objeto Z80 cada 20 ms, simulando una frecuencia de reloj del sistema PAL Sega Master de 3,54 MHz (ish).

El método que he descrito anteriormente funciona perfectamente en Windows 7 (probamos dos máquinas) pero también me han tratado dos equipos con Windows XP y en dos de ellos, el objeto Timer exceso de sueño parece estar en torno al 50% o menos. Esto significa que un segundo del tiempo de emulación realmente demora alrededor de 1,5 segundos más o menos en una máquina con Windows XP.

He intentado usar Thread.sleep() en lugar de un objeto Timer, pero esto tiene exactamente el mismo efecto. Me doy cuenta de que la granularidad del tiempo en la mayoría de los sistemas operativos no es mejor que 1 ms, pero puedo soportar 999 ms o 1001 ms en lugar de 1000 ms. Lo que no puedo aguantar es 1562ms - Simplemente no entiendo por qué mi método funciona bien en la versión más nueva de Windows, pero no en la anterior - He investigado los periodos de interrupción y demás, pero no parezco han desarrollado una solución alternativa.

Podría alguien por favor dígame la causa de este problema y una solución sugerida? Muchas gracias.

Actualización: Aquí está el código completo para una aplicación más pequeña he construido para mostrar el mismo problema:

import java.util.Timer; 
import java.util.TimerTask; 

public class WorkThread extends Thread 
{ 
    private Timer timerThread; 
    private WakeUpTask timerTask; 

    public WorkThread() 
    { 
     timerThread = new Timer(); 
     timerTask = new WakeUpTask(this); 
    } 

    public void run() 
    { 
     timerThread.schedule(timerTask, 0, 20); 
     while (true) 
     { 
     long startTime = System.nanoTime(); 
     for (int i = 0; i < 50; i++) 
     { 
      int a = 1 + 1; 
      goToSleep(); 
     } 
     long timeTaken = (System.nanoTime() - startTime)/1000000; 
     System.out.println("Time taken this loop: " + timeTaken + " milliseconds"); 
     } 
    } 

    synchronized public void goToSleep() 
    { 
     try 
     { 
     wait(); 
     } 
     catch (InterruptedException e) 
     { 
     System.exit(0); 
     } 
    } 

    synchronized public void wakeUp() 
    { 
     notifyAll(); 
    } 

    private class WakeUpTask extends TimerTask 
    { 
     private WorkThread w; 

     public WakeUpTask(WorkThread t) 
     { 
      w = t; 
     } 

     public void run() 
     { 
      w.wakeUp(); 
     } 
    } 
} 

Toda la clase principal que hace es crear e iniciar uno de estos subprocesos de trabajo. En Windows 7, este código produce un tiempo de alrededor de 999ms - 1000ms, que es totalmente correcto. Sin embargo, ejecutar el mismo jar en Windows XP produce un tiempo de alrededor de 1562 ms - 1566 ms, y esto es en dos máquinas separadas de XP que he probado esto. Todos están ejecutando Java 6 actualización 27.

Me parece que este problema está sucediendo porque el temporizador está durmiendo durante 20ms (un valor bastante pequeño) - si bung todos los bucles de ejecución por un solo segundo en esperar esperar() - notifyAll() ciclo, esto produce el resultado correcto - estoy seguro de que las personas que ven lo que estoy tratando de hacer (emulan un Sega Master System a 50 fps) verán que esto no es una solución, sin embargo, no dará un resultado. tiempo de respuesta interactivo, omitiendo 49 de cada 50. Como ya he dicho, Win7 se adapta bien a esto. Lo siento si mi código es demasiado grande :-(

+0

Interesante pregunta. ¿Sería posible para usted proporcionar un fragmento de código autónomo que muestre este comportamiento? – NPE

+0

¿Puedo darle el código fuente del temporizador si lo desea? Te doy la suerte, pero el núcleo del Z80 se perfila como bastante robusto ;-) – PhilPotter1987

+1

@aix * "fragmento de código autónomo" * El código autónomo puede ser corto, pero no puede (por definición) ser un [ snippet] (http://en.wikipedia.org/wiki/Snippet_%28programming%29). Recomiendo publicar un [SSCCE] (http://pscode.org/sscce.html). –

Respuesta

5

Podría alguien por favor dígame la causa de este problema y una solución sugerida?

El problema que está viendo probablemente tiene que ver con clock resolution. Algunos sistemas operativos (Windows XP y anteriores) son notorios por quedarse dormidos y por ser lentos con wait/notify/sleep (interrupciones en general).Mientras tanto, otros sistemas operativos (todos los Linux que he visto) son excelentes para devolver el control casi en el momento especificado.

¿La solución? Para duraciones cortas, use una espera en vivo (bucle ocupado). Para duraciones largas, duerma por menos tiempo de lo que realmente desea y luego espere a vivir el resto.

+1

Al igual que para agregar, al usar bucles, use [System.nanoTime()] (http://download.oracle.com/javase/6/docs/api/java/lang/System.html#nanoTime()) en lugar de que [System.currentTimeMillis()] (http://download.oracle.com/javase/6/docs/api/java/lang/System.html#currentTimeMillis()) ya que currentTimeMillis() tiene la misma granularidad que Thread. dormir(). [Enlace] (http://stackoverflow.com/questions/351565/system-currenttimemillis-vs-system-nanotime) a discusión adicional. – prunge

+0

¿No hay una manera de establecer la resolución del reloj en 1 ms en XP? Tenía la impresión de que lo había, pero obviamente, como no he podido hacerlo, puedo estar equivocado ;-) – PhilPotter1987

+0

Lo siento, no. Es una cosa de OS. Una de esas formas ocultas en que un programa Java puede convertirse involuntariamente en una plataforma específica. –

2

Me renunciar a la TimerTask y sólo tiene que utilizar un bucle ocupado:

long sleepUntil = System.nanoTime() + TimeUnit.MILLISECONDS.toNanos(20); 
while (System.nanoTime() < sleepUntil) { 
    Thread.sleep(2); // catch of InterruptedException left out for brevity 
} 

El retraso de dos milisegundos da el sistema operativo anfitrión un montón de tiempo para trabajar en otras cosas (y es muy probable que estar en un multi-núcleo de todos modos). el código de programa restante es mucho más simple.

Si los codificadas de forma rígida dos milisegundos son demasiado de un instrumento romo, se puede calcular el tiempo de sueño requerida y utilizar la sobrecarga de Thread.sleep(long, int).

+0

Gracias por este Barend - No había considerado esto. Lo intenté y está mucho más cerca de mi rango de tiempo requerido, aunque aún no es suficiente. Todavía tengo un exceso de sueño de aproximadamente 90 ms en promedio, incluso cuando calculo el tiempo de suspensión requerido y el uso de Thread.sleep (long, int). Como dije, no me importa perder uno o dos milisegundos, pero esto es demasiado.Sin embargo, gracias por la sugerencia: es una buena idea y cuando llego a casa del trabajo y pruebo esto en Windows 7, no tengo dudas de que funcionará bien; sin embargo, todavía no explica por qué existe este problema. – PhilPotter1987

1

Usted puede ajustar la resolución del temporizador en Windows XP.

http://msdn.microsoft.com/en-us/library/windows/desktop/dd757624%28v=vs.85%29.aspx

Dado que esta es una configuración de todo el sistema, puede utilizar una herramienta para ajustar la resolución para que pueda comprobar si este es su problema.

probar esto y ver si ayuda: http://www.lucashale.com/timer-resolution/

que te pueden ver mejores tiempos en las versiones más recientes de Windows, ya que, por defecto, la versión más reciente podría tener horarios más estrictos. Además, si está ejecutando una aplicación como Windows Media Player, mejora la resolución del temporizador. Entonces, si escuchas algo de música mientras ejecutas tu emulador, es posible que obtengas grandes tiempos.

+0

Gracias por esto: mi emulador está específicamente diseñado para ser multiplataforma, por lo que no puede confiar en detalles específicos de Windows como este para obtener una buena resolución de tiempo. Al final utilicé un hilo para dormir, con código para volver a un ciclo de espera ocupado si el tiempo se acaba en más de 1 ms en cada 20 ms. Parece que funciona bastante bien en XP ahora. – PhilPotter1987

+0

Phil: parece una buena solución multiplataforma sin tener que escribir un código de caso especial para cada plataforma. Bonito – dss539

Cuestiones relacionadas