Investigación del cálculo óptimo del tiempo de sueño en el bucle del juego

Al programar animaciones y pequeños juegos, he llegado a conocer la increíble importancia deThread.sleep(n); Confío en este método para decirle al sistema operativo cuándo mi aplicación no necesitará ninguna CPU, y usar esto hace que mi programa progrese a una velocidad predecible.

Mi problema es que el JRE utiliza diferentes métodos de implementación de esta funcionalidad en diferentes sistemas operativos. En sistemas operativos basados en UNIX (o influenciados): como Ubuntu y OS X, la implementación JRE subyacente utiliza un sistema preciso y que funciona bien para distribuir el tiempo de CPU a diferentes aplicaciones, y así hacer que mi juego 2D sea fluido y sin retrasos . Sin embargo, en Windows 7 y sistemas Microsoft más antiguos, la distribución del tiempo de CPU parece funcionar de manera diferente, y generalmente recupera su tiempo de CPU después de la cantidad de sueño dada, variando con aproximadamente 1-2 ms desde el sueño objetivo. Sin embargo, obtienes ráfagas ocasionales de 10-20 ms adicionales de tiempo de sueño. Esto hace que mi juego se retrase una vez cada pocos segundos cuando esto sucede. He notado que este problema existe en la mayoría de los juegos Java que he probado en Windows, Minecraft es un ejemplo notable.

Ahora, he estado buscando en Internet para encontrar una solución a este problema. He visto a muchas personas usar soloThread.yield(); en lugar deThread.sleep(n);, que funciona a la perfección a costa de que el núcleo de la CPU actualmente en uso obtenga carga completa, sin importar la cantidad de CPU que tu juego realmente necesite. Esto no es ideal para jugar en computadoras portátiles o estaciones de trabajo de alto consumo de energía, y es una compensación innecesaria en sistemas Mac y Linux.

irando un poco más, encontré un método de uso común para corregir las inconsistencias del tiempo de sueño llamado "spin-sleep", donde solo ordena dormir por 1 ms a la vez y verifica la consistencia usando elSystem.nanoTime(); método, que es muy preciso incluso en sistemas Microsoft. Esto ayuda para la inconsistencia normal de 1-2 ms del sueño, pero no ayudará contra las explosiones ocasionales de + 10-20 ms de inconsistencia del sueño, ya que esto a menudo resulta en más tiempo de lo que un ciclo de mi ciclo debería tomar todo juntos

Después de toneladas de búsqueda, encontré este artículo críptico de Andy Malakov, que fue muy útil para mejorar mi ciclo:http: //andy-malakov.blogspot.com/2010/06/alternative-to-threadsleep.htm

e acuerdo con su artículo, escribí este método de sueño:

// Variables for calculating optimal sleep time. In nanoseconds (1s = 10^-9ms).
private long timeBefore = 0L;
private long timeSleepEnd, timeLeft;

// The estimated game update rate.
private double timeUpdateRate;

// The time one game loop cycle should take in order to reach the max FPS.
private long timeLoop;

private void sleep() throws InterruptedException {

    // Skip first game loop cycle.
    if (timeBefore != 0L) {

        // Calculate optimal game loop sleep time.
        timeLeft = timeLoop - (System.nanoTime() - timeBefore);

        // If all necessary calculations took LESS time than given by the sleepTimeBuffer. Max update rate was reached.
        if (timeLeft > 0 && isUpdateRateLimited) {

            // Determine when to stop sleeping.
            timeSleepEnd = System.nanoTime() + timeLeft;

            // Sleep, yield or keep the thread busy until there is not time left to sleep.
            do {
                if (timeLeft > SLEEP_PRECISION) {
                    Thread.sleep(1); // Sleep for approximately 1 millisecond.
                }
                else if (timeLeft > SPIN_YIELD_PRECISION) {
                    Thread.yield(); // Yield the thread.
                }
                if (Thread.interrupted()) {
                    throw new InterruptedException();
            }
                timeLeft = timeSleepEnd - System.nanoTime();
            }
            while (timeLeft > 0);
        }
        // Save the calculated update rate.
        timeUpdateRate =  1000000000D / (double) (System.nanoTime() - timeBefore);
    }
    // Starting point for time measurement.
    timeBefore = System.nanoTime();
}

SLEEP_PRECISION Normalmente pongo alrededor de 2 ms, ySPIN_YIELD_PRECISION a aproximadamente 10 000 ns para obtener el mejor rendimiento en mi máquina con Windows 7.

Después de toneladas de arduo trabajo, esto es lo mejor que se me ocurre. Entonces, dado que todavía me importa mejorar la precisión de este método de suspensión, y todavía no estoy satisfecho con el rendimiento, me gustaría hacer un llamamiento a todos ustedes, hackers y animadores de juegos Java, para que sugieran una mejor solución para el Plataforma de Windows. ¿Podría usar una forma específica de plataforma en Windows para mejorarla? No me importa tener un pequeño código específico de plataforma en mis aplicaciones, siempre que la mayoría del código sea independiente del sistema operativo.

También me gustaría saber si hay alguien que sepa acerca de Microsoft y Oracle trabajando en una mejor implementación deThread.sleep(n);, ¿o cuáles son los planes futuros de Oracle para mejorar su entorno como base de aplicaciones que requieren una alta precisión de sincronización, como software de música y juegos?

Gracias a todos por leer mi larga pregunta / artículo. ¡Espero que algunas personas encuentren útil mi investigación!

Respuestas a la pregunta(5)

Su respuesta a la pregunta