首页
学习
活动
专区
工具
TVP
发布
社区首页 >问答首页 >游戏循环中最佳睡眠时间计算的研究

游戏循环中最佳睡眠时间计算的研究
EN

Stack Overflow用户
提问于 2011-03-11 23:11:21
回答 3查看 5.9K关注 0票数 19

当我编写动画和小游戏时,我已经知道了Thread.sleep(n);的难以置信的重要性,我依靠这种方法来告诉操作系统,当我的应用程序不需要任何CPU时,并使用这种方法使我的程序以可预测的速度进行。

我的问题是,JRE在不同的操作系统上使用不同的方法来实现此功能。在基于UNIX的(或受其影响的) OS上:es,如Ubuntu和OS,底层的JRE实现使用一个功能良好且精确的系统来将CPU时间分配给不同的应用程序,从而使我的2D游戏流畅且无延迟。但是,在Windows 7和更早的Microsoft系统上,CPU时间分布的工作方式似乎有所不同,您通常会在给定的睡眠时间后找回CPU时间,距离目标睡眠时间大约1-2毫秒。然而,你偶尔会得到额外的10-20毫秒的睡眠时间。当这种情况发生时,这会导致我的游戏每隔几秒钟就会延迟一次。我注意到这个问题存在于我在Windows上尝试过的大多数Java游戏中,“我的世界”就是一个明显的例子。

现在,我一直在互联网上寻找解决这个问题的方法。我见过很多人只使用Thread.yield();而不使用Thread.sleep(n);,无论你的游戏实际需要多少CPU,它都会以当前使用的CPU核心得到满载为代价来完美地工作。这并不适合在笔记本电脑或高能耗工作站上玩游戏,而且在Mac和Linux系统上也是不必要的权衡。

再往下看,我发现了一种常用的纠正睡眠时间不一致的方法,称为“自旋睡眠”,在这种方法中,你一次只能安排1ms的睡眠,并使用System.nanoTime();方法检查一致性,即使在微软的系统上,这种方法也非常准确。这对正常的1-2ms的睡眠不一致有帮助,但对偶尔的+10-20ms的睡眠不一致没有帮助,因为这通常会导致花费的时间超过我的循环的一个周期。

经过大量的寻找,我找到了Andy Malakov的这篇晦涩的文章,这篇文章对改进我的循环非常有帮助:http://andy-malakov.blogspot.com/2010/06/alternative-to-threadsleep.html

基于他的文章,我写了这个睡眠方法:

代码语言:javascript
复制
// 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();
}

为了在我的Windows7机器上获得最佳性能,我通常将SLEEP_PRECISION设置为大约2ms,将SPIN_YIELD_PRECISION设置为大约10000 ns。

经过大量的艰苦工作,这是我能想到的最好的办法了。所以,由于我仍然关心提高这种睡眠方法的准确性,而且我仍然对性能不满意,所以我想呼吁所有java游戏黑客和动画师为Windows平台提供更好的解决方案的建议。我可以在Windows上使用特定于平台的方式来使其更好吗?我不关心在我的应用程序中有一些特定于平台的代码,只要大多数代码是独立于操作系统的。

我还想知道是否有人知道微软和甲骨文正在更好地实现Thread.sleep(n);方法,或者甲骨文未来计划如何改善他们的环境,作为需要高计时精度的应用程序的基础,例如音乐软件和游戏?

感谢大家阅读我冗长的问题/文章。我希望一些人会发现我的研究是有帮助的!

EN

回答 3

Stack Overflow用户

发布于 2011-03-12 00:11:15

在windows上计时是出了名的糟糕。This article是一个很好的起点。我不确定你是否关心,但也要注意,在虚拟系统上(当windows是客户操作系统时)也可能会有更严重的问题(特别是System.nanoTime)。

票数 2
EN

Stack Overflow用户

发布于 2011-03-11 23:16:25

Thread.Sleep说你的应用程序不需要更多时间。这意味着在最坏的情况下,您将不得不等待整个线程切片(40ms左右)。

现在,在最坏的情况下,当一个驱动程序或某件事情占用更多的时间时,你可能需要等待120ms (3*40ms),所以Thread.Sleep不是最好的方法。采取另一种方式,比如注册一个1ms的回调,并开始绘制代码非常X的回调。

(这是在windows上,我会使用MultiMedia工具来获取1ms分辨率回调)

票数 1
EN

Stack Overflow用户

发布于 2012-01-23 06:35:45

Thread.sleep不准确,并且会使动画在大部分时间内抖动。

如果你完全用Thread.yield代替它,你将得到一个稳定的FPS,没有延迟或抖动,但是CPU使用率大大增加。我很久以前就搬到了Thread.yield。

这个问题已经在Java游戏开发论坛上讨论了很多年了。

票数 0
EN
页面原文内容由Stack Overflow提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://stackoverflow.com/questions/5274619

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档